fix: 引入Settings的Module

This commit is contained in:
2024-12-10 14:57:24 +08:00
parent ad8fc8731d
commit df105485bd
6934 changed files with 896168 additions and 2 deletions

View File

@@ -0,0 +1,55 @@
package {
default_team: "trendy_team_android_settings_app",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "packages_apps_Settings_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["packages_apps_Settings_license"],
}
android_test {
name: "SettingsUnitTests",
certificate: "platform",
libs: [
"telephony-common",
"ims-common",
],
static_libs: [
"aconfig_settings_flags_lib",
"androidx.arch.core_core-testing",
"androidx.test.core",
"androidx.test.espresso.core",
"androidx.test.rules",
"androidx.test.ext.junit",
"androidx.preference_preference",
"flag-junit",
"mockito-target-minus-junit4",
"platform-test-annotations",
"platform-test-rules",
"truth",
"kotlinx_coroutines_test",
"flag-junit",
"Settings-testutils2",
"MediaDrmSettingsFlagsLib",
// Don't add SettingsLib libraries here - you can use them directly as they are in the
// instrumented Settings app.
],
errorprone: {
javacflags: ["-Xep:CheckReturnValue:WARN"],
},
// Include all test java/kotlin files.
srcs: [
"src/**/*.java",
"src/**/*.kt",
],
use_resource_processor: true,
platform_apis: true,
test_suites: ["device-tests"],
instrumentation_for: "Settings",
}

View File

@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.android.settings.tests.unit">
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
<activity android:name="com.android.settings.tests.BluetoothRequestPermissionTest"
android:exported="true"
android:label="Bluetooth Perm Test" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="Operator" android:label="Operator Hook Test"
android:exported="true">
<intent-filter>
<action android:name="com.android.settings.OPERATOR_APPLICATION_SETTING" />
</intent-filter>
<meta-data android:name="com.android.settings.title" android:resource="@string/operator_settings_title" />
<meta-data android:name="com.android.settings.summary" android:resource="@string/operator_settings_summary" />
<meta-data android:name="com.android.settings.icon" android:resource="@drawable/ic_settings_applications" />
</activity>
<activity android:name="Manufacturer" android:label="Manufacturer Hook Test"
android:exported="true">
<intent-filter>
<action android:name="com.android.settings.MANUFACTURER_APPLICATION_SETTING" />
</intent-filter>
<meta-data android:name="com.android.settings.title" android:resource="@string/manufacturer_settings_title" />
<meta-data android:name="com.android.settings.summary" android:resource="@string/manufacturer_settings_summary" />
<meta-data android:name="com.android.settings.icon" android:resource="@drawable/ic_settings_applications" />
</activity>
<service android:name="com.android.settings.accounts.TestAuthService"
android:exported="true">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
</service>
<!-- Disable startup provider due to resource loading issue. -->
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove"/>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.settings"
android:label="Settings Test Cases">
</instrumentation>
</manifest>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<configuration description="Runs Settings Test Cases.">
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-instrumentation" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="SettingsUnitTests.apk" />
<option name="aapt-version" value="AAPT2" />
</target_preparer>
<option name="test-tag" value="SettingsUnitTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.settings.tests.unit" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
</configuration>

View File

@@ -0,0 +1,2 @@
# We do not guard tests - everyone is welcomed to contribute to tests.
per-file *.java=*

View File

@@ -0,0 +1,29 @@
The simplest way to run all SettingsUnitTests is with atest
$ atest SettingsUnitTests
A single class can also be tested with the following command
$ atest -c <YourClassName>
// The following instructions show how to run the test suite using make + adb //
To build the tests you can use the following command at the root of your android source tree
$ make -j SettingsUnitTests
The test apk then needs to be installed onto your test device. The apk's location will vary
depending on your device model and architecture. At the end of the make command's output, there
should be a line similar to the following:
"Copy: ${ANDROID_PRODUCT_OUT}/testcases/SettingsUnitTests/arm64/SettingsUnitTests.apk"
Install via the following command:
$ adb install -r ${ANDROID_PRODUCT_OUT}/testcases/SettingsUnitTests/arm64/SettingsUnitTests.apk
To run all tests:
$ adb shell am instrument -w com.android.settings.tests.unit/androidx.test.runner.AndroidJUnitRunner
To run all tests in a specific class:
$ adb shell am instrument -w -e class com.android.settings.<class> com.android.settings.tests.unit/androidx.test.runner.AndroidJUnitRunner
To run a specific test:
$ adb shell am instrument -w -e class com.android.settings.<class>#<test> com.android.settings.tests.unit/androidx.test.runner.AndroidJUnitRunner
More general information can be found at
http://developer.android.com/reference/android/support/test/runner/AndroidJUnitRunner.html

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,20 @@
<!--
~ Copyright (C) 2024 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#ff0000"/>
<size android:width="1dp" android:height="1dp" />
</shape>

View File

@@ -0,0 +1,20 @@
<!--
~ Copyright (C) 2024 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#ff0000"/>
<size android:width="128dp" android:height="128dp" />
</shape>

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView android:id="@+id/msg_container"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:transcriptMode="normal"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@android:style/ButtonBar">
<Button android:id="@+id/enable"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/enable" />
<Button android:id="@+id/discoverable"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/discoverable" />
<Button android:id="@+id/scan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/start_scan" />
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content" android:text="@string/manufacturer_hello" />
</LinearLayout>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content" android:text="@string/operator_hello" />
</LinearLayout>

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<!-- Test only. Do not translate. -->
<!-- Test only. Do not translate. -->
<!-- Test only. Do not translate. -->
<string name="enable" translatable="false">Enable</string>
<string name="discoverable" translatable="false">Discoverable</string>
<string name="start_scan" translatable="false">Start scan</string>
<string name="stop_scan" translatable="false">Stop scan</string>
<string name="operator_hello" translatable="false">Hello Operator!</string>
<string name="operator_settings_title" translatable="false">Operator</string>
<string name="operator_settings_summary" translatable="false">Operator hook that can be used to start activity of choice</string>
<string name="manufacturer_hello" translatable="false">Hello Manufacturer!</string>
<string name="manufacturer_settings_title" translatable="false">Manufacturer</string>
<string name="manufacturer_settings_summary" translatable="false">Manufacturer hook that can be used to start activity of choice</string>
<string name="account_auth_label" translatable="false">Settings Test Account</string>
<string name="account_type" translatable="false">com.settingstest.account-prefs</string>
<string name="account_pref_title" translatable="false">Test preference for external account</string>
</resources>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreference
android:key="account_test_switch"
android:title="@string/account_pref_title" />
</PreferenceScreen>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<account-authenticator
xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/account_auth_label"
android:icon="@drawable/ic_settings_applications"
android:accountType="@string/account_type"
android:accountPreferences="@xml/account_preferences" />

View File

@@ -0,0 +1,155 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.ContentInterface;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.UserProperties;
import android.net.Uri;
import android.os.UserHandle;
import android.os.UserManager;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
/** Unittest for DefaultRingtonePreference. */
@RunWith(AndroidJUnit4.class)
public class DefaultRingtonePreferenceTest {
private static final int OWNER_USER_ID = 1;
private static final int OTHER_USER_ID = 10;
private static final int INVALID_RINGTONE_TYPE = 0;
private DefaultRingtonePreference mDefaultRingtonePreference;
@Mock
private ContentResolver mContentResolver;
@Mock
private UserManager mUserManager;
private Uri mRingtoneUri;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
Context context = spy(ApplicationProvider.getApplicationContext());
mContentResolver = ContentResolver.wrap(Mockito.mock(ContentInterface.class));
when(context.getContentResolver()).thenReturn(mContentResolver);
mDefaultRingtonePreference = spy(new DefaultRingtonePreference(context, null /* attrs */));
doReturn(context).when(mDefaultRingtonePreference).getContext();
// Use INVALID_RINGTONE_TYPE to return early in RingtoneManager.setActualDefaultRingtoneUri
when(mDefaultRingtonePreference.getRingtoneType())
.thenReturn(INVALID_RINGTONE_TYPE);
mDefaultRingtonePreference.setUserId(OWNER_USER_ID);
mDefaultRingtonePreference.mUserContext = context;
when(mDefaultRingtonePreference.isDefaultRingtone(any(Uri.class))).thenReturn(false);
when(context.getSystemServiceName(UserManager.class)).thenReturn(Context.USER_SERVICE);
when(context.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
UserProperties userProperties = new UserProperties.Builder().setMediaSharedWithParent(false)
.build();
when(mUserManager.getUserProperties(UserHandle.of(OTHER_USER_ID))).thenReturn(
userProperties);
mRingtoneUri = Uri.parse("content://none");
}
@Test
public void onSaveRingtone_nullMimeType_shouldNotSetRingtone() {
when(mContentResolver.getType(mRingtoneUri)).thenReturn(null);
mDefaultRingtonePreference.onSaveRingtone(mRingtoneUri);
verify(mDefaultRingtonePreference, never()).setActualDefaultRingtoneUri(mRingtoneUri);
}
@Test
public void onSaveRingtone_notAudioMimeType_shouldNotSetRingtone() {
when(mContentResolver.getType(mRingtoneUri)).thenReturn("text/plain");
mDefaultRingtonePreference.onSaveRingtone(mRingtoneUri);
verify(mDefaultRingtonePreference, never()).setActualDefaultRingtoneUri(mRingtoneUri);
}
@Test
public void onSaveRingtone_notManagedProfile_shouldNotSetRingtone() {
mRingtoneUri = Uri.parse("content://" + OTHER_USER_ID + "@ringtone");
when(mContentResolver.getType(mRingtoneUri)).thenReturn("audio/*");
when(mUserManager.isSameProfileGroup(OWNER_USER_ID, OTHER_USER_ID)).thenReturn(true);
when(mUserManager.getProfileParent(UserHandle.of(OTHER_USER_ID))).thenReturn(
UserHandle.of(OWNER_USER_ID));
when(mUserManager.isManagedProfile(OTHER_USER_ID)).thenReturn(false);
mDefaultRingtonePreference.onSaveRingtone(mRingtoneUri);
verify(mDefaultRingtonePreference, never()).setActualDefaultRingtoneUri(mRingtoneUri);
}
@Test
public void onSaveRingtone_notSameUser_shouldNotSetRingtone() {
mRingtoneUri = Uri.parse("content://" + OTHER_USER_ID + "@ringtone");
when(mContentResolver.getType(mRingtoneUri)).thenReturn("audio/*");
when(mUserManager.isSameProfileGroup(OWNER_USER_ID, OTHER_USER_ID)).thenReturn(false);
mDefaultRingtonePreference.onSaveRingtone(mRingtoneUri);
verify(mDefaultRingtonePreference, never()).setActualDefaultRingtoneUri(mRingtoneUri);
}
@Test
public void onSaveRingtone_isManagedProfile_shouldSetRingtone() {
mRingtoneUri = Uri.parse("content://" + OTHER_USER_ID + "@ringtone");
when(mContentResolver.getType(mRingtoneUri)).thenReturn("audio/*");
when(mUserManager.isSameProfileGroup(OWNER_USER_ID, OTHER_USER_ID)).thenReturn(true);
when(mUserManager.getProfileParent(UserHandle.of(OTHER_USER_ID))).thenReturn(
UserHandle.of(OWNER_USER_ID));
when(mUserManager.isManagedProfile(OTHER_USER_ID)).thenReturn(true);
mDefaultRingtonePreference.onSaveRingtone(mRingtoneUri);
verify(mDefaultRingtonePreference).setActualDefaultRingtoneUri(mRingtoneUri);
}
@Test
public void onSaveRingtone_defaultUri_shouldSetRingtone() {
mRingtoneUri = Uri.parse("default_ringtone");
when(mDefaultRingtonePreference.isDefaultRingtone(any(Uri.class))).thenReturn(true);
mDefaultRingtonePreference.onSaveRingtone(mRingtoneUri);
verify(mDefaultRingtonePreference).setActualDefaultRingtoneUri(mRingtoneUri);
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings;
import static com.google.common.truth.Truth.assertThat;
import android.text.Spannable;
import android.text.style.ClickableSpan;
import android.widget.TextView;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class LinkifyUtilsTest {
private static final String TEST_STRING = "to LINK_BEGINscanning settingsLINK_END.";
private static final String WRONG_STRING = "to scanning settingsLINK_END.";
private final LinkifyUtils.OnClickListener mClickListener = () -> { /* Do nothing */ };
private StringBuilder mSpanStringBuilder;
private StringBuilder mWrongSpanStringBuilder;
TextView mTextView;
@Before
public void setUp() throws Exception {
mSpanStringBuilder = new StringBuilder(TEST_STRING);
mWrongSpanStringBuilder = new StringBuilder(WRONG_STRING);
mTextView = new TextView(ApplicationProvider.getApplicationContext());
}
@Test
public void linkify_whenSpanStringCorrect_shouldReturnTrue() {
final boolean linkifyResult = LinkifyUtils.linkify(mTextView, mSpanStringBuilder,
mClickListener);
assertThat(linkifyResult).isTrue();
}
@Test
public void linkify_whenSpanStringWrong_shouldReturnFalse() {
final boolean linkifyResult = LinkifyUtils.linkify(mTextView, mWrongSpanStringBuilder,
mClickListener);
assertThat(linkifyResult).isFalse();
}
@Test
public void linkify_whenSpanStringCorrect_shouldContainClickableSpan() {
LinkifyUtils.linkify(mTextView, mSpanStringBuilder, mClickListener);
final Spannable spannableContent = (Spannable) mTextView.getText();
final int len = spannableContent.length();
final Object[] spans = spannableContent.getSpans(0, len, Object.class);
assertThat(spans[1] instanceof ClickableSpan).isTrue();
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings
import android.platform.test.flag.junit.SetFlagsRule
import androidx.test.core.app.ActivityScenario
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.Settings.FactoryResetActivity
import com.android.settings.flags.Flags
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
/** Test [MainClear]. */
@RunWith(AndroidJUnit4::class)
class MainClearTest {
@get:Rule
val mSetFlagsRule = SetFlagsRule()
@Test
fun factoryResetCancelButton_flagDisabled_noCancelButton() {
mSetFlagsRule.disableFlags(Flags.FLAG_SHOW_FACTORY_RESET_CANCEL_BUTTON)
ActivityScenario.launch(FactoryResetActivity::class.java).use {
ensurePrimaryButton()
onView(withText(android.R.string.cancel)).check(doesNotExist())
it.onActivity { activity -> assertThat(activity.isFinishing).isFalse() }
}
}
@Test
fun factoryResetCancelButton_flagEnabled_showCancelButton() {
mSetFlagsRule.enableFlags(Flags.FLAG_SHOW_FACTORY_RESET_CANCEL_BUTTON)
ActivityScenario.launch(FactoryResetActivity::class.java).use {
ensurePrimaryButton()
it.onActivity { activity -> assertThat(activity.isFinishing).isFalse() }
// Note: onView CANNOT be called within onActivity block, which runs in the main thread
onView(withText(android.R.string.cancel)).check(matches(isDisplayed())).perform(click())
it.onActivity { activity -> assertThat(activity.isFinishing).isTrue() }
}
}
private fun ensurePrimaryButton() {
onView(withText(R.string.main_clear_button_text)).check(matches(isDisplayed()))
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.os.Bundle;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class ResetSubscriptionContractTest {
private static final int SUB_ID_1 = 3;
private static final int SUB_ID_2 = 8;
@Mock
private SubscriptionManager mSubscriptionManager;
@Mock
private OnSubscriptionsChangedListener mOnSubscriptionsChangedListener;
@Mock
private SubscriptionInfo mSubscriptionInfo1;
@Mock
private SubscriptionInfo mSubscriptionInfo2;
private Context mContext;
private ResetNetworkRequest mRequestArgs;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
mRequestArgs = new ResetNetworkRequest(new Bundle());
}
private ResetSubscriptionContract createTestObject() {
return new ResetSubscriptionContract(mContext, mRequestArgs) {
@Override
protected SubscriptionManager getSubscriptionManager() {
return mSubscriptionManager;
}
@Override
protected OnSubscriptionsChangedListener getChangeListener() {
return mOnSubscriptionsChangedListener;
}
};
}
@Test
public void getAnyMissingSubscriptionId_returnNull_whenNoSubscriptionChange() {
mRequestArgs.setResetTelephonyAndNetworkPolicyManager(SUB_ID_1);
doReturn(mSubscriptionInfo1).when(mSubscriptionManager)
.getActiveSubscriptionInfo(SUB_ID_1);
mRequestArgs.setResetApn(SUB_ID_2);
doReturn(mSubscriptionInfo2).when(mSubscriptionManager)
.getActiveSubscriptionInfo(SUB_ID_2);
ResetSubscriptionContract target = createTestObject();
verify(mSubscriptionManager).addOnSubscriptionsChangedListener(any(), any());
assertNull(target.getAnyMissingSubscriptionId());
}
@Test
public void getAnyMissingSubscriptionId_returnSubId_whenSubscriptionNotActive() {
mRequestArgs.setResetTelephonyAndNetworkPolicyManager(SUB_ID_1);
doReturn(mSubscriptionInfo1).when(mSubscriptionManager)
.getActiveSubscriptionInfo(SUB_ID_1);
mRequestArgs.setResetApn(SUB_ID_2);
doReturn(null).when(mSubscriptionManager)
.getActiveSubscriptionInfo(SUB_ID_2);
ResetSubscriptionContract target = createTestObject();
verify(mSubscriptionManager).addOnSubscriptionsChangedListener(any(), any());
assertEquals(target.getAnyMissingSubscriptionId(), new Integer(SUB_ID_2));
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings;
import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_KEY;
import static com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag.FLAG_INCLUDE_PREF_SCREEN;
import static com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag.FLAG_NEED_KEY;
import android.annotation.XmlRes;
import android.content.Context;
import android.os.Bundle;
import android.provider.SearchIndexableResource;
import com.android.settings.core.PreferenceXmlParserUtils;
import com.android.settings.search.BaseSearchIndexProvider;
import java.util.ArrayList;
import java.util.List;
/**
* Convenience methods and constants for testing.
*/
public class TestUtils {
public static final long KILOBYTE = 1024L; // TODO: Change to 1000 in O Robolectric.
public static final long MEGABYTE = KILOBYTE * KILOBYTE;
public static final long GIGABYTE = KILOBYTE * MEGABYTE;
public static List<String> getAllXmlKeys(
Context context, BaseSearchIndexProvider indexProvider)
throws Exception {
final List<SearchIndexableResource> resources = indexProvider.getXmlResourcesToIndex(
context, true /* not used*/);
if (resources == null || resources.isEmpty()) {
return new ArrayList<>();
}
final List<String> keys = new ArrayList<>();
for (SearchIndexableResource res : resources) {
keys.addAll(getKeysFromXml(res.xmlResId, context));
}
return keys;
}
private static List<String> getKeysFromXml(@XmlRes int xmlResId, Context context)
throws Exception {
final List<String> keys = new ArrayList<>();
final List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(context, xmlResId,
FLAG_NEED_KEY | FLAG_INCLUDE_PREF_SCREEN);
for (Bundle bundle : metadata) {
keys.add(bundle.getString(METADATA_KEY));
}
return keys;
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.UserManager;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class TestingSettingsTest {
@Mock
private UserManager mUserManager;
@Mock
private TestingSettings mTestingSettings;
private Context mContext;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
}
@Test
public void isRadioInfoVisible_returnFalse_whenUserRestricted() {
mockService(mContext, Context.USER_SERVICE, UserManager.class, mUserManager);
doReturn(true).when(mUserManager).isAdminUser();
doReturn(false).when(mUserManager).isGuestUser();
doReturn(true).when(mUserManager)
.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
assertThat(mTestingSettings.isRadioInfoVisible(mContext)).isFalse();
}
private <T> void mockService(Context context, String serviceName,
Class<T> serviceClass, T service) {
when(context.getSystemServiceName(serviceClass)).thenReturn(serviceName);
when(context.getSystemService(serviceName)).thenReturn(service);
}
}

View File

@@ -0,0 +1,195 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.Looper;
import android.os.Process;
import android.view.View;
import android.widget.TextView;
import androidx.test.annotation.UiThreadTest;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.testutils.ResourcesUtils;
import com.android.settings.wifi.helper.SavedWifiHelper;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@RunWith(AndroidJUnit4.class)
public class UserCredentialsSettingsTest {
static final String TEST_ALIAS = "test_alias";
static final String TEST_USER_BY_NAME = "test_used_by_name";
static final String TEXT_PURPOSE_SYSTEM = "credential_for_vpn_and_apps";
static final String TEXT_PURPOSE_WIFI = "credential_for_wifi";
static final String TEXT_PURPOSE_WIFI_IN_USE = "credential_for_wifi_in_use";
@Rule
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Spy
final Context mContext = ApplicationProvider.getApplicationContext();
@Mock
SavedWifiHelper mSavedWifiHelper;
@Mock
View mView;
UserCredentialsSettings mSettings;
UserCredentialsSettings.Credential mSysCredential =
new UserCredentialsSettings.Credential(TEST_ALIAS, Process.SYSTEM_UID);
UserCredentialsSettings.Credential mWifiCredential =
new UserCredentialsSettings.Credential(TEST_ALIAS, Process.WIFI_UID);
List<String> mUsedByNames = Arrays.asList(TEST_USER_BY_NAME);
TextView mPurposeView = new TextView(ApplicationProvider.getApplicationContext());
TextView mUsedByTitleView = new TextView(ApplicationProvider.getApplicationContext());
TextView mUsedByContentView = new TextView(ApplicationProvider.getApplicationContext());
@Before
@UiThreadTest
public void setUp() {
when(mSavedWifiHelper.isCertificateInUse(any(String.class))).thenReturn(false);
when(mSavedWifiHelper.getCertificateNetworkNames(any(String.class)))
.thenReturn(new ArrayList<>());
when(mView.getTag()).thenReturn(mWifiCredential);
if (Looper.myLooper() == null) {
Looper.prepare();
}
mSettings = spy(new UserCredentialsSettings());
when(mSettings.getContext()).thenReturn(mContext);
mSettings.mSavedWifiHelper = mSavedWifiHelper;
doNothing().when(mSettings)
.showCredentialDialogFragment(any(UserCredentialsSettings.Credential.class));
}
@Test
@UiThreadTest
public void onClick_noCredentialInTag_doNothing() {
when(mView.getTag()).thenReturn(null);
mSettings.onClick(mView);
verify(mSavedWifiHelper, never()).getCertificateNetworkNames(any(String.class));
verify(mSettings, never())
.showCredentialDialogFragment(any(UserCredentialsSettings.Credential.class));
}
@Test
@UiThreadTest
public void onClick_credentialInNotUse_notSetUsedByNamesThenShowDialog() {
mWifiCredential.setInUse(false);
when(mView.getTag()).thenReturn(mWifiCredential);
mSettings.onClick(mView);
verify(mSavedWifiHelper, never()).getCertificateNetworkNames(any(String.class));
verify(mSettings)
.showCredentialDialogFragment(any(UserCredentialsSettings.Credential.class));
}
@Test
@UiThreadTest
public void onClick_credentialInUse_setUsedByNamesThenShowDialog() {
mWifiCredential.setInUse(true);
when(mView.getTag()).thenReturn(mWifiCredential);
when(mSavedWifiHelper.getCertificateNetworkNames(any(String.class)))
.thenReturn(mUsedByNames);
mSettings.onClick(mView);
verify(mSavedWifiHelper).getCertificateNetworkNames(any(String.class));
assertThat(mWifiCredential.getUsedByNames()).isEqualTo(mUsedByNames);
verify(mSettings)
.showCredentialDialogFragment(any(UserCredentialsSettings.Credential.class));
}
@Test
@UiThreadTest
public void updatePurposeView_getSystemCert_setTextCorrectly() {
mSettings.updatePurposeView(mPurposeView, mSysCredential);
assertThat(mPurposeView.getText()).isEqualTo(getResString(TEXT_PURPOSE_SYSTEM));
}
@Test
@UiThreadTest
public void updatePurposeView_getWifiCert_setTextCorrectly() {
mWifiCredential.setInUse(false);
mSettings.updatePurposeView(mPurposeView, mWifiCredential);
assertThat(mPurposeView.getText()).isEqualTo(getResString(TEXT_PURPOSE_WIFI));
}
@Test
@UiThreadTest
public void updatePurposeView_isWifiCertInUse_setTextCorrectly() {
mWifiCredential.setInUse(true);
mSettings.updatePurposeView(mPurposeView, mWifiCredential);
assertThat(mPurposeView.getText()).isEqualTo(getResString(TEXT_PURPOSE_WIFI_IN_USE));
}
@Test
@UiThreadTest
public void updateUsedByViews_noUsedByName_hideViews() {
mWifiCredential.setUsedByNames(new ArrayList<>());
mSettings.updateUsedByViews(mUsedByTitleView, mUsedByContentView, mWifiCredential);
assertThat(mUsedByTitleView.getVisibility()).isEqualTo(View.GONE);
assertThat(mUsedByContentView.getVisibility()).isEqualTo(View.GONE);
}
@Test
@UiThreadTest
public void updateUsedByViews_hasUsedByName_showViews() {
mWifiCredential.setUsedByNames(mUsedByNames);
mSettings.updateUsedByViews(mUsedByTitleView, mUsedByContentView, mWifiCredential);
assertThat(mUsedByTitleView.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mUsedByContentView.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mUsedByContentView.getText().toString().contains(TEST_USER_BY_NAME)).isTrue();
}
static String getResString(String name) {
return ResourcesUtils.getResourcesString(ApplicationProvider.getApplicationContext(), name);
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accessibility;
import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.os.UserHandle;
import android.provider.Settings;
import androidx.preference.SwitchPreference;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class AccessibilityShortcutPreferenceControllerTest {
private Context mContext;
private SwitchPreference mPreference;
private AccessibilityShortcutPreferenceController mController;
@Before
public void setUp() {
mContext = ApplicationProvider.getApplicationContext();
mPreference = new SwitchPreference(mContext);
mController = new AccessibilityShortcutPreferenceController(mContext,
"accessibility_shortcut_preference");
}
@Test
public void isChecked_enabledShortcutOnLockScreen_shouldReturnTrue() {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, ON, UserHandle.USER_CURRENT);
mController.updateState(mPreference);
assertThat(mController.isChecked()).isTrue();
assertThat(mPreference.isChecked()).isTrue();
}
@Test
public void isChecked_disabledShortcutOnLockScreen_shouldReturnFalse() {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, OFF,
UserHandle.USER_CURRENT);
mController.updateState(mPreference);
assertThat(mController.isChecked()).isFalse();
assertThat(mPreference.isChecked()).isFalse();
}
@Test
public void setChecked_setTrue_shouldEnableShortcutOnLockScreen() {
mController.setChecked(true);
assertThat(Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, OFF,
UserHandle.USER_CURRENT)).isEqualTo(ON);
}
@Test
public void setChecked_setFalse_shouldDisableShortcutOnLockScreen() {
mController.setChecked(false);
assertThat(Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, ON,
UserHandle.USER_CURRENT)).isEqualTo(OFF);
}
}

View File

@@ -0,0 +1,193 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accessibility;
import static com.android.settings.accessibility.DisableAnimationsPreferenceController.ANIMATION_OFF_VALUE;
import static com.android.settings.accessibility.DisableAnimationsPreferenceController.ANIMATION_ON_VALUE;
import static com.android.settings.accessibility.DisableAnimationsPreferenceController.TOGGLE_ANIMATION_TARGETS;
import static com.google.common.truth.Truth.assertThat;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.provider.Settings;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.core.BasePreferenceController;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
public class DisableAnimationsPreferenceControllerTest {
private static final String TEST_PREFERENCE_KEY = "disable_animation";
private final Context mContext = ApplicationProvider.getApplicationContext();
private Looper mLooper;
private PreferenceScreen mScreen;
private SwitchPreference mPreference;
private DisableAnimationsPreferenceController mController;
@Before
public void setUp() {
if (Looper.myLooper() == null) {
Looper.prepare();
}
mLooper = Looper.myLooper();
PreferenceManager preferenceManager = new PreferenceManager(mContext);
mScreen = preferenceManager.createPreferenceScreen(mContext);
final SwitchPreference preference = new SwitchPreference(mContext);
preference.setKey(TEST_PREFERENCE_KEY);
preference.setPersistent(false);
mScreen.addPreference(preference);
mController = new DisableAnimationsPreferenceController(mContext, TEST_PREFERENCE_KEY);
mController.displayPreference(mScreen);
mPreference = mScreen.findPreference(TEST_PREFERENCE_KEY);
}
@After
public void cleanUp() {
// calling Settings.Global.resetToDefaults doesn't work somehow
// one could check if it works by running the test ones, and see if the settings
// that were changed being restored to default
setAnimationScaleAndWaitForUpdate(ANIMATION_ON_VALUE);
}
@Test
public void getAvailabilityStatus_shouldReturnAvailable() {
assertThat(mController.getAvailabilityStatus()).isEqualTo(
BasePreferenceController.AVAILABLE);
}
@Test
public void isChecked_enabledAnimation_shouldReturnFalse() {
setAnimationScaleAndWaitForUpdate(ANIMATION_ON_VALUE);
mController.updateState(mPreference);
assertThat(mController.isChecked()).isFalse();
assertThat(mPreference.isChecked()).isFalse();
}
@Test
public void isChecked_disabledAnimation_shouldReturnTrue() {
setAnimationScaleAndWaitForUpdate(ANIMATION_OFF_VALUE);
mController.updateState(mPreference);
assertThat(mController.isChecked()).isTrue();
assertThat(mPreference.isChecked()).isTrue();
}
@Test
public void setChecked_disabledAnimation_shouldDisableAnimationTargets() {
mController.setChecked(true);
for (String animationSetting : TOGGLE_ANIMATION_TARGETS) {
final float value = Settings.Global.getFloat(mContext.getContentResolver(),
animationSetting, /* def= */ -1.0f);
assertThat(Float.compare(value, ANIMATION_OFF_VALUE)).isEqualTo(0);
}
}
@Test
public void setChecked_enabledAnimation_shouldEnableAnimationTargets() {
mController.setChecked(false);
for (String animationSetting : TOGGLE_ANIMATION_TARGETS) {
final float value = Settings.Global.getFloat(mContext.getContentResolver(),
animationSetting, /* def= */ -1.0f);
assertThat(Float.compare(value, ANIMATION_ON_VALUE)).isEqualTo(0);
}
}
@Test
public void onStart_enabledAnimation_shouldReturnFalse() {
mController.onStart();
setAnimationScaleAndWaitForUpdate(ANIMATION_ON_VALUE);
assertThat(mController.isChecked()).isFalse();
assertThat(mPreference.isChecked()).isFalse();
}
@Test
public void onStart_disabledAnimation_shouldReturnTrue() {
mController.onStart();
setAnimationScaleAndWaitForUpdate(ANIMATION_OFF_VALUE);
assertThat(mController.isChecked()).isTrue();
assertThat(mPreference.isChecked()).isTrue();
}
@Test
public void onStop_shouldNotUpdateTargets() {
mPreference.setChecked(true);
mController.onStart();
mController.onStop();
setAnimationScaleAndWaitForUpdate(ANIMATION_ON_VALUE);
assertThat(mPreference.isChecked()).isTrue();
}
private void setAnimationScaleAndWaitForUpdate(float newValue) {
ContentResolver resolver = mContext.getContentResolver();
CountDownLatch countDownLatch = new CountDownLatch(TOGGLE_ANIMATION_TARGETS.size());
ContentObserver settingsObserver = new ContentObserver(new Handler(mLooper)) {
@Override
public void onChange(boolean selfChange, @Nullable Uri uri) {
countDownLatch.countDown();
}
};
try {
for (String key : TOGGLE_ANIMATION_TARGETS) {
resolver.registerContentObserver(Settings.Global.getUriFor(key),
false, settingsObserver, UserHandle.USER_ALL);
Settings.Global.putFloat(mContext.getContentResolver(), key,
newValue);
}
countDownLatch.await(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Assert.fail(e.getMessage());
} finally {
resolver.unregisterContentObserver(settingsObserver);
}
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accessibility;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.os.AsyncTask;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
public class DisplaySizeDataTest {
private final Context mContext = ApplicationProvider.getApplicationContext();
private DisplaySizeData mDisplaySizeData;
private int mInitialIndex;
@Before
public void setUp() {
mDisplaySizeData = new DisplaySizeData(mContext);
mInitialIndex = mDisplaySizeData.getInitialIndex();
}
@After
public void cleanUp() throws InterruptedException {
mDisplaySizeData.commit(mInitialIndex);
waitForDisplayChangesSynchronously();
}
@Test
public void commit_success() throws InterruptedException {
final int progress = mDisplaySizeData.getValues().size() - 1;
Assume.assumeTrue("We need more default display size to make the test effective",
mInitialIndex != progress && progress > 0);
mDisplaySizeData.commit(progress);
waitForDisplayChangesSynchronously();
final int density = mContext.getResources().getDisplayMetrics().densityDpi;
assertThat(density).isEqualTo(mDisplaySizeData.getValues().get(progress));
}
/**
* Wait for the display change propagated synchronously.
* <p/>
* Note: Currently, DisplayDensityUtils uses AsyncTask to change the display density
* asynchronously. If in the future we stop using the deprecated AsyncTask, we will need to
* update the wait mechanism in the test.
*/
private void waitForDisplayChangesSynchronously() throws InterruptedException {
// The default AsyncTask.execute run tasks in serial order.
// Posting a new runnable and wait for it to finish means the previous tasks are all done.
CountDownLatch latch = new CountDownLatch(1);
AsyncTask.execute(latch::countDown);
latch.await(5, TimeUnit.SECONDS);
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accessibility;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.provider.Settings;
import androidx.preference.SwitchPreference;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.core.BasePreferenceController;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Tests for {@link FontWeightAdjustmentPreferenceController}.
*/
@RunWith(AndroidJUnit4.class)
public class FontWeightAdjustmentPreferenceControllerTest {
private static final int ON = FontWeightAdjustmentPreferenceController.BOLD_TEXT_ADJUSTMENT;
private static final int OFF = 0;
private Context mContext;
private SwitchPreference mPreference;
private FontWeightAdjustmentPreferenceController mController;
@Before
public void setUp() {
mContext = ApplicationProvider.getApplicationContext();
mPreference = new SwitchPreference(mContext);
mController = new FontWeightAdjustmentPreferenceController(
mContext, "font_weight_adjustment");
}
@After
public void teardown() {
Settings.Secure.resetToDefaults(mContext.getContentResolver(), /* tag= */ null);
}
@Test
public void getAvailabilityStatus_byDefault_shouldReturnAvailable() {
assertThat(mController.getAvailabilityStatus()).isEqualTo(
BasePreferenceController.AVAILABLE);
}
@Test
public void isChecked_enabledBoldText_shouldReturnTrue() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.FONT_WEIGHT_ADJUSTMENT, ON);
mController.updateState(mPreference);
assertThat(mController.isChecked()).isTrue();
assertThat(mPreference.isChecked()).isTrue();
}
@Test
public void isChecked_disabledBoldText_shouldReturnFalse() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.FONT_WEIGHT_ADJUSTMENT, OFF);
mController.updateState(mPreference);
assertThat(mController.isChecked()).isFalse();
assertThat(mPreference.isChecked()).isFalse();
}
@Test
public void setChecked_setTrue_shouldEnableBoldText() {
mController.setChecked(true);
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.FONT_WEIGHT_ADJUSTMENT, OFF)).isEqualTo(ON);
}
@Test
public void setChecked_setFalse_shouldDisableBoldText() {
mController.setChecked(false);
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.FONT_WEIGHT_ADJUSTMENT, OFF)).isEqualTo(OFF);
}
@Test
public void resetState_shouldDisableBoldText() {
mController.setChecked(true);
mController.resetState();
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.FONT_WEIGHT_ADJUSTMENT, OFF)).isEqualTo(OFF);
}
}

View File

@@ -0,0 +1,124 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accessibility;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.os.Looper;
import android.provider.Settings;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.core.BasePreferenceController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Tests for {@link HighTextContrastPreferenceController}.
*/
@RunWith(AndroidJUnit4.class)
public class HighTextContrastPreferenceControllerTest {
private static final String PREF_KEY = "text_contrast";
private static final int ON = 1;
private static final int OFF = 0;
private static final int UNKNOWN = -1;
private Context mContext;
private SwitchPreference mPreference;
private HighTextContrastPreferenceController mController;
private PreferenceScreen mScreen;
@Before
public void setUp() {
mContext = ApplicationProvider.getApplicationContext();
if (Looper.myLooper() == null) {
Looper.prepare();
}
final PreferenceManager preferenceManager = new PreferenceManager(mContext);
mScreen = preferenceManager.createPreferenceScreen(mContext);
mPreference = new SwitchPreference(mContext);
mPreference.setKey(PREF_KEY);
mScreen.addPreference(mPreference);
mController = new HighTextContrastPreferenceController(mContext, PREF_KEY);
}
@Test
public void getAvailabilityStatus_byDefault_shouldReturnAvailable() {
assertThat(mController.getAvailabilityStatus()).isEqualTo(
BasePreferenceController.AVAILABLE);
}
@Test
public void isChecked_enabledTextContrast_shouldReturnTrue() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, ON);
mController.updateState(mPreference);
assertThat(mController.isChecked()).isTrue();
assertThat(mPreference.isChecked()).isTrue();
}
@Test
public void isChecked_disabledTextContrast_shouldReturnFalse() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, OFF);
mController.updateState(mPreference);
assertThat(mController.isChecked()).isFalse();
assertThat(mPreference.isChecked()).isFalse();
}
@Test
public void setChecked_setTrue_shouldEnableTextContrast() {
mController.setChecked(true);
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, UNKNOWN)).isEqualTo(ON);
}
@Test
public void setChecked_setFalse_shouldDisableTextContrast() {
mController.setChecked(false);
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, UNKNOWN)).isEqualTo(OFF);
}
@Test
public void resetState_shouldDisableTextContrast() {
mController.displayPreference(mScreen);
mController.setChecked(true);
mPreference.setChecked(true);
mController.resetState();
assertThat(mPreference.isChecked()).isFalse();
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, UNKNOWN)).isEqualTo(OFF);
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accessibility;
import static com.android.settings.accessibility.LargePointerIconPreferenceController.OFF;
import static com.android.settings.accessibility.LargePointerIconPreferenceController.ON;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.provider.Settings;
import androidx.preference.SwitchPreference;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.core.BasePreferenceController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class LargePointerIconPreferenceControllerTest {
private static final int UNKNOWN = -1;
private Context mContext;
private SwitchPreference mPreference;
private LargePointerIconPreferenceController mController;
@Before
public void setUp() {
mContext = ApplicationProvider.getApplicationContext();
mPreference = new SwitchPreference(mContext);
mController = new LargePointerIconPreferenceController(mContext, "large_pointer");
}
@Test
public void getAvailabilityStatus_shouldReturnAvailable() {
assertThat(mController.getAvailabilityStatus()).isEqualTo(
BasePreferenceController.AVAILABLE);
}
@Test
public void isChecked_enabledLargePointer_shouldReturnTrue() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON, ON);
mController.updateState(mPreference);
assertThat(mController.isChecked()).isTrue();
assertThat(mPreference.isChecked()).isTrue();
}
@Test
public void isChecked_disabledLargePointer_shouldReturnFalse() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON, OFF);
mController.updateState(mPreference);
assertThat(mController.isChecked()).isFalse();
assertThat(mPreference.isChecked()).isFalse();
}
@Test
public void setChecked_enabled_shouldEnableLargePointer() {
mController.setChecked(true);
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON, UNKNOWN)).isEqualTo(ON);
}
@Test
public void setChecked_disabled_shouldDisableLargePointer() {
mController.setChecked(false);
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON, UNKNOWN)).isEqualTo(OFF);
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accessibility;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.core.BasePreferenceController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class MagnificationPreferenceControllerTest {
private Context mContext;
private MagnificationPreferenceController mController;
@Before
public void setUp() {
mContext = ApplicationProvider.getApplicationContext();
mController = new MagnificationPreferenceController(mContext, "magnification");
}
@Test
public void getAvailabilityStatus_shouldReturnAvailable() {
assertThat(mController.getAvailabilityStatus()).isEqualTo(
BasePreferenceController.AVAILABLE);
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accessibility;
import static com.google.common.truth.Truth.assertThat;
import android.content.ComponentName;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
/** Tests for {@link PreferredShortcut} */
@RunWith(AndroidJUnit4.class)
public class PreferredShortcutTest {
private static final String STUB_COMPONENT_NAME = new ComponentName("com.example",
"com.example.testActivity").flattenToString();
private static final int STUB_TYPE = 3;
@Test
public void fromString_matchMemberObject() {
final String preferredShortcutString = STUB_COMPONENT_NAME + ":" + STUB_TYPE;
final PreferredShortcut shortcut = PreferredShortcut.fromString(preferredShortcutString);
assertThat(shortcut.getComponentName()).isEqualTo(STUB_COMPONENT_NAME);
assertThat(shortcut.getType()).isEqualTo(STUB_TYPE);
}
@Test
public void toString_matchString() {
final PreferredShortcut shortcut = new PreferredShortcut(STUB_COMPONENT_NAME, STUB_TYPE);
final String preferredShortcutString = shortcut.toString();
assertThat(preferredShortcutString).isEqualTo(STUB_COMPONENT_NAME + ":" + STUB_TYPE);
}
@Test
public void assertSameObject() {
final String preferredShortcutString = STUB_COMPONENT_NAME + ":" + STUB_TYPE;
final PreferredShortcut targetShortcut = PreferredShortcut.fromString(
preferredShortcutString);
assertThat(targetShortcut).isEqualTo(new PreferredShortcut(STUB_COMPONENT_NAME, STUB_TYPE));
}
}

View File

@@ -0,0 +1,183 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accessibility;
import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
import static com.google.common.truth.Truth.assertThat;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.provider.Settings;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.internal.accessibility.util.ShortcutUtils;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Set;
/** Tests for {@link PreferredShortcuts} */
@RunWith(AndroidJUnit4.class)
public class PreferredShortcutsTest {
private static final String PACKAGE_NAME_1 = "com.test1.example";
private static final String CLASS_NAME_1 = PACKAGE_NAME_1 + ".test1";
private static final ComponentName COMPONENT_NAME_1 = new ComponentName(PACKAGE_NAME_1,
CLASS_NAME_1);
private static final String PACKAGE_NAME_2 = "com.test2.example";
private static final String CLASS_NAME_2 = PACKAGE_NAME_2 + ".test2";
private static final ComponentName COMPONENT_NAME_2 = new ComponentName(PACKAGE_NAME_2,
CLASS_NAME_2);
private static final ContentResolver sContentResolver =
ApplicationProvider.getApplicationContext().getContentResolver();
private final Context mContext = ApplicationProvider.getApplicationContext();
@Before
public void setUp() {
clearShortcuts();
}
@AfterClass
public static void cleanUp() {
clearShortcuts();
}
@Test
public void retrieveUserShortcutType_fromSingleData_matchSavedType() {
final int type = 1;
final PreferredShortcut shortcut = new PreferredShortcut(COMPONENT_NAME_1.flattenToString(),
type);
PreferredShortcuts.saveUserShortcutType(mContext, shortcut);
final int retrieveType = PreferredShortcuts.retrieveUserShortcutType(mContext,
COMPONENT_NAME_1.flattenToString());
assertThat(retrieveType).isEqualTo(type);
}
@Test
public void retrieveUserShortcutType_fromMultiData_matchSavedType() {
final int type1 = 1;
final int type2 = 2;
final PreferredShortcut shortcut1 = new PreferredShortcut(
COMPONENT_NAME_1.flattenToString(), type1);
final PreferredShortcut shortcut2 = new PreferredShortcut(
COMPONENT_NAME_2.flattenToString(), type2);
PreferredShortcuts.saveUserShortcutType(mContext, shortcut1);
PreferredShortcuts.saveUserShortcutType(mContext, shortcut2);
final int retrieveType = PreferredShortcuts.retrieveUserShortcutType(mContext,
COMPONENT_NAME_1.flattenToString());
assertThat(retrieveType).isEqualTo(type1);
}
@Test
public void updatePreferredShortcutsFromSetting_magnificationWithTripleTapAndVolumeKeyShortcuts_preferredShortcutsMatches() {
ShortcutUtils.optInValueToSettings(mContext, ShortcutConstants.UserShortcutType.HARDWARE,
MAGNIFICATION_CONTROLLER_NAME);
Settings.Secure.putInt(
sContentResolver,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
AccessibilityUtil.State.ON);
PreferredShortcuts.updatePreferredShortcutsFromSettings(mContext,
Set.of(MAGNIFICATION_CONTROLLER_NAME));
int expectedShortcutTypes = ShortcutConstants.UserShortcutType.HARDWARE
| ShortcutConstants.UserShortcutType.TRIPLETAP;
assertThat(
PreferredShortcuts.retrieveUserShortcutType(
mContext, MAGNIFICATION_CONTROLLER_NAME
))
.isEqualTo(expectedShortcutTypes);
}
@Test
public void updatePreferredShortcutsFromSetting_magnificationWithNoActiveShortcuts_noChangesOnPreferredShortcutTypes() {
int expectedShortcutTypes = ShortcutConstants.UserShortcutType.HARDWARE
| ShortcutConstants.UserShortcutType.SOFTWARE;
PreferredShortcuts.saveUserShortcutType(mContext,
new PreferredShortcut(MAGNIFICATION_CONTROLLER_NAME, expectedShortcutTypes));
PreferredShortcuts.updatePreferredShortcutsFromSettings(mContext,
Set.of(MAGNIFICATION_CONTROLLER_NAME));
assertThat(
PreferredShortcuts.retrieveUserShortcutType(
mContext, MAGNIFICATION_CONTROLLER_NAME
))
.isEqualTo(expectedShortcutTypes);
}
@Test
public void updatePreferredShortcutsFromSetting_multipleComponents_preferredShortcutsMatches() {
String target1 = COLOR_INVERSION_COMPONENT_NAME.flattenToString();
String target2 = DALTONIZER_COMPONENT_NAME.flattenToString();
Settings.Secure.putString(sContentResolver,
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, target1);
Settings.Secure.putString(sContentResolver,
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
target1 + ShortcutConstants.SERVICES_SEPARATOR + target2);
int target1ShortcutTypes = ShortcutConstants.UserShortcutType.HARDWARE
| ShortcutConstants.UserShortcutType.SOFTWARE;
int target2ShortcutTypes = ShortcutConstants.UserShortcutType.HARDWARE;
PreferredShortcuts.updatePreferredShortcutsFromSettings(mContext, Set.of(target1, target2));
assertThat(
PreferredShortcuts.retrieveUserShortcutType(
mContext, target1
))
.isEqualTo(target1ShortcutTypes);
assertThat(
PreferredShortcuts.retrieveUserShortcutType(
mContext, target2
))
.isEqualTo(target2ShortcutTypes);
}
private static void clearShortcuts() {
Settings.Secure.putString(sContentResolver,
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, "");
Settings.Secure.putString(sContentResolver,
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "");
Settings.Secure.putInt(
sContentResolver,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
AccessibilityUtil.State.OFF);
Settings.Secure.putInt(
sContentResolver,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED,
AccessibilityUtil.State.OFF);
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accessibility;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.os.UserHandle;
import android.provider.Settings;
import androidx.preference.SwitchPreference;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.core.BasePreferenceController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class PrimaryMonoPreferenceControllerTest {
private static final int ON = 1;
private static final int OFF = 0;
private static final int UNKNOWN = -1;
private Context mContext;
private SwitchPreference mPreference;
private PrimaryMonoPreferenceController mController;
@Before
public void setUp() {
mContext = ApplicationProvider.getApplicationContext();
mPreference = new SwitchPreference(mContext);
mController = new PrimaryMonoPreferenceController(mContext, "test_key");
}
@Test
public void getAvailabilityStatus_byDefault_shouldReturnAvailable() {
assertThat(mController.getAvailabilityStatus()).isEqualTo(
BasePreferenceController.AVAILABLE);
}
@Test
public void isChecked_enabledMonoAudio_shouldReturnTrue() {
Settings.System.putIntForUser(mContext.getContentResolver(),
Settings.System.MASTER_MONO, ON, UserHandle.USER_CURRENT);
mController.updateState(mPreference);
assertThat(mController.isChecked()).isTrue();
assertThat(mPreference.isChecked()).isTrue();
}
@Test
public void isChecked_disabledMonoAudio_shouldReturnFalse() {
Settings.System.putIntForUser(mContext.getContentResolver(),
Settings.System.MASTER_MONO, OFF, UserHandle.USER_CURRENT);
mController.updateState(mPreference);
assertThat(mController.isChecked()).isFalse();
assertThat(mPreference.isChecked()).isFalse();
}
@Test
public void setChecked_setTrue_shouldEnableMonoAudio() {
mController.setChecked(true);
assertThat(Settings.System.getIntForUser(mContext.getContentResolver(),
Settings.System.MASTER_MONO, UNKNOWN, UserHandle.USER_CURRENT)).isEqualTo(ON);
}
@Test
public void setChecked_setFalse_shouldDisableMonoAudio() {
mController.setChecked(false);
assertThat(Settings.System.getIntForUser(mContext.getContentResolver(),
Settings.System.MASTER_MONO, UNKNOWN, UserHandle.USER_CURRENT)).isEqualTo(OFF);
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accessibility;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import android.provider.Settings;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.internal.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class ReduceBrightColorsIntensityPreferenceControllerTest {
private Context mContext;
private Resources mResources;
private ReduceBrightColorsIntensityPreferenceController mPreferenceController;
@Before
public void setUp() {
mContext = spy(ApplicationProvider.getApplicationContext());
mResources = spy(mContext.getResources());
when(mContext.getResources()).thenReturn(mResources);
mPreferenceController = new ReduceBrightColorsIntensityPreferenceController(mContext,
"rbc_intensity");
}
@Test
public void isAvailable_configuredRbcAvailable_enabledRbc_shouldReturnTrue() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 1);
doReturn(true).when(mResources).getBoolean(
R.bool.config_reduceBrightColorsAvailable);
assertThat(mPreferenceController.isAvailable()).isTrue();
}
@Test
public void isAvailable_configuredRbcAvailable_disabledRbc_shouldReturnTrue() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 0);
doReturn(true).when(mResources).getBoolean(
R.bool.config_reduceBrightColorsAvailable);
assertThat(mPreferenceController.isAvailable()).isTrue();
}
@Test
public void isAvailable_configuredRbcUnavailable_enabledRbc_shouldReturnFalse() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 1);
doReturn(false).when(mResources).getBoolean(
R.bool.config_reduceBrightColorsAvailable);
assertThat(mPreferenceController.isAvailable()).isFalse();
}
@Test
public void onPreferenceChange_changesTemperature() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 1);
mPreferenceController.onPreferenceChange(/* preference= */ null, 20);
assertThat(
Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL, 0))
.isEqualTo(80);
}
@Test
public void rangeOfSlider_staysWithinValidRange() {
when(mResources.getInteger(
R.integer.config_reduceBrightColorsStrengthMax)).thenReturn(90);
when(mResources.getInteger(
R.integer.config_reduceBrightColorsStrengthMin)).thenReturn(15);
assertThat(mPreferenceController.getMax()).isEqualTo(85);
assertThat(mPreferenceController.getMin()).isEqualTo(10);
assertThat(mPreferenceController.getMax() - mPreferenceController.getMin())
.isEqualTo(75);
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accessibility;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.provider.Settings;
import androidx.preference.SwitchPreference;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class ReduceBrightColorsPersistencePreferenceControllerTest {
private static final String PREF_KEY = "rbc_persist";
private static final String RBC_PERSIST =
Settings.Secure.REDUCE_BRIGHT_COLORS_PERSIST_ACROSS_REBOOTS;
private static final int ON = 1;
private static final int OFF = 0;
private static final int UNKNOWN = -1;
private final Context mContext = ApplicationProvider.getApplicationContext();
private final SwitchPreference mPreference = new SwitchPreference(mContext);
private final ReduceBrightColorsPersistencePreferenceController mController =
new ReduceBrightColorsPersistencePreferenceController(mContext, PREF_KEY);
@Test
public void isChecked_enabledRbc_shouldReturnTrue() {
Settings.Secure.putInt(mContext.getContentResolver(), RBC_PERSIST, ON);
mController.updateState(mPreference);
assertThat(mController.isChecked()).isTrue();
assertThat(mPreference.isChecked()).isTrue();
}
@Test
public void isChecked_disabledRbc_shouldReturnFalse() {
Settings.Secure.putInt(mContext.getContentResolver(), RBC_PERSIST, OFF);
mController.updateState(mPreference);
assertThat(mController.isChecked()).isFalse();
assertThat(mPreference.isChecked()).isFalse();
}
@Test
public void setChecked_setTrue_shouldEnableRbc() {
mController.setChecked(true);
assertThat(
Settings.Secure.getInt(mContext.getContentResolver(), RBC_PERSIST, UNKNOWN))
.isEqualTo(ON);
}
@Test
public void setChecked_setFalse_shouldDisableRbc() {
mController.setChecked(false);
assertThat(
Settings.Secure.getInt(mContext.getContentResolver(), RBC_PERSIST, UNKNOWN))
.isEqualTo(OFF);
}
}

View File

@@ -0,0 +1,98 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accessibility;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import android.provider.Settings;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.internal.R;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class ReduceBrightColorsPreferenceControllerTest {
private static final String PREF_KEY = "rbc_preference";
private Context mContext;
private Resources mResources;;
private ReduceBrightColorsPreferenceController mController;
@Before
public void setUp() {
mContext = spy(ApplicationProvider.getApplicationContext());
mResources = spy(mContext.getResources());
when(mContext.getResources()).thenReturn(mResources);
mController = new ReduceBrightColorsPreferenceController(mContext,
PREF_KEY);
}
@Test
public void getSummary_returnSummary() {
assertThat(mController.getSummary().toString().contains(
resourceString("reduce_bright_colors_preference_summary"))).isTrue();
}
@Ignore("ColorDisplayManager runs in a different thread which results in a race condition")
@Test
public void isChecked_reduceBrightColorsIsActivated_returnTrue() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 1);
assertThat(mController.isChecked()).isTrue();
}
@Ignore("ColorDisplayManager runs in a different thread which results in a race condition")
@Test
public void isChecked_reduceBrightColorsIsNotActivated_returnFalse() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 0);
assertThat(mController.isChecked()).isFalse();
}
@Test
public void isAvailable_configuredRbcAvailable_shouldReturnTrue() {
doReturn(true).when(mResources).getBoolean(
R.bool.config_reduceBrightColorsAvailable);
assertThat(mController.isAvailable()).isTrue();
}
@Test
public void isAvailable_configuredRbcUnAvailable_shouldReturnFalse() {
doReturn(false).when(mResources).getBoolean(
R.bool.config_reduceBrightColorsAvailable);
assertThat(mController.isAvailable()).isFalse();
}
private int resourceId(String type, String name) {
return mContext.getResources().getIdentifier(name, type, mContext.getPackageName());
}
private String resourceString(String name) {
return mContext.getResources().getString(resourceId("string", name));
}
}

View File

@@ -0,0 +1,7 @@
{
"imports": [
{
"path": "packages/apps/Settings/src/com/android/settings/accessibility/TEST_MAPPING"
}
]
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accounts;
import static com.google.common.truth.Truth.assertThat;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.lang.reflect.Modifier;
@RunWith(AndroidJUnit4.class)
public class RemoveUserFragmentTest {
@Test
public void testClassModifier_shouldBePublic() {
final int modifiers = RemoveUserFragment.class.getModifiers();
assertThat(Modifier.isPublic(modifiers)).isTrue();
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accounts;
import static com.google.common.truth.Truth.assertThat;
import android.accounts.Account;
import android.content.Context;
import android.os.UserHandle;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class SyncStateSwitchPreferenceTest {
private Context mContext;
private SyncStateSwitchPreference mPreference;
@Before
public void setup() {
mContext = ApplicationProvider.getApplicationContext();
}
@Test
public void setup_validAuthority_shouldBeVisible() {
mPreference = new SyncStateSwitchPreference(mContext, null /* attrs */);
mPreference.setup(new Account("name", "type"), "authority", mContext.getPackageName(),
UserHandle.USER_CURRENT);
assertThat(mPreference.isVisible()).isTrue();
}
@Test
public void setup_emptyAuthority_shouldBeInvisible() {
mPreference = new SyncStateSwitchPreference(mContext, null /* attrs */);
mPreference.setup(new Account("name", "type"), null /* authority */,
mContext.getPackageName(), UserHandle.USER_CURRENT);
assertThat(mPreference.isVisible()).isFalse();
}
}

View File

@@ -0,0 +1,155 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.LocaleConfig;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.os.LocaleList;
import android.util.FeatureFlagUtils;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
@RunWith(AndroidJUnit4.class)
public class AppLocaleUtilTest {
@Mock
private PackageManager mPackageManager;
@Mock
private ActivityManager mActivityManager;
@Mock
private Resources mResources;
@Mock
private LocaleConfig mLocaleConfig;
private Context mContext;
private String mDisallowedPackage = "com.disallowed.package";
private String mAllowedPackage = "com.allowed.package";
private List<ResolveInfo> mListResolveInfo;
private ApplicationInfo mAppInfo;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mContext.getSystemService(ActivityManager.class)).thenReturn(mActivityManager);
FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_APP_LOCALE_OPT_IN_ENABLED,
true);
setDisallowedPackageName(mDisallowedPackage);
mAppInfo = new ApplicationInfo();
mLocaleConfig = mock(LocaleConfig.class);
}
@After
public void tearDown() {
AppLocaleUtil.sLocaleConfig = null;
}
@Test
public void canDisplayLocaleUi_showUI() {
when(mLocaleConfig.getStatus()).thenReturn(LocaleConfig.STATUS_SUCCESS);
when(mLocaleConfig.getSupportedLocales()).thenReturn(LocaleList.forLanguageTags("en-US"));
AppLocaleUtil.sLocaleConfig = mLocaleConfig;
setActivityInfo(mAllowedPackage);
mAppInfo.packageName = mAllowedPackage;
assertTrue(AppLocaleUtil.canDisplayLocaleUi(mContext, mAppInfo, mListResolveInfo));
}
@Test
public void canDisplayLocaleUi_notShowUI_hasPlatformKey() {
setActivityInfo(mAllowedPackage);
mAppInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY;
mAppInfo.packageName = mAllowedPackage;
assertFalse(AppLocaleUtil.canDisplayLocaleUi(mContext, mAppInfo, mListResolveInfo));
}
@Test
public void canDisplayLocaleUi_notShowUI_noLauncherEntry() {
setActivityInfo("");
assertFalse(AppLocaleUtil.canDisplayLocaleUi(mContext, mAppInfo, mListResolveInfo));
}
@Test
public void canDisplayLocaleUi_notShowUI_matchDisallowedPackageList() {
setActivityInfo("");
mAppInfo.packageName = mDisallowedPackage;
assertFalse(AppLocaleUtil
.canDisplayLocaleUi(mContext, mAppInfo, mListResolveInfo));
}
@Test
public void getPackageLocales_getLocales_success() {
when(mLocaleConfig.getStatus()).thenReturn(LocaleConfig.STATUS_SUCCESS);
when(mLocaleConfig.getSupportedLocales()).thenReturn(LocaleList.forLanguageTags("en-US"));
LocaleList result = AppLocaleUtil.getPackageLocales(mLocaleConfig);
assertFalse(result.isEmpty());
}
@Test
public void getPackageLocales_getLocales_failed() {
when(mLocaleConfig.getStatus()).thenReturn(LocaleConfig.STATUS_PARSING_FAILED);
LocaleList result = AppLocaleUtil.getPackageLocales(mLocaleConfig);
assertNull(result);
}
private void setDisallowedPackageName(String packageName) {
when(mContext.getResources()).thenReturn(mResources);
when(mResources.getStringArray(anyInt())).thenReturn(new String[]{packageName});
}
private void setActivityInfo(String packageName) {
ResolveInfo resolveInfo = mock(ResolveInfo.class);
ActivityInfo activityInfo = mock(ActivityInfo.class);
activityInfo.packageName = packageName;
resolveInfo.activityInfo = activityInfo;
mListResolveInfo = new ArrayList<>();
mListResolveInfo.add(resolveInfo);
}
}

View File

@@ -0,0 +1,228 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import android.Manifest;
import android.app.AlarmManager;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.PowerExemptionManager;
import android.os.UserHandle;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import libcore.util.EmptyArray;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class AppStateAlarmsAndRemindersBridgeTest {
private static final String TEST_PACKAGE = "com.example.test.1";
private static final int TEST_UID = 12345;
@Mock
private AlarmManager mAlarmManager;
@Mock
private PowerExemptionManager mPowerExemptionManager;
@Mock
private PackageManager mPackageManager;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
}
@Test
public void alarmsAndRemindersState_shouldBeVisible() {
boolean seaPermissionRequested;
boolean ueaPermissionRequested;
boolean seaPermissionGranted;
boolean allowListed;
for (int i = 0; i < (1 << 4); i++) {
seaPermissionRequested = (i & 1) != 0;
ueaPermissionRequested = (i & (1 << 1)) != 0;
seaPermissionGranted = (i & (1 << 2)) != 0;
allowListed = (i & (1 << 3)) != 0;
final boolean visible = new AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState(
seaPermissionRequested,
ueaPermissionRequested,
seaPermissionGranted,
allowListed).shouldBeVisible();
assertWithMessage("Wrong return value " + visible
+ " for {seaPermissionRequested = " + seaPermissionRequested
+ ", ueaPermissionRequested = " + ueaPermissionRequested
+ ", seaPermissionGranted = " + seaPermissionGranted
+ ", allowListed = " + allowListed + "}")
.that(visible)
.isEqualTo(seaPermissionRequested && !ueaPermissionRequested && !allowListed);
}
}
@Test
public void alarmsAndRemindersState_isAllowed() {
boolean seaPermissionRequested;
boolean ueaPermissionRequested;
boolean seaPermissionGranted;
boolean allowListed;
for (int i = 0; i < (1 << 4); i++) {
seaPermissionRequested = (i & 1) != 0;
ueaPermissionRequested = (i & (1 << 1)) != 0;
seaPermissionGranted = (i & (1 << 2)) != 0;
allowListed = (i & (1 << 3)) != 0;
final boolean allowed = new AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState(
seaPermissionRequested,
ueaPermissionRequested,
seaPermissionGranted,
allowListed).isAllowed();
assertWithMessage("Wrong return value " + allowed
+ " for {seaPermissionRequested = " + seaPermissionRequested
+ ", ueaPermissionRequested = " + ueaPermissionRequested
+ ", seaPermissionGranted = " + seaPermissionGranted
+ ", allowListed = " + allowListed + "}")
.that(allowed)
.isEqualTo(seaPermissionGranted || ueaPermissionRequested || allowListed);
}
}
private PackageInfo createPackageInfoWithPermissions(String... requestedPermissions) {
final PackageInfo info = new PackageInfo();
info.requestedPermissions = requestedPermissions;
return info;
}
@Test
public void createPermissionState_SeaGrantedNoUeaNoAllowlist() throws Exception {
AppStateAlarmsAndRemindersBridge bridge = new AppStateAlarmsAndRemindersBridge(mContext,
null, null);
bridge.mAlarmManager = mAlarmManager;
bridge.mPackageManager = mPackageManager;
bridge.mPowerExemptionManager = mPowerExemptionManager;
doReturn(true).when(mAlarmManager).hasScheduleExactAlarm(TEST_PACKAGE,
UserHandle.getUserId(TEST_UID));
doReturn(createPackageInfoWithPermissions(Manifest.permission.SCHEDULE_EXACT_ALARM))
.when(mPackageManager).getPackageInfoAsUser(eq(TEST_PACKAGE), anyInt(), anyInt());
doReturn(false).when(mPowerExemptionManager).isAllowListed(TEST_PACKAGE, true);
AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState state =
bridge.createPermissionState(TEST_PACKAGE, TEST_UID);
assertThat(state.shouldBeVisible()).isTrue();
assertThat(state.isAllowed()).isTrue();
}
@Test
public void createPermissionState_requestsBothSeaDeniedNoAllowlist() throws Exception {
AppStateAlarmsAndRemindersBridge bridge = new AppStateAlarmsAndRemindersBridge(mContext,
null, null);
bridge.mAlarmManager = mAlarmManager;
bridge.mPackageManager = mPackageManager;
bridge.mPowerExemptionManager = mPowerExemptionManager;
doReturn(false).when(mAlarmManager).hasScheduleExactAlarm(TEST_PACKAGE,
UserHandle.getUserId(TEST_UID));
doReturn(createPackageInfoWithPermissions(
Manifest.permission.SCHEDULE_EXACT_ALARM,
Manifest.permission.USE_EXACT_ALARM))
.when(mPackageManager).getPackageInfoAsUser(eq(TEST_PACKAGE), anyInt(), anyInt());
doReturn(false).when(mPowerExemptionManager).isAllowListed(TEST_PACKAGE, true);
AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState state =
bridge.createPermissionState(TEST_PACKAGE, TEST_UID);
assertThat(state.shouldBeVisible()).isFalse();
assertThat(state.isAllowed()).isTrue();
}
@Test
public void createPermissionState_requestsNoneNoAllowlist() throws Exception {
AppStateAlarmsAndRemindersBridge bridge = new AppStateAlarmsAndRemindersBridge(mContext,
null, null);
bridge.mAlarmManager = mAlarmManager;
bridge.mPackageManager = mPackageManager;
bridge.mPowerExemptionManager = mPowerExemptionManager;
doReturn(false).when(mAlarmManager).hasScheduleExactAlarm(TEST_PACKAGE,
UserHandle.getUserId(TEST_UID));
doReturn(createPackageInfoWithPermissions(EmptyArray.STRING))
.when(mPackageManager).getPackageInfoAsUser(eq(TEST_PACKAGE), anyInt(), anyInt());
doReturn(false).when(mPowerExemptionManager).isAllowListed(TEST_PACKAGE, true);
AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState state =
bridge.createPermissionState(TEST_PACKAGE, TEST_UID);
assertThat(state.shouldBeVisible()).isFalse();
assertThat(state.isAllowed()).isFalse();
}
@Test
public void createPermissionState_requestsOnlyUeaNoAllowlist() throws Exception {
AppStateAlarmsAndRemindersBridge bridge = new AppStateAlarmsAndRemindersBridge(mContext,
null, null);
bridge.mAlarmManager = mAlarmManager;
bridge.mPackageManager = mPackageManager;
bridge.mPowerExemptionManager = mPowerExemptionManager;
doReturn(false).when(mAlarmManager).hasScheduleExactAlarm(TEST_PACKAGE,
UserHandle.getUserId(TEST_UID));
doReturn(createPackageInfoWithPermissions(Manifest.permission.USE_EXACT_ALARM))
.when(mPackageManager).getPackageInfoAsUser(eq(TEST_PACKAGE), anyInt(), anyInt());
doReturn(false).when(mPowerExemptionManager).isAllowListed(TEST_PACKAGE, true);
AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState state =
bridge.createPermissionState(TEST_PACKAGE, TEST_UID);
assertThat(state.shouldBeVisible()).isFalse();
assertThat(state.isAllowed()).isTrue();
}
@Test
public void createPermissionState_requestsNoneButAllowlisted() throws Exception {
AppStateAlarmsAndRemindersBridge bridge = new AppStateAlarmsAndRemindersBridge(mContext,
null, null);
bridge.mAlarmManager = mAlarmManager;
bridge.mPackageManager = mPackageManager;
bridge.mPowerExemptionManager = mPowerExemptionManager;
doReturn(false).when(mAlarmManager).hasScheduleExactAlarm(TEST_PACKAGE,
UserHandle.getUserId(TEST_UID));
doReturn(createPackageInfoWithPermissions(EmptyArray.STRING))
.when(mPackageManager).getPackageInfoAsUser(eq(TEST_PACKAGE), anyInt(), anyInt());
doReturn(true).when(mPowerExemptionManager).isAllowListed(TEST_PACKAGE, true);
AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState state =
bridge.createPermissionState(TEST_PACKAGE, TEST_UID);
assertThat(state.shouldBeVisible()).isFalse();
assertThat(state.isAllowed()).isTrue();
}
}

View File

@@ -0,0 +1,78 @@
package com.android.settings.applications;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.content.Context;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.fuelgauge.PowerAllowlistBackend;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public final class AppStateAppBatteryUsageBridgeTest {
private static final String TEST_PACKAGE_1 = "com.example.test.pkg1";
private static final String TEST_PACKAGE_2 = "com.example.test.pkg2";
private static final int UID_1 = 12345;
private static final int UID_2 = 7654321;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@Mock
private AppOpsManager mAppOpsManager;
@Mock
private PowerAllowlistBackend mPowerAllowlistBackend;
@Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
}
@Test
public void updateExtraInfo_updatesRestricted() {
when(mPowerAllowlistBackend.isAllowlisted(TEST_PACKAGE_1, UID_1)).thenReturn(false);
when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
UID_1, TEST_PACKAGE_1)).thenReturn(AppOpsManager.MODE_IGNORED);
AppStateAppBatteryUsageBridge bridge =
new AppStateAppBatteryUsageBridge(mContext, null, null);
bridge.mAppOpsManager = mAppOpsManager;
bridge.mPowerAllowlistBackend = mPowerAllowlistBackend;
AppEntry entry = new AppEntry(mContext, null, 0);
bridge.updateExtraInfo(entry, TEST_PACKAGE_1, UID_1);
assertThat(entry.extraInfo.getClass())
.isEqualTo(AppStateAppBatteryUsageBridge.AppBatteryUsageDetails.class);
assertThat(AppStateAppBatteryUsageBridge.getAppBatteryUsageDetailsMode(entry))
.isEqualTo(AppStateAppBatteryUsageBridge.MODE_RESTRICTED);
}
@Test
public void updateExtraInfo_updatesUnrestricted() {
when(mPowerAllowlistBackend.isAllowlisted(TEST_PACKAGE_1, UID_1)).thenReturn(true);
when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
UID_2, TEST_PACKAGE_2)).thenReturn(AppOpsManager.MODE_ALLOWED);
AppStateAppBatteryUsageBridge bridge =
new AppStateAppBatteryUsageBridge(mContext, null, null);
bridge.mAppOpsManager = mAppOpsManager;
bridge.mPowerAllowlistBackend = mPowerAllowlistBackend;
AppEntry entry = new AppEntry(mContext, null, 0);
bridge.updateExtraInfo(entry, TEST_PACKAGE_2, UID_2);
assertThat(entry.extraInfo.getClass())
.isEqualTo(AppStateAppBatteryUsageBridge.AppBatteryUsageDetails.class);
assertThat(AppStateAppBatteryUsageBridge.getAppBatteryUsageDetailsMode(entry))
.isEqualTo(AppStateAppBatteryUsageBridge.MODE_UNRESTRICTED);
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications;
import static com.google.common.truth.Truth.assertThat;
import android.app.AppOpsManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class AppStateInstallAppsBridgeTest {
@Test
public void testInstallAppsStateCanInstallApps() {
AppStateInstallAppsBridge.InstallAppsState appState =
new AppStateInstallAppsBridge.InstallAppsState();
assertThat(appState.canInstallApps()).isFalse();
appState.permissionRequested = true;
assertThat(appState.canInstallApps()).isFalse();
appState.appOpMode = AppOpsManager.MODE_ALLOWED;
assertThat(appState.canInstallApps()).isTrue();
appState.appOpMode = AppOpsManager.MODE_ERRORED;
assertThat(appState.canInstallApps()).isFalse();
}
@Test
public void testInstallAppsStateIsPotentialAppSource() {
AppStateInstallAppsBridge.InstallAppsState appState =
new AppStateInstallAppsBridge.InstallAppsState();
assertThat(appState.isPotentialAppSource()).isFalse();
appState.appOpMode = AppOpsManager.MODE_ERRORED;
assertThat(appState.isPotentialAppSource()).isTrue();
appState.permissionRequested = true;
appState.appOpMode = AppOpsManager.MODE_DEFAULT;
assertThat(appState.isPotentialAppSource()).isTrue();
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications;
import static org.junit.Assert.assertTrue;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.R;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class EnterpriseDefaultAppsTest {
@Test
public void testNumberOfIntentsCorrelateWithUI() {
final int[] concatenation_templates =
new int[]{0 /* no need for single app name */,
R.string.app_names_concatenation_template_2,
R.string.app_names_concatenation_template_3};
for (EnterpriseDefaultApps app : EnterpriseDefaultApps.values()) {
assertTrue("Number of intents should be limited by number of apps the UI can show",
app.getIntents().length <= concatenation_templates.length);
}
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications;
import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION;
import static com.android.settings.Utils.PROPERTY_APP_HIBERNATION_ENABLED;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageStatsManager;
import android.apphibernation.AppHibernationManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Looper;
import android.provider.DeviceConfig;
import androidx.preference.Preference;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class HibernatedAppsPreferenceControllerTest {
@Mock
PackageManager mPackageManager;
@Mock
AppHibernationManager mAppHibernationManager;
@Mock
IUsageStatsManager mIUsageStatsManager;
PreferenceScreen mPreferenceScreen;
private static final String KEY = "key";
private Context mContext;
private HibernatedAppsPreferenceController mController;
@Before
public void setUp() {
if (Looper.myLooper() == null) {
Looper.prepare();
}
MockitoAnnotations.initMocks(this);
DeviceConfig.setProperty(NAMESPACE_APP_HIBERNATION, PROPERTY_APP_HIBERNATION_ENABLED,
"true", false);
mContext = spy(ApplicationProvider.getApplicationContext());
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mContext.getSystemService(AppHibernationManager.class))
.thenReturn(mAppHibernationManager);
when(mContext.getSystemService(UsageStatsManager.class)).thenReturn(
new UsageStatsManager(mContext, mIUsageStatsManager));
PreferenceManager manager = new PreferenceManager(mContext);
mPreferenceScreen = manager.createPreferenceScreen(mContext);
Preference preference = mock(Preference.class);
when(preference.getKey()).thenReturn(KEY);
mPreferenceScreen.addPreference(preference);
mController = new HibernatedAppsPreferenceController(mContext, KEY,
command -> command.run());
}
@Test
public void getAvailabilityStatus_featureDisabled_shouldNotReturnAvailable() {
DeviceConfig.setProperty(NAMESPACE_APP_HIBERNATION, PROPERTY_APP_HIBERNATION_ENABLED,
"false", true);
assertThat((mController).getAvailabilityStatus()).isNotEqualTo(AVAILABLE);
}
}

View File

@@ -0,0 +1,474 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.appcompat;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_APP_DEFAULT;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE;
import static com.android.settings.applications.appcompat.UserAspectRatioManager.KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN;
import static com.android.settings.applications.appcompat.UserAspectRatioManager.KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS;
import static com.android.window.flags.Flags.FLAG_USER_MIN_ASPECT_RATIO_APP_DEFAULT;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.RemoteException;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.DeviceConfig;
import androidx.annotation.NonNull;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.internal.R;
import com.android.settings.testutils.ResourcesUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.List;
/**
* To run this test: atest SettingsUnitTests:UserAspectRatioManagerTest
*/
@RunWith(AndroidJUnit4.class)
public class UserAspectRatioManagerTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
private final String mPackageName = "com.test.mypackage";
private Context mContext;
private Resources mResources;
private FakeUserAspectRatioManager mUtils;
private String mOriginalSettingsFlag;
private String mOriginalFullscreenFlag;
private IPackageManager mIPm;
private PackageManager mPm;
private List<LauncherActivityInfo> mLauncherActivities;
@Before
public void setUp() throws RemoteException, PackageManager.NameNotFoundException {
mContext = spy(ApplicationProvider.getApplicationContext());
mResources = mock(Resources.class);
final LauncherApps launcherApps = mock(LauncherApps.class);
mLauncherActivities = mock(List.class);
mIPm = mock(IPackageManager.class);
mPm = mock(PackageManager.class);
when(mContext.getPackageManager()).thenReturn(mPm);
when(mContext.getResources()).thenReturn(mResources);
when(mContext.getSystemService(LauncherApps.class)).thenReturn(launcherApps);
enableAllDefaultAspectRatioOptions();
mSetFlagsRule.disableFlags(FLAG_USER_MIN_ASPECT_RATIO_APP_DEFAULT);
mUtils = new FakeUserAspectRatioManager(mContext, mIPm);
doReturn(mLauncherActivities).when(launcherApps).getActivityList(anyString(), any());
mOriginalSettingsFlag = DeviceConfig.getProperty(
DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS);
setAspectRatioSettingsBuildTimeFlagEnabled(true);
setAspectRatioSettingsDeviceConfigEnabled("true" /* enabled */, false /* makeDefault */);
mOriginalFullscreenFlag = DeviceConfig.getProperty(
DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN);
setAspectRatioFullscreenBuildTimeFlagEnabled(true);
setAspectRatioFullscreenDeviceConfigEnabled("true" /* enabled */, false /* makeDefault */);
mockProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE, true);
mockProperty(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE, true);
mockProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, true);
}
@After
public void tearDown() {
setAspectRatioSettingsDeviceConfigEnabled(mOriginalSettingsFlag, true /* makeDefault */);
setAspectRatioFullscreenDeviceConfigEnabled(mOriginalFullscreenFlag,
true /* makeDefault */);
}
@Test
public void testCanDisplayAspectRatioUi() {
final ApplicationInfo canDisplay = new ApplicationInfo();
canDisplay.packageName = mPackageName;
doReturn(false).when(mLauncherActivities).isEmpty();
assertTrue(mUtils.canDisplayAspectRatioUi(canDisplay));
final ApplicationInfo noLauncherEntry = new ApplicationInfo();
noLauncherEntry.packageName = mPackageName;
doReturn(true).when(mLauncherActivities).isEmpty();
assertFalse(mUtils.canDisplayAspectRatioUi(noLauncherEntry));
}
@Test
public void testCanDisplayAspectRatioUi_hasLauncher_propertyFalse_returnFalse()
throws PackageManager.NameNotFoundException {
mockProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, false);
doReturn(false).when(mLauncherActivities).isEmpty();
final ApplicationInfo canDisplay = new ApplicationInfo();
canDisplay.packageName = mPackageName;
assertFalse(mUtils.canDisplayAspectRatioUi(canDisplay));
}
@Test
public void testCanDisplayAspectRatioUi_noLauncher_propertyTrue_returnFalse()
throws PackageManager.NameNotFoundException {
mockProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, true);
doReturn(true).when(mLauncherActivities).isEmpty();
final ApplicationInfo noLauncherEntry = new ApplicationInfo();
noLauncherEntry.packageName = mPackageName;
assertFalse(mUtils.canDisplayAspectRatioUi(noLauncherEntry));
}
@Test
public void testIsFeatureEnabled() {
assertTrue(UserAspectRatioManager.isFeatureEnabled(mContext));
}
@Test
public void testIsFeatureEnabled_disabledBuildTimeFlag_returnFalse() {
setAspectRatioSettingsBuildTimeFlagEnabled(false);
assertFalse(UserAspectRatioManager.isFeatureEnabled(mContext));
}
@Test
public void testIsFeatureEnabled_disabledRuntimeFlag_returnFalse() {
setAspectRatioSettingsDeviceConfigEnabled("false" /* enabled */, false /* makeDefault */);
assertFalse(UserAspectRatioManager.isFeatureEnabled(mContext));
}
@Test
public void testIsFullscreenOptionEnabled() {
assertTrue(mUtils.isFullscreenOptionEnabled(mPackageName));
}
@Test
public void testIsFullscreenOptionEnabled_settingsDisabled_returnFalse() {
setAspectRatioFullscreenBuildTimeFlagEnabled(false);
assertFalse(mUtils.isFullscreenOptionEnabled(mPackageName));
}
@Test
public void testIsFullscreenOptionEnabled_disabledBuildTimeFlag_returnFalse() {
setAspectRatioFullscreenBuildTimeFlagEnabled(false);
assertFalse(mUtils.isFullscreenOptionEnabled(mPackageName));
}
@Test
public void testIsFullscreenOptionEnabled_disabledRuntimeFlag_returnFalse() {
setAspectRatioFullscreenDeviceConfigEnabled("false" /* enabled */, false /*makeDefault */);
assertFalse(mUtils.isFullscreenOptionEnabled(mPackageName));
}
@Test
public void testIsFullscreenOptionEnabled_propertyFalse_returnsFalse()
throws PackageManager.NameNotFoundException {
mockProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE, false);
assertFalse(mUtils.isFullscreenOptionEnabled(mPackageName));
}
@Test
public void testIsFullscreenOptionEnabled_propertyTrue_configDisabled_returnsFalse()
throws PackageManager.NameNotFoundException {
mockProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE, true);
setAspectRatioFullscreenDeviceConfigEnabled("false" /* enabled */, false /*makeDefault */);
assertFalse(mUtils.isFullscreenOptionEnabled(mPackageName));
}
@Test
public void testHasAspectRatioOption_fullscreen() {
assertTrue(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_FULLSCREEN,
mPackageName));
assertTrue(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_SPLIT_SCREEN,
mPackageName));
// Only fullscreen option should be disabled
when(mUtils.isFullscreenOptionEnabled(mPackageName)).thenReturn(false);
assertFalse(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_FULLSCREEN,
mPackageName));
assertTrue(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_SPLIT_SCREEN,
mPackageName));
}
private String getUserMinAspectRatioEntry(int aspectRatio, String packageName) {
return mUtils.getUserMinAspectRatioEntry(aspectRatio, packageName, mContext.getUserId());
}
@Test
public void testGetUserMinAspectRatioEntry() {
final Context context = ApplicationProvider.getApplicationContext();
// R.string.user_aspect_ratio_app_default
final String appDefault = ResourcesUtils.getResourcesString(context,
"user_aspect_ratio_app_default");
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName))
.isEqualTo(appDefault);
// should always return default if value does not correspond to anything
assertThat(getUserMinAspectRatioEntry(-1, mPackageName))
.isEqualTo(appDefault);
// R.string.user_aspect_ratio_half_screen
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_SPLIT_SCREEN,
mPackageName)).isEqualTo(ResourcesUtils.getResourcesString(context,
"user_aspect_ratio_half_screen"));
// R.string.user_aspect_ratio_display_size
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_DISPLAY_SIZE,
mPackageName)).isEqualTo(ResourcesUtils.getResourcesString(context,
"user_aspect_ratio_device_size"));
// R.string.user_aspect_ratio_16_9
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_16_9, mPackageName))
.isEqualTo(ResourcesUtils.getResourcesString(context, "user_aspect_ratio_16_9"));
// R.string.user_aspect_ratio_4_3
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_4_3, mPackageName))
.isEqualTo(ResourcesUtils.getResourcesString(context, "user_aspect_ratio_4_3"));
// R.string.user_aspect_ratio_3_2
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_3_2, mPackageName))
.isEqualTo(ResourcesUtils.getResourcesString(context, "user_aspect_ratio_3_2"));
// R.string.user_aspect_ratio_fullscreen
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_FULLSCREEN,
mPackageName)).isEqualTo(ResourcesUtils.getResourcesString(context,
"user_aspect_ratio_fullscreen"));
}
@Test
public void testGetUserMinAspectRatioEntry_fullscreenDisabled_shouldReturnDefault() {
setAspectRatioFullscreenBuildTimeFlagEnabled(false);
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_FULLSCREEN,
mPackageName)).isEqualTo(ResourcesUtils.getResourcesString(
ApplicationProvider.getApplicationContext(),
"user_aspect_ratio_app_default"));
}
@Test
public void testGetUserMinAspectRatioEntry_nonDefaultString_shouldReturnNewString() {
final String newOptionName = "new_option_name";
when(mResources.getIntArray(anyInt())).thenReturn(new int[] {USER_MIN_ASPECT_RATIO_UNSET});
when(mResources.getStringArray(anyInt())).thenReturn(new String[] {newOptionName});
mUtils = new FakeUserAspectRatioManager(mContext, mIPm);
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName))
.isEqualTo(newOptionName);
}
@Test
public void testGetUserMinAspectRatioMapping_noAppDefault_shouldThrowException() {
when(mResources.getIntArray(anyInt())).thenReturn(new int[] {USER_MIN_ASPECT_RATIO_4_3});
when(mResources.getStringArray(anyInt())).thenReturn(new String[] {"4:3"});
assertThrows(RuntimeException.class, () -> new FakeUserAspectRatioManager(mContext, mIPm));
}
@Test
public void testGetUserMinAspectRatioMapping_configLengthMismatch_shouldThrowException() {
when(mResources.getIntArray(anyInt())).thenReturn(new int[] {
USER_MIN_ASPECT_RATIO_UNSET,
USER_MIN_ASPECT_RATIO_4_3});
when(mResources.getStringArray(anyInt())).thenReturn(new String[] {"4:3"});
assertThrows(RuntimeException.class, () -> new FakeUserAspectRatioManager(mContext, mIPm));
}
@Test
public void testGetUserMinAspectRatioMapping_appDefaultFlagEnabled() {
// Flag is disabled by default, app default not loaded
assertFalse(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_APP_DEFAULT, mPackageName));
mSetFlagsRule.enableFlags(FLAG_USER_MIN_ASPECT_RATIO_APP_DEFAULT);
mUtils = new FakeUserAspectRatioManager(mContext, mIPm);
assertTrue(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_APP_DEFAULT, mPackageName));
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_APP_DEFAULT, mPackageName))
.isEqualTo(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName));
}
@Test
public void testGetUserMinAspectRatioEntry_enabledFullscreenOverride_returnsFullscreen() {
setIsOverrideToFullscreenEnabled(true);
// Fullscreen option is pre-selected
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName))
.isEqualTo(ResourcesUtils.getResourcesString(
ApplicationProvider.getApplicationContext(),
"user_aspect_ratio_fullscreen"));
// App default exists
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_APP_DEFAULT, mPackageName))
.isEqualTo(ResourcesUtils.getResourcesString(
ApplicationProvider.getApplicationContext(),
"user_aspect_ratio_app_default"));
}
@Test
public void testGetUserMinAspectRatioEntry_disabledFullscreenOverride_returnsUnchanged() {
setIsOverrideToFullscreenEnabled(false);
// Fullscreen option is not pre-selected
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName))
.isEqualTo(ResourcesUtils.getResourcesString(
ApplicationProvider.getApplicationContext(),
"user_aspect_ratio_app_default"));
}
@Test
public void testIsOverrideToFullscreenEnabled_returnsTrue()
throws PackageManager.NameNotFoundException {
setIsOverrideToFullscreenEnabled(true);
assertTrue(mUtils.isOverrideToFullscreenEnabled(mPackageName, mContext.getUserId()));
mockProperty(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE, true);
assertTrue(mUtils.isOverrideToFullscreenEnabled(mPackageName, mContext.getUserId()));
}
@Test
public void testIsOverrideToFullscreenEnabled_optOut_returnsFalse()
throws PackageManager.NameNotFoundException {
setIsOverrideToFullscreenEnabled(true);
mockProperty(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE, false);
assertFalse(mUtils.isOverrideToFullscreenEnabled(mPackageName, mContext.getUserId()));
}
@Test
public void testIsOverrideToFullscreenEnabled_flagDisabled_returnsFalse() {
mUtils.setFullscreenCompatChange(true);
assertFalse(mUtils.isOverrideToFullscreenEnabled(mPackageName, mContext.getUserId()));
}
@Test
public void testIsOverrideToFullscreenEnabled_optionDisabled_returnsFalse() {
mUtils.setFullscreenCompatChange(true);
when(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_FULLSCREEN, mPackageName))
.thenReturn(false);
assertFalse(mUtils.isOverrideToFullscreenEnabled(mPackageName, mContext.getUserId()));
}
private void setIsOverrideToFullscreenEnabled(boolean enabled) {
if (enabled) {
mSetFlagsRule.enableFlags(FLAG_USER_MIN_ASPECT_RATIO_APP_DEFAULT);
mUtils = new FakeUserAspectRatioManager(mContext, mIPm);
}
mUtils.setFullscreenCompatChange(enabled);
when(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_FULLSCREEN, mPackageName))
.thenReturn(enabled);
}
private void enableAllDefaultAspectRatioOptions() {
final int[] aspectRatioOptions = new int[] {
USER_MIN_ASPECT_RATIO_UNSET,
USER_MIN_ASPECT_RATIO_SPLIT_SCREEN,
USER_MIN_ASPECT_RATIO_DISPLAY_SIZE,
USER_MIN_ASPECT_RATIO_4_3,
USER_MIN_ASPECT_RATIO_16_9,
USER_MIN_ASPECT_RATIO_3_2,
USER_MIN_ASPECT_RATIO_FULLSCREEN};
when(mResources.getIntArray(anyInt())).thenReturn(aspectRatioOptions);
// String array config overlay with @null values so default strings should be used
when(mResources.getStringArray(anyInt())).thenReturn(new String[aspectRatioOptions.length]);
final Context context = ApplicationProvider.getApplicationContext();
mockString(context, "user_aspect_ratio_app_default");
mockString(context, "user_aspect_ratio_half_screen");
mockString(context, "user_aspect_ratio_device_size");
mockString(context, "user_aspect_ratio_4_3");
mockString(context, "user_aspect_ratio_16_9");
mockString(context, "user_aspect_ratio_3_2");
mockString(context, "user_aspect_ratio_fullscreen");
}
private void mockString(Context context, String stringResName) {
final int resId = ResourcesUtils.getResourcesId(context, "string", stringResName);
final String string = ResourcesUtils.getResourcesString(context, stringResName);
when(mContext.getString(resId)).thenReturn(string);
}
private void mockProperty(String propertyName, boolean value)
throws PackageManager.NameNotFoundException {
PackageManager.Property prop = new PackageManager.Property(
propertyName, value, mPackageName, "" /* className */);
when(mPm.getProperty(propertyName, mPackageName)).thenReturn(prop);
}
private void setAspectRatioSettingsBuildTimeFlagEnabled(boolean enabled) {
when(mResources.getBoolean(R.bool.config_appCompatUserAppAspectRatioSettingsIsEnabled))
.thenReturn(enabled);
}
private void setAspectRatioSettingsDeviceConfigEnabled(String enabled, boolean makeDefault) {
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS, enabled, makeDefault);
}
private void setAspectRatioFullscreenBuildTimeFlagEnabled(boolean enabled) {
when(mResources.getBoolean(R.bool.config_appCompatUserAppAspectRatioFullscreenIsEnabled))
.thenReturn(enabled);
}
private void setAspectRatioFullscreenDeviceConfigEnabled(String enabled, boolean makeDefault) {
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN, enabled, makeDefault);
}
private static class FakeUserAspectRatioManager extends UserAspectRatioManager {
private boolean mFullscreenCompatChange = false;
private FakeUserAspectRatioManager(@NonNull Context context, IPackageManager pm) {
super(context, pm);
}
@Override
boolean isFullscreenCompatChangeEnabled(String pkgName, int userId) {
return mFullscreenCompatChange;
}
void setFullscreenCompatChange(boolean enabled) {
mFullscreenCompatChange = enabled;
}
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.appinfo;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.core.BasePreferenceController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class AppLocalePreferenceControllerTest {
private Context mContext;
private boolean mCanDisplayLocaleUi;
private AppLocalePreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = ApplicationProvider.getApplicationContext();
mController = new AppLocalePreferenceController(mContext, "test_key") {
@Override
boolean canDisplayLocaleUi() {
return mCanDisplayLocaleUi;
}
};
}
@Test
public void getAvailabilityStatus_canShowUi_shouldReturnAvailable() {
mCanDisplayLocaleUi = true;
assertThat(mController.getAvailabilityStatus())
.isEqualTo(BasePreferenceController.AVAILABLE);
}
@Test
public void getAvailabilityStatus_canNotShowUi_shouldReturnUnavailable() {
mCanDisplayLocaleUi = false;
assertThat(mController.getAvailabilityStatus())
.isEqualTo(BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
}
}

View File

@@ -0,0 +1,181 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.appinfo;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.AppOpsManager.OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED;
import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION;
import static com.android.settings.Utils.PROPERTY_APP_HIBERNATION_ENABLED;
import static com.android.settings.Utils.PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.apphibernation.AppHibernationManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.provider.DeviceConfig;
import androidx.preference.SwitchPreference;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class HibernationSwitchPreferenceControllerTest {
private static final int PACKAGE_UID = 1;
private static final String INVALID_PACKAGE_NAME = "invalid_package";
private static final String KEY = "key";
private static final String VALID_PACKAGE_NAME = "package";
private static final String EXEMPTED_PACKAGE_NAME = "exempted_package";
private static final String UNEXEMPTED_PACKAGE_NAME = "unexempted_package";
@Mock
private AppOpsManager mAppOpsManager;
@Mock
private PackageManager mPackageManager;
@Mock
private AppHibernationManager mAppHibernationManager;
@Mock
private SwitchPreference mPreference;
private HibernationSwitchPreferenceController mController;
private Context mContext;
private String mOriginalPreSFlagValue;
@Before
public void setUp() throws PackageManager.NameNotFoundException {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
when(mContext.getSystemService(AppHibernationManager.class))
.thenReturn(mAppHibernationManager);
when(mPackageManager.getPackageUid(eq(VALID_PACKAGE_NAME), anyInt()))
.thenReturn(PACKAGE_UID);
when(mPackageManager.getPackageUid(eq(INVALID_PACKAGE_NAME), anyInt()))
.thenThrow(new PackageManager.NameNotFoundException());
when(mPackageManager.getTargetSdkVersion(eq(EXEMPTED_PACKAGE_NAME)))
.thenReturn(android.os.Build.VERSION_CODES.Q);
when(mPackageManager.getTargetSdkVersion(eq(UNEXEMPTED_PACKAGE_NAME)))
.thenReturn(android.os.Build.VERSION_CODES.S);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
DeviceConfig.setProperty(NAMESPACE_APP_HIBERNATION, PROPERTY_APP_HIBERNATION_ENABLED,
"true", true /* makeDefault */);
mController = new HibernationSwitchPreferenceController(mContext, KEY);
when(mPreference.getKey()).thenReturn(mController.getPreferenceKey());
mOriginalPreSFlagValue = DeviceConfig.getProperty(NAMESPACE_APP_HIBERNATION,
PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS);
}
@After
public void cleanUp() {
// Restore original device config values.
DeviceConfig.setProperty(NAMESPACE_APP_HIBERNATION, PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS,
mOriginalPreSFlagValue, true /* makeDefault */);
}
@Test
public void getAvailabilityStatus_featureNotEnabled_shouldNotReturnAvailable() {
mController.setPackage(VALID_PACKAGE_NAME);
DeviceConfig.setProperty(NAMESPACE_APP_HIBERNATION, PROPERTY_APP_HIBERNATION_ENABLED,
"false", true /* makeDefault */);
assertThat(mController.getAvailabilityStatus()).isNotEqualTo(AVAILABLE);
}
@Test
public void getAvailabilityStatus_invalidPackage_shouldReturnNotAvailable() {
mController.setPackage(INVALID_PACKAGE_NAME);
assertThat(mController.getAvailabilityStatus()).isNotEqualTo(AVAILABLE);
}
@Test
public void getAvailabilityStatus_validPackage_shouldReturnAvailable() {
mController.setPackage(VALID_PACKAGE_NAME);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void onPreferenceChange_unhibernatesWhenExempted() {
mController.setPackage(VALID_PACKAGE_NAME);
mController.onPreferenceChange(mPreference, false);
verify(mAppHibernationManager).setHibernatingForUser(VALID_PACKAGE_NAME, false);
verify(mAppHibernationManager).setHibernatingGlobally(VALID_PACKAGE_NAME, false);
}
@Test
public void isPackageHibernationExemptByUser_preSAppShouldBeExemptByDefault() {
when(mAppOpsManager.unsafeCheckOpNoThrow(
eq(OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED), anyInt(), eq(EXEMPTED_PACKAGE_NAME)))
.thenReturn(MODE_DEFAULT);
mController.setPackage(EXEMPTED_PACKAGE_NAME);
assertTrue(mController.isPackageHibernationExemptByUser());
}
@Test
public void isPackageHibernationExemptByUser_preSAppShouldNotBeExemptWithUserSetting() {
when(mAppOpsManager.unsafeCheckOpNoThrow(
eq(OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED), anyInt(), eq(EXEMPTED_PACKAGE_NAME)))
.thenReturn(MODE_ALLOWED);
mController.setPackage(EXEMPTED_PACKAGE_NAME);
assertFalse(mController.isPackageHibernationExemptByUser());
}
@Test
public void isPackageHibernationExemptByUser_SAppShouldBeExemptWithUserSetting() {
when(mAppOpsManager.unsafeCheckOpNoThrow(
eq(OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED), anyInt(), eq(UNEXEMPTED_PACKAGE_NAME)))
.thenReturn(MODE_IGNORED);
mController.setPackage(UNEXEMPTED_PACKAGE_NAME);
assertTrue(mController.isPackageHibernationExemptByUser());
}
@Test
public void isPackageHibernationExemptByUser_preSAppShouldNotBeExemptByDefaultWithPreSFlag() {
DeviceConfig.setProperty(NAMESPACE_APP_HIBERNATION, PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS,
"true", true /* makeDefault */);
when(mAppOpsManager.unsafeCheckOpNoThrow(
eq(OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED), anyInt(), eq(EXEMPTED_PACKAGE_NAME)))
.thenReturn(MODE_DEFAULT);
mController.setPackage(EXEMPTED_PACKAGE_NAME);
assertFalse(mController.isPackageHibernationExemptByUser());
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.assist;
import android.net.Uri;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.List;
@RunWith(AndroidJUnit4.class)
public class AssistSettingObserverTest {
@Test
public void callConstructor_shouldNotCrash() {
new AssistSettingObserver() {
@Override
protected List<Uri> getSettingUris() {
return null;
}
@Override
public void onSettingChange() {
}
};
}
}

View File

@@ -0,0 +1 @@
include /src/com/android/settings/applications/autofill/OWNERS

View File

@@ -0,0 +1,164 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.autofill;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Looper;
import android.os.UserHandle;
import android.service.autofill.AutofillServiceInfo;
import androidx.lifecycle.Lifecycle;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.test.annotation.UiThreadTest;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.collect.Lists;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import java.util.Collections;
import java.util.List;
@RunWith(AndroidJUnit4.class)
public class PasswordsPreferenceControllerTest {
private Context mContext;
private PreferenceScreen mScreen;
private PreferenceCategory mPasswordsPreferenceCategory;
@Before
public void setUp() {
mContext = spy(ApplicationProvider.getApplicationContext());
if (Looper.myLooper() == null) {
Looper.prepare(); // needed to create the preference screen
}
mScreen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
mPasswordsPreferenceCategory = new PreferenceCategory(mContext);
mPasswordsPreferenceCategory.setKey("passwords");
mScreen.addPreference(mPasswordsPreferenceCategory);
}
@Test
// Tests that getAvailabilityStatus() does not throw an exception if it's called before the
// Controller is initialized (this can happen during indexing).
public void getAvailabilityStatus_withoutInit_returnsUnavailable() {
PasswordsPreferenceController controller =
new PasswordsPreferenceController(mContext, mPasswordsPreferenceCategory.getKey());
assertThat(controller.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
@Test
public void getAvailabilityStatus_noServices_returnsUnavailable() {
PasswordsPreferenceController controller =
createControllerWithServices(Collections.emptyList());
assertThat(controller.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
@Test
public void getAvailabilityStatus_noPasswords_returnsUnavailable() {
AutofillServiceInfo service = new AutofillServiceInfo.TestDataBuilder().build();
PasswordsPreferenceController controller =
createControllerWithServices(Lists.newArrayList(service));
assertThat(controller.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
@Test
public void getAvailabilityStatus_withPasswords_returnsAvailable() {
PasswordsPreferenceController controller =
createControllerWithServices(Lists.newArrayList(createServiceWithPasswords()));
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void displayPreference_noServices_noPreferencesAdded() {
PasswordsPreferenceController controller =
createControllerWithServices(Collections.emptyList());
controller.displayPreference(mScreen);
assertThat(mPasswordsPreferenceCategory.getPreferenceCount()).isEqualTo(0);
}
@Test
public void displayPreference_noPasswords_noPreferencesAdded() {
AutofillServiceInfo service = new AutofillServiceInfo.TestDataBuilder().build();
PasswordsPreferenceController controller =
createControllerWithServices(Lists.newArrayList(service));
controller.displayPreference(mScreen);
assertThat(mPasswordsPreferenceCategory.getPreferenceCount()).isEqualTo(0);
}
@Test
@UiThreadTest
public void displayPreference_withPasswords_addsPreference() {
AutofillServiceInfo service = createServiceWithPasswords();
service.getServiceInfo().packageName = "";
service.getServiceInfo().name = "";
PasswordsPreferenceController controller =
createControllerWithServices(Lists.newArrayList(service));
doReturn(false).when(mContext).bindServiceAsUser(any(), any(), anyInt(), any());
controller.displayPreference(mScreen);
assertThat(mPasswordsPreferenceCategory.getPreferenceCount()).isEqualTo(1);
Preference pref = mPasswordsPreferenceCategory.getPreference(0);
assertThat(pref.getIcon()).isNotNull();
pref.performClick();
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
UserHandle user = mContext.getUser();
verify(mContext).startActivityAsUser(intentCaptor.capture(), eq(user));
assertThat(intentCaptor.getValue().getComponent())
.isEqualTo(
new ComponentName(
service.getServiceInfo().packageName,
service.getPasswordsActivity()));
}
private PasswordsPreferenceController createControllerWithServices(
List<AutofillServiceInfo> availableServices) {
PasswordsPreferenceController controller =
new PasswordsPreferenceController(mContext, mPasswordsPreferenceCategory.getKey());
controller.init(() -> mock(Lifecycle.class), availableServices);
return controller;
}
private AutofillServiceInfo createServiceWithPasswords() {
return new AutofillServiceInfo.TestDataBuilder()
.setPasswordsActivity("com.android.test.Passwords")
.build();
}
}

View File

@@ -0,0 +1,643 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.credentials;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.credentials.CredentialProviderInfo;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Looper;
import android.provider.Settings;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.settings.tests.unit.R;
import com.google.android.collect.Lists;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@RunWith(AndroidJUnit4.class)
public class CredentialManagerPreferenceControllerTest {
private Context mContext;
private PreferenceScreen mScreen;
private PreferenceCategory mCredentialsPreferenceCategory;
private CredentialManagerPreferenceController.Delegate mDelegate;
private static final String TEST_PACKAGE_NAME_A = "com.android.providerA";
private static final String TEST_PACKAGE_NAME_B = "com.android.providerB";
private static final String TEST_PACKAGE_NAME_C = "com.android.providerC";
private static final String TEST_TITLE_APP_A = "test app A";
private static final String TEST_TITLE_APP_B = "test app B";
private static final String TEST_TITLE_APP_C = "test app C1";
private static final String PRIMARY_INTENT = "android.settings.CREDENTIAL_PROVIDER";
private static final String ALTERNATE_INTENT = "android.settings.SYNC_SETTINGS";
@Before
public void setUp() {
mContext = spy(ApplicationProvider.getApplicationContext());
if (Looper.myLooper() == null) {
Looper.prepare(); // needed to create the preference screen
}
mScreen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
mCredentialsPreferenceCategory = new PreferenceCategory(mContext);
mCredentialsPreferenceCategory.setKey("credentials_test");
mScreen.addPreference(mCredentialsPreferenceCategory);
mDelegate =
new CredentialManagerPreferenceController.Delegate() {
public void setActivityResult(int resultCode) {}
public void forceDelegateRefresh() {}
};
}
@Test
// Tests that getAvailabilityStatus() does not throw an exception if it's called before the
// Controller is initialized (this can happen during indexing).
public void getAvailabilityStatus_withoutInit_returnsUnavailable() {
if (Looper.myLooper() == null) {
Looper.prepare(); // needed to create the preference screen
}
CredentialManagerPreferenceController controller =
new CredentialManagerPreferenceController(
mContext, mCredentialsPreferenceCategory.getKey());
assertThat(controller.isConnected()).isFalse();
assertThat(controller.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_noServices_returnsUnavailable() {
CredentialManagerPreferenceController controller =
createControllerWithServices(Collections.emptyList());
assertThat(controller.isConnected()).isFalse();
assertThat(controller.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_withServices_returnsAvailable() {
CredentialManagerPreferenceController controller =
createControllerWithServices(Lists.newArrayList(createCredentialProviderInfo()));
controller.setSimulateConnectedForTests(true);
assertThat(controller.isConnected()).isTrue();
controller.setSimulateHiddenForTests(Optional.of(false));
assertThat(controller.isHiddenDueToNoProviderSet()).isFalse();
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void getAvailabilityStatus_isHidden_returnsConditionallyUnavailable() {
CredentialManagerPreferenceController controller =
createControllerWithServices(Lists.newArrayList(createCredentialProviderInfo()));
controller.setSimulateConnectedForTests(true);
assertThat(controller.isConnected()).isTrue();
controller.setSimulateHiddenForTests(Optional.of(true));
assertThat(controller.isHiddenDueToNoProviderSet()).isTrue();
assertThat(controller.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
@Test
public void displayPreference_withServices_preferencesAdded() {
CredentialManagerPreferenceController controller =
createControllerWithServices(Lists.newArrayList(createCredentialProviderInfo()));
controller.setSimulateConnectedForTests(true);
controller.setSimulateHiddenForTests(Optional.of(false));
assertThat(controller.isHiddenDueToNoProviderSet()).isFalse();
assertThat(controller.isConnected()).isTrue();
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
controller.displayPreference(mScreen);
assertThat(mCredentialsPreferenceCategory.getPreferenceCount()).isEqualTo(1);
Preference pref = mCredentialsPreferenceCategory.getPreference(0);
assertThat(pref.getTitle()).isNotEqualTo("Add account");
}
@Test
public void buildPreference() {
CredentialProviderInfo providerInfo1 =
createCredentialProviderInfoWithSubtitle(
"com.android.provider1", "ClassA", "Service Title", null);
CredentialProviderInfo providerInfo2 =
createCredentialProviderInfoWithSubtitle(
"com.android.provider2", "ClassA", "Service Title", "Summary Text");
CredentialManagerPreferenceController controller =
createControllerWithServices(Lists.newArrayList(providerInfo1, providerInfo2));
controller.setSimulateConnectedForTests(true);
assertThat(controller.isConnected()).isTrue();
controller.setSimulateHiddenForTests(Optional.of(false));
assertThat(controller.isHiddenDueToNoProviderSet()).isFalse();
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
// Test the data is correct.
assertThat(providerInfo1.isEnabled()).isFalse();
assertThat(providerInfo2.isEnabled()).isFalse();
assertThat(controller.getEnabledProviders().size()).isEqualTo(0);
// Toggle one provider and make sure it worked.
assertThat(controller.togglePackageNameEnabled("com.android.provider1")).isTrue();
Set<String> enabledProviders = controller.getEnabledProviders();
assertThat(enabledProviders.size()).isEqualTo(1);
assertThat(enabledProviders.contains("com.android.provider1")).isTrue();
// Create the pref (checked).
CredentialManagerPreferenceController.CombiPreference pref =
controller.createPreference(mContext, providerInfo1);
assertThat(pref.getTitle().toString()).isEqualTo("Service Title");
assertThat(pref.isChecked()).isTrue();
assertThat(pref.getSummary()).isNull();
// Create the pref (not checked).
CredentialManagerPreferenceController.CombiPreference pref2 =
controller.createPreference(mContext, providerInfo2);
assertThat(pref2.getTitle().toString()).isEqualTo("Service Title");
assertThat(pref2.isChecked()).isFalse();
assertThat(pref2.getSummary().toString()).isEqualTo("Summary Text");
}
@Test
public void getAvailabilityStatus_handlesToggleAndSave() {
CredentialManagerPreferenceController controller =
createControllerWithServices(
Lists.newArrayList(
createCredentialProviderInfo("com.android.provider1", "ClassA"),
createCredentialProviderInfo("com.android.provider1", "ClassB"),
createCredentialProviderInfo("com.android.provider2", "ClassA"),
createCredentialProviderInfo("com.android.provider3", "ClassA"),
createCredentialProviderInfo("com.android.provider4", "ClassA"),
createCredentialProviderInfo("com.android.provider5", "ClassA"),
createCredentialProviderInfo("com.android.provider6", "ClassA")));
controller.setSimulateConnectedForTests(true);
assertThat(controller.isConnected()).isTrue();
controller.setSimulateHiddenForTests(Optional.of(false));
assertThat(controller.isHiddenDueToNoProviderSet()).isFalse();
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
// Ensure that we stay under 5 providers (one is reserved for primary).
assertThat(controller.togglePackageNameEnabled("com.android.provider1")).isTrue();
assertThat(controller.togglePackageNameEnabled("com.android.provider2")).isTrue();
assertThat(controller.togglePackageNameEnabled("com.android.provider3")).isTrue();
assertThat(controller.togglePackageNameEnabled("com.android.provider4")).isTrue();
assertThat(controller.togglePackageNameEnabled("com.android.provider5")).isFalse();
assertThat(controller.togglePackageNameEnabled("com.android.provider6")).isFalse();
// Check that they are all actually registered.
Set<String> enabledProviders = controller.getEnabledProviders();
assertThat(enabledProviders.size()).isEqualTo(4);
assertThat(enabledProviders.contains("com.android.provider1")).isTrue();
assertThat(enabledProviders.contains("com.android.provider2")).isTrue();
assertThat(enabledProviders.contains("com.android.provider3")).isTrue();
assertThat(enabledProviders.contains("com.android.provider4")).isTrue();
assertThat(enabledProviders.contains("com.android.provider5")).isFalse();
assertThat(enabledProviders.contains("com.android.provider6")).isFalse();
// Check that the settings string has the right component names.
List<String> enabledServices = controller.getEnabledSettings();
assertThat(enabledServices.size()).isEqualTo(5);
assertThat(enabledServices.contains("com.android.provider1/ClassA")).isTrue();
assertThat(enabledServices.contains("com.android.provider1/ClassB")).isTrue();
assertThat(enabledServices.contains("com.android.provider2/ClassA")).isTrue();
assertThat(enabledServices.contains("com.android.provider3/ClassA")).isTrue();
assertThat(enabledServices.contains("com.android.provider4/ClassA")).isTrue();
assertThat(enabledServices.contains("com.android.provider5/ClassA")).isFalse();
assertThat(enabledServices.contains("com.android.provider6/ClassA")).isFalse();
// Toggle the provider disabled.
controller.togglePackageNameDisabled("com.android.provider2");
// Check that the provider was removed from the list of providers.
Set<String> currentlyEnabledProviders = controller.getEnabledProviders();
assertThat(currentlyEnabledProviders.size()).isEqualTo(3);
assertThat(currentlyEnabledProviders.contains("com.android.provider1")).isTrue();
assertThat(currentlyEnabledProviders.contains("com.android.provider2")).isFalse();
assertThat(currentlyEnabledProviders.contains("com.android.provider3")).isTrue();
assertThat(currentlyEnabledProviders.contains("com.android.provider4")).isTrue();
assertThat(currentlyEnabledProviders.contains("com.android.provider5")).isFalse();
assertThat(currentlyEnabledProviders.contains("com.android.provider6")).isFalse();
// Check that the provider was removed from the list of services stored in the setting.
List<String> currentlyEnabledServices = controller.getEnabledSettings();
assertThat(currentlyEnabledServices.size()).isEqualTo(4);
assertThat(currentlyEnabledServices.contains("com.android.provider1/ClassA")).isTrue();
assertThat(currentlyEnabledServices.contains("com.android.provider1/ClassB")).isTrue();
assertThat(currentlyEnabledServices.contains("com.android.provider3/ClassA")).isTrue();
assertThat(currentlyEnabledServices.contains("com.android.provider4/ClassA")).isTrue();
assertThat(currentlyEnabledServices.contains("com.android.provider5/ClassA")).isFalse();
assertThat(currentlyEnabledServices.contains("com.android.provider6/ClassA")).isFalse();
}
@Test
public void handlesCredentialProviderInfoEnabledDisabled() {
CredentialProviderInfo providerInfo1 =
createCredentialProviderInfoWithIsEnabled(
"com.android.provider1", "ClassA", "Service Title", false);
CredentialProviderInfo providerInfo2 =
createCredentialProviderInfoWithIsEnabled(
"com.android.provider2", "ClassA", "Service Title", true);
CredentialManagerPreferenceController controller =
createControllerWithServices(Lists.newArrayList(providerInfo1, providerInfo2));
controller.setSimulateConnectedForTests(true);
assertThat(controller.isConnected()).isTrue();
controller.setSimulateHiddenForTests(Optional.of(false));
assertThat(controller.isHiddenDueToNoProviderSet()).isFalse();
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
// Test the data is correct.
assertThat(providerInfo1.isEnabled()).isFalse();
assertThat(providerInfo2.isEnabled()).isTrue();
// Check that they are all actually registered.
Set<String> enabledProviders = controller.getEnabledProviders();
assertThat(enabledProviders.size()).isEqualTo(1);
assertThat(enabledProviders.contains("com.android.provider1")).isFalse();
assertThat(enabledProviders.contains("com.android.provider2")).isTrue();
// Check that the settings string has the right component names.
List<String> enabledServices = controller.getEnabledSettings();
assertThat(enabledServices.size()).isEqualTo(1);
assertThat(enabledServices.contains("com.android.provider1/ClassA")).isFalse();
assertThat(enabledServices.contains("com.android.provider2/ClassA")).isTrue();
}
@Test
public void displayPreference_withServices_preferencesAdded_sameAppShouldBeMerged() {
CredentialProviderInfo serviceA1 =
createCredentialProviderInfoWithAppLabel(
TEST_PACKAGE_NAME_A,
"CredManProviderA1",
TEST_TITLE_APP_A,
"test service A1");
CredentialProviderInfo serviceB1 =
createCredentialProviderInfoWithAppLabel(
TEST_PACKAGE_NAME_B,
"CredManProviderB1",
TEST_TITLE_APP_B,
"test service B");
CredentialProviderInfo serviceC1 =
createCredentialProviderInfoWithAppLabel(
TEST_PACKAGE_NAME_C, "CredManProviderC1", "test app C1", TEST_TITLE_APP_C);
CredentialProviderInfo serviceC2 =
createCredentialProviderInfoWithAppLabel(
TEST_PACKAGE_NAME_C, "CredManProviderC2", "test app C2", TEST_TITLE_APP_C);
CredentialProviderInfo serviceC3 =
createCredentialProviderInfoBuilder(
TEST_PACKAGE_NAME_C,
"CredManProviderC3",
"test app C3",
TEST_TITLE_APP_C)
.setEnabled(true)
.build();
CredentialManagerPreferenceController controller =
createControllerWithServices(
Lists.newArrayList(serviceA1, serviceB1, serviceC1, serviceC2, serviceC3));
controller.setSimulateConnectedForTests(true);
controller.setSimulateHiddenForTests(Optional.of(false));
assertThat(controller.isHiddenDueToNoProviderSet()).isFalse();
assertThat(controller.isConnected()).isTrue();
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
controller.displayPreference(mScreen);
assertThat(mCredentialsPreferenceCategory.getPreferenceCount()).isEqualTo(3);
Map<String, CredentialManagerPreferenceController.CombiPreference> prefs =
controller.buildPreferenceList(mContext, mCredentialsPreferenceCategory);
assertThat(prefs.keySet())
.containsExactly(TEST_PACKAGE_NAME_A, TEST_PACKAGE_NAME_B, TEST_PACKAGE_NAME_C);
assertThat(prefs.size()).isEqualTo(3);
assertThat(prefs.containsKey(TEST_PACKAGE_NAME_A)).isTrue();
assertThat(prefs.get(TEST_PACKAGE_NAME_A).getTitle()).isEqualTo(TEST_TITLE_APP_A);
assertThat(prefs.get(TEST_PACKAGE_NAME_A).isChecked()).isFalse();
assertThat(prefs.containsKey(TEST_PACKAGE_NAME_B)).isTrue();
assertThat(prefs.get(TEST_PACKAGE_NAME_B).getTitle()).isEqualTo(TEST_TITLE_APP_B);
assertThat(prefs.get(TEST_PACKAGE_NAME_B).isChecked()).isFalse();
assertThat(prefs.containsKey(TEST_PACKAGE_NAME_C)).isTrue();
assertThat(prefs.get(TEST_PACKAGE_NAME_C).getTitle()).isEqualTo(TEST_TITLE_APP_C);
assertThat(prefs.get(TEST_PACKAGE_NAME_C).isChecked()).isTrue();
}
@Test
public void handleIntentWithProviderServiceInfo_handleBadIntent_missingData() {
CredentialProviderInfo cpi = createCredentialProviderInfo();
CredentialManagerPreferenceController controller =
createControllerWithServices(Lists.newArrayList(cpi));
// Create an intent with missing data.
Intent missingDataIntent = new Intent(PRIMARY_INTENT);
assertThat(controller.verifyReceivedIntent(missingDataIntent)).isFalse();
}
@Test
public void handleIntentWithProviderServiceInfo_handleBadIntent_successDialog() {
CredentialProviderInfo cpi = createCredentialProviderInfo();
CredentialManagerPreferenceController controller =
createControllerWithServices(Lists.newArrayList(cpi));
String packageName = cpi.getServiceInfo().packageName;
// Create an intent with valid data.
Intent intent = new Intent(PRIMARY_INTENT);
intent.setData(Uri.parse("package:" + packageName));
assertThat(controller.verifyReceivedIntent(intent)).isTrue();
int resultCode =
controller.completeEnableProviderDialogBox(
DialogInterface.BUTTON_POSITIVE, packageName, true);
assertThat(resultCode).isEqualTo(Activity.RESULT_OK);
}
@Test
public void handleIntentWithProviderServiceInfo_handleIntent_cancelDialog() {
CredentialProviderInfo cpi = createCredentialProviderInfo();
CredentialManagerPreferenceController controller =
createControllerWithServices(Lists.newArrayList(cpi));
String packageName = cpi.getServiceInfo().packageName;
// Create an intent with valid data.
Intent intent = new Intent(PRIMARY_INTENT);
intent.setData(Uri.parse("package:" + packageName));
assertThat(controller.verifyReceivedIntent(intent)).isTrue();
int resultCode =
controller.completeEnableProviderDialogBox(
DialogInterface.BUTTON_NEGATIVE, packageName, true);
assertThat(resultCode).isEqualTo(Activity.RESULT_CANCELED);
}
@Test
public void handleOtherIntentWithProviderServiceInfo_handleBadIntent_missingData() {
CredentialProviderInfo cpi = createCredentialProviderInfo();
CredentialManagerPreferenceController controller =
createControllerWithServices(Lists.newArrayList(cpi));
// Create an intent with missing data.
Intent missingDataIntent = new Intent(ALTERNATE_INTENT);
assertThat(controller.verifyReceivedIntent(missingDataIntent)).isFalse();
}
@Test
public void handleOtherIntentWithProviderServiceInfo_handleBadIntent_successDialog() {
CredentialProviderInfo cpi = createCredentialProviderInfo();
CredentialManagerPreferenceController controller =
createControllerWithServices(Lists.newArrayList(cpi));
String packageName = cpi.getServiceInfo().packageName;
// Create an intent with valid data.
Intent intent = new Intent(ALTERNATE_INTENT);
intent.setData(Uri.parse("package:" + packageName));
assertThat(controller.verifyReceivedIntent(intent)).isTrue();
int resultCode =
controller.completeEnableProviderDialogBox(
DialogInterface.BUTTON_POSITIVE, packageName, true);
assertThat(resultCode).isEqualTo(Activity.RESULT_OK);
}
@Test
public void handleOtherIntentWithProviderServiceInfo_handleIntent_cancelDialog() {
CredentialProviderInfo cpi = createCredentialProviderInfo();
CredentialManagerPreferenceController controller =
createControllerWithServices(Lists.newArrayList(cpi));
String packageName = cpi.getServiceInfo().packageName;
// Create an intent with valid data.
Intent intent = new Intent(ALTERNATE_INTENT);
intent.setData(Uri.parse("package:" + packageName));
assertThat(controller.verifyReceivedIntent(intent)).isTrue();
int resultCode =
controller.completeEnableProviderDialogBox(
DialogInterface.BUTTON_NEGATIVE, packageName, true);
assertThat(resultCode).isEqualTo(Activity.RESULT_CANCELED);
}
@Test
public void handleIntentWithProviderServiceInfo_handleIntent_incorrectAction() {
CredentialProviderInfo cpi = createCredentialProviderInfo();
CredentialManagerPreferenceController controller =
createControllerWithServices(Lists.newArrayList(cpi));
String packageName = cpi.getServiceInfo().packageName;
// Create an intent with valid data.
Intent intent = new Intent(Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE);
intent.setData(Uri.parse("package:" + packageName));
assertThat(controller.verifyReceivedIntent(intent)).isFalse();
}
@Test
public void handleIntentWithProviderServiceInfo_handleNullIntent() {
CredentialProviderInfo cpi = createCredentialProviderInfo();
CredentialManagerPreferenceController controller =
createControllerWithServices(Lists.newArrayList(cpi));
// Use a null intent.
assertThat(controller.verifyReceivedIntent(null)).isFalse();
}
@Test
public void testIconResizer_resizeLargeImage() throws Throwable {
CredentialProviderInfo cpi = createCredentialProviderInfo();
CredentialManagerPreferenceController controller =
createControllerWithServices(Lists.newArrayList(cpi));
final Drawable d =
InstrumentationRegistry.getInstrumentation()
.getContext()
.getResources()
.getDrawable(R.drawable.credman_icon_32_32);
assertThat(d).isNotNull();
assertThat(d.getIntrinsicHeight() >= 0).isTrue();
assertThat(d.getIntrinsicWidth() >= 0).isTrue();
Drawable thumbnail = controller.processIcon(d);
assertThat(thumbnail).isNotNull();
assertThat(thumbnail.getIntrinsicHeight()).isEqualTo(getIconSize());
assertThat(thumbnail.getIntrinsicWidth()).isEqualTo(getIconSize());
}
@Test
public void testIconResizer_resizeNullImage() throws Throwable {
CredentialProviderInfo cpi = createCredentialProviderInfo();
CredentialManagerPreferenceController controller =
createControllerWithServices(Lists.newArrayList(cpi));
Drawable thumbnail = controller.processIcon(null);
assertThat(thumbnail).isNotNull();
assertThat(thumbnail.getIntrinsicHeight()).isEqualTo(getIconSize());
assertThat(thumbnail.getIntrinsicWidth()).isEqualTo(getIconSize());
}
@Test
public void testIconResizer_resizeSmallImage() throws Throwable {
CredentialProviderInfo cpi = createCredentialProviderInfo();
CredentialManagerPreferenceController controller =
createControllerWithServices(Lists.newArrayList(cpi));
final Drawable d =
InstrumentationRegistry.getInstrumentation()
.getContext()
.getResources()
.getDrawable(R.drawable.credman_icon_1_1);
assertThat(d).isNotNull();
assertThat(d.getIntrinsicHeight() >= 0).isTrue();
assertThat(d.getIntrinsicWidth() >= 0).isTrue();
Drawable thumbnail = controller.processIcon(null);
assertThat(thumbnail).isNotNull();
assertThat(thumbnail.getIntrinsicHeight()).isEqualTo(getIconSize());
assertThat(thumbnail.getIntrinsicWidth()).isEqualTo(getIconSize());
}
@Test
public void hasNonPrimaryServices_allServicesArePrimary() {
CredentialManagerPreferenceController controller =
createControllerWithServices(
Lists.newArrayList(createCredentialProviderPrimary()));
assertThat(controller.hasNonPrimaryServices()).isFalse();
}
@Test
public void hasNonPrimaryServices_mixtureOfServices() {
CredentialManagerPreferenceController controller =
createControllerWithServices(
Lists.newArrayList(createCredentialProviderInfo(),
createCredentialProviderPrimary()));
assertThat(controller.hasNonPrimaryServices()).isTrue();
}
@Test
public void testProviderLimitReached() {
// The limit is 5 with one slot reserved for primary.
assertThat(CredentialManagerPreferenceController.hasProviderLimitBeenReached(0)).isFalse();
assertThat(CredentialManagerPreferenceController.hasProviderLimitBeenReached(1)).isFalse();
assertThat(CredentialManagerPreferenceController.hasProviderLimitBeenReached(2)).isFalse();
assertThat(CredentialManagerPreferenceController.hasProviderLimitBeenReached(3)).isFalse();
assertThat(CredentialManagerPreferenceController.hasProviderLimitBeenReached(4)).isTrue();
assertThat(CredentialManagerPreferenceController.hasProviderLimitBeenReached(5)).isTrue();
}
private int getIconSize() {
final Resources resources = mContext.getResources();
return (int) resources.getDimension(android.R.dimen.app_icon_size);
}
private CredentialManagerPreferenceController createControllerWithServices(
List<CredentialProviderInfo> availableServices) {
return createControllerWithServicesAndAddServiceOverride(availableServices, null);
}
private CredentialManagerPreferenceController createControllerWithServicesAndAddServiceOverride(
List<CredentialProviderInfo> availableServices, String addServiceOverride) {
if (Looper.myLooper() == null) {
Looper.prepare(); // needed to create the preference screen
}
CredentialManagerPreferenceController controller =
new CredentialManagerPreferenceController(
mContext, mCredentialsPreferenceCategory.getKey());
controller.setAvailableServices(availableServices, addServiceOverride);
controller.setDelegate(mDelegate);
return controller;
}
private CredentialProviderInfo createCredentialProviderInfo() {
return createCredentialProviderInfo("com.android.provider", "CredManProvider");
}
private CredentialProviderInfo createCredentialProviderInfo(
String packageName, String className) {
return createCredentialProviderInfoBuilder(packageName, className, null, "App Name")
.build();
}
private CredentialProviderInfo createCredentialProviderInfoWithIsEnabled(
String packageName, String className, CharSequence serviceLabel, boolean isEnabled) {
return createCredentialProviderInfoBuilder(packageName, className, serviceLabel, "App Name")
.setEnabled(isEnabled)
.build();
}
private CredentialProviderInfo createCredentialProviderPrimary() {
return createCredentialProviderInfoBuilder(
"com.android.primary", "CredManProvider", "Service Label", "App Name")
.setPrimary(true)
.build();
}
private CredentialProviderInfo createCredentialProviderInfoWithSubtitle(
String packageName, String className, CharSequence label, CharSequence subtitle) {
ServiceInfo si = new ServiceInfo();
si.packageName = packageName;
si.name = className;
si.nonLocalizedLabel = "test";
si.applicationInfo = new ApplicationInfo();
si.applicationInfo.packageName = packageName;
si.applicationInfo.nonLocalizedLabel = "test";
return new CredentialProviderInfo.Builder(si)
.setOverrideLabel(label)
.setSettingsSubtitle(subtitle)
.build();
}
private CredentialProviderInfo createCredentialProviderInfoWithAppLabel(
String packageName, String className, CharSequence serviceLabel, String appLabel) {
return createCredentialProviderInfoBuilder(packageName, className, serviceLabel, appLabel)
.build();
}
private CredentialProviderInfo.Builder createCredentialProviderInfoBuilder(
String packageName, String className, CharSequence serviceLabel, String appLabel) {
ServiceInfo si = new ServiceInfo();
si.packageName = packageName;
si.name = className;
si.nonLocalizedLabel = serviceLabel;
si.applicationInfo = new ApplicationInfo();
si.applicationInfo.packageName = packageName;
si.applicationInfo.nonLocalizedLabel = appLabel;
return new CredentialProviderInfo.Builder(si).setOverrideLabel(serviceLabel);
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.credentials;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.Intent;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.os.UserManager;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.collect.Lists;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class CredentialsPickerActivityTest {
@Mock private UserManager mUserManager;
private Context mMockContext;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mMockContext = spy(ApplicationProvider.getApplicationContext());
when(mMockContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
}
@Test
public void testInjectFragmentIntoIntent_normalProfile() {
Intent intent = new Intent();
CredentialsPickerActivity.injectFragmentIntoIntent(mMockContext, intent);
assertThat(intent.getStringExtra(CredentialsPickerActivity.EXTRA_SHOW_FRAGMENT))
.isEqualTo(DefaultCombinedPicker.class.getName());
}
@Test
public void testInjectFragmentIntoIntent_workProfile() {
Intent intent = new Intent();
// Simulate managed / work profile.
when(mUserManager.isManagedProfile(anyInt())).thenReturn(true);
assertThat(DefaultCombinedPickerWork.isUserHandledByFragment(mUserManager, 10)).isTrue();
CredentialsPickerActivity.injectFragmentIntoIntent(mMockContext, intent);
assertThat(intent.getStringExtra(CredentialsPickerActivity.EXTRA_SHOW_FRAGMENT))
.isEqualTo(DefaultCombinedPickerWork.class.getName());
}
@Test
public void testInjectFragmentIntoIntent_privateProfile() {
Intent intent = new Intent();
// Simulate private profile.
UserHandle privateUser = new UserHandle(100);
when(mUserManager.getUserInfo(100))
.thenReturn(new UserInfo(100, "", "", 0, UserManager.USER_TYPE_PROFILE_PRIVATE));
when(mUserManager.getUserProfiles()).thenReturn(Lists.newArrayList(privateUser));
assertThat(DefaultCombinedPickerPrivate.isUserHandledByFragment(mUserManager)).isTrue();
CredentialsPickerActivity.injectFragmentIntoIntent(mMockContext, intent);
assertThat(intent.getStringExtra(CredentialsPickerActivity.EXTRA_SHOW_FRAGMENT))
.isEqualTo(DefaultCombinedPickerPrivate.class.getName());
}
}

View File

@@ -0,0 +1,142 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.credentials;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Looper;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import androidx.preference.PreferenceViewHolder;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.R;
import com.android.settings.testutils.ResourcesUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class DefaultCombinedPreferenceControllerTest {
private Context mContext;
private PrimaryProviderPreference.Delegate mDelegate;
private AttributeSet mAttributes;
@Before
public void setUp() {
mContext = spy(ApplicationProvider.getApplicationContext());
if (Looper.myLooper() == null) {
Looper.prepare(); // needed to create the preference screen
}
mDelegate =
new PrimaryProviderPreference.Delegate() {
public void onOpenButtonClicked() {}
public void onChangeButtonClicked() {}
};
}
@Test
public void ensureSettingIntentNullForNewDesign() {
if (!PrimaryProviderPreference.shouldUseNewSettingsUi()) {
return;
}
// The setting intent should be null for the new design since this
// is handled by the delegate for the PrimaryProviderPreference.
DefaultCombinedPreferenceController dcpc =
new DefaultCombinedPreferenceController(mContext);
assertThat(dcpc.getSettingIntent(null).getPackage()).isNull();
}
@Test
public void ensureSettingIntentNotNullForOldDesign() {
if (PrimaryProviderPreference.shouldUseNewSettingsUi()) {
return;
}
// For the old design the setting intent should still be used.
DefaultCombinedPreferenceController dcpc =
new DefaultCombinedPreferenceController(mContext);
assertThat(dcpc.getSettingIntent(null).getPackage()).isNotNull();
}
@Test
public void ensureSettingsActivityIntentCreatedSuccessfully() {
// Ensure that the settings activity is only created if we haved the right combination
// of package and class name.
assertThat(CombinedProviderInfo.createSettingsActivityIntent(null, null)).isNull();
assertThat(CombinedProviderInfo.createSettingsActivityIntent("", null)).isNull();
assertThat(CombinedProviderInfo.createSettingsActivityIntent("", "")).isNull();
assertThat(CombinedProviderInfo.createSettingsActivityIntent("com.test", "")).isNull();
assertThat(CombinedProviderInfo.createSettingsActivityIntent("com.test", "ClassName"))
.isNotNull();
}
@Test
public void ensureUpdatePreferenceForProviderPopulatesInfo() {
if (!PrimaryProviderPreference.shouldUseNewSettingsUi()) {
return;
}
DefaultCombinedPreferenceController dcpc =
new DefaultCombinedPreferenceController(mContext);
PrimaryProviderPreference ppp = createTestPreference();
Drawable appIcon = mContext.getResources().getDrawable(R.drawable.ic_settings_delete);
// Update the preference to use the provider and make sure the view
// was updated.
dcpc.updatePreferenceForProvider(ppp, "App Name", "Subtitle", appIcon, null, null);
assertThat(ppp.getTitle().toString()).isEqualTo("App Name");
assertThat(ppp.getSummary().toString()).isEqualTo("Subtitle");
assertThat(ppp.getIcon()).isEqualTo(appIcon);
// Set the preference back to none and make sure the view was updated.
dcpc.updatePreferenceForProvider(ppp, null, null, null, null, null);
assertThat(ppp.getTitle().toString()).isEqualTo("None");
assertThat(ppp.getSummary()).isNull();
assertThat(ppp.getIcon()).isNull();
}
private PrimaryProviderPreference createTestPreference() {
int layoutId =
ResourcesUtils.getResourcesId(
mContext, "layout", "preference_credential_manager_with_buttons");
PreferenceViewHolder holder =
PreferenceViewHolder.createInstanceForTests(
LayoutInflater.from(mContext).inflate(layoutId, null));
PreferenceViewHolder holderForTest = spy(holder);
View gearView = new View(mContext, null);
int gearId = ResourcesUtils.getResourcesId(mContext, "id", "settings_button");
when(holderForTest.findViewById(gearId)).thenReturn(gearView);
PrimaryProviderPreference ppp = new PrimaryProviderPreference(mContext, mAttributes);
ppp.setDelegate(mDelegate);
ppp.onBindViewHolder(holderForTest);
return ppp;
}
}

View File

@@ -0,0 +1 @@
include /src/com/android/settings/applications/credentials/OWNERS

View File

@@ -0,0 +1,153 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.credentials;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.Looper;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import androidx.preference.PreferenceViewHolder;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.testutils.ResourcesUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class PrimaryProviderPreferenceTest {
private Context mContext;
private PrimaryProviderPreference.Delegate mDelegate;
private boolean mReceivedOpenButtonClicked = false;
private boolean mReceivedChangeButtonClicked = false;
private AttributeSet mAttributes;
@Before
public void setUp() {
mContext = spy(ApplicationProvider.getApplicationContext());
if (Looper.myLooper() == null) {
Looper.prepare(); // needed to create the preference screen
}
mReceivedOpenButtonClicked = false;
mReceivedChangeButtonClicked = false;
mDelegate =
new PrimaryProviderPreference.Delegate() {
public void onOpenButtonClicked() {
mReceivedOpenButtonClicked = true;
}
public void onChangeButtonClicked() {
mReceivedChangeButtonClicked = true;
}
};
}
@Test
public void ensureButtonsClicksCallDelegate_newDesign() {
if (!PrimaryProviderPreference.shouldUseNewSettingsUi()) {
return;
}
PrimaryProviderPreference ppp = createTestPreferenceWithNewLayout();
// Test that all the views & buttons are bound correctly.
assertThat(ppp.getOpenButton()).isNotNull();
assertThat(ppp.getChangeButton()).isNotNull();
assertThat(ppp.getButtonFrameView()).isNotNull();
// Test that clicking the open button results in the delegate being
// called.
assertThat(mReceivedOpenButtonClicked).isFalse();
ppp.getOpenButton().performClick();
assertThat(mReceivedOpenButtonClicked).isTrue();
// Test that clicking the change button results in the delegate being
// called.
assertThat(mReceivedChangeButtonClicked).isFalse();
ppp.getChangeButton().performClick();
assertThat(mReceivedChangeButtonClicked).isTrue();
}
@Test
public void ensureButtonsClicksCallDelegate_newDesign_openButtonVisibility() {
if (!PrimaryProviderPreference.shouldUseNewSettingsUi()) {
return;
}
PrimaryProviderPreference ppp = createTestPreferenceWithNewLayout();
// Test that the open button is visible.
assertThat(ppp.getOpenButton()).isNotNull();
assertThat(ppp.getOpenButton().getVisibility()).isEqualTo(View.GONE);
// Show the button and make sure the view was updated.
ppp.setOpenButtonVisible(true);
assertThat(ppp.getOpenButton().getVisibility()).isEqualTo(View.VISIBLE);
// Hide the button and make sure the view was updated.
ppp.setOpenButtonVisible(false);
assertThat(ppp.getOpenButton().getVisibility()).isEqualTo(View.GONE);
}
@Test
public void ensureButtonsClicksCallDelegate_newDesign_buttonsCompactMode() {
if (!PrimaryProviderPreference.shouldUseNewSettingsUi()) {
return;
}
PrimaryProviderPreference ppp = createTestPreferenceWithNewLayout();
int initialPaddingLeft = ppp.getButtonFrameView().getPaddingLeft();
// If we show the buttons the left padding should be updated.
ppp.setButtonsCompactMode(true);
assertThat(ppp.getButtonFrameView().getPaddingLeft()).isNotEqualTo(initialPaddingLeft);
// If we hide the buttons the left padding should be updated.
ppp.setButtonsCompactMode(false);
assertThat(ppp.getButtonFrameView().getPaddingLeft()).isEqualTo(initialPaddingLeft);
}
private PrimaryProviderPreference createTestPreferenceWithNewLayout() {
return createTestPreference("preference_credential_manager_with_buttons");
}
private PrimaryProviderPreference createTestPreference(String layoutName) {
int layoutId = ResourcesUtils.getResourcesId(mContext, "layout", layoutName);
PreferenceViewHolder holder =
PreferenceViewHolder.createInstanceForTests(
LayoutInflater.from(mContext).inflate(layoutId, null));
PreferenceViewHolder holderForTest = spy(holder);
View gearView = new View(mContext, null);
int gearId = ResourcesUtils.getResourcesId(mContext, "id", "settings_button");
when(holderForTest.findViewById(gearId)).thenReturn(gearView);
PrimaryProviderPreference ppp = new PrimaryProviderPreference(mContext, mAttributes);
ppp.setDelegate(mDelegate);
ppp.onBindViewHolder(holderForTest);
return ppp;
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.manageapplications;
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_ALARMS_AND_REMINDERS;
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_ALL;
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_BATTERY_OPTIMIZED;
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_INSTALL_SOURCES;
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_MEDIA_MANAGEMENT;
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_POWER_ALLOWLIST;
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_RECENT;
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_TURN_SCREEN_ON;
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_USAGE_ACCESS;
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_WITH_OVERLAY;
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_WRITE_SETTINGS;
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_LONG_BACKGROUND_TASKS;
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_ALARMS_AND_REMINDERS;
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_BATTERY_OPTIMIZATION;
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_GAMES;
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_HIGH_POWER;
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_LONG_BACKGROUND_TASKS;
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_MAIN;
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_MANAGE_SOURCES;
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_MEDIA_MANAGEMENT_APPS;
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_NOTIFICATION;
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_OVERLAY;
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_STORAGE;
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_TURN_SCREEN_ON;
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_USAGE_ACCESS;
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_WRITE_SETTINGS;
import static com.google.common.truth.Truth.assertThat;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class AppFilterRegistryTest {
@Test
public void getDefaultType_shouldMatchForAllListType() {
final AppFilterRegistry registry = AppFilterRegistry.getInstance();
assertThat(registry.getDefaultFilterType(LIST_TYPE_USAGE_ACCESS))
.isEqualTo(FILTER_APPS_USAGE_ACCESS);
assertThat(registry.getDefaultFilterType(LIST_TYPE_HIGH_POWER))
.isEqualTo(FILTER_APPS_POWER_ALLOWLIST);
assertThat(registry.getDefaultFilterType(LIST_TYPE_OVERLAY))
.isEqualTo(FILTER_APPS_WITH_OVERLAY);
assertThat(registry.getDefaultFilterType(LIST_TYPE_WRITE_SETTINGS))
.isEqualTo(FILTER_APPS_WRITE_SETTINGS);
assertThat(registry.getDefaultFilterType(LIST_TYPE_MANAGE_SOURCES))
.isEqualTo(FILTER_APPS_INSTALL_SOURCES);
assertThat(registry.getDefaultFilterType(LIST_TYPE_ALARMS_AND_REMINDERS))
.isEqualTo(FILTER_ALARMS_AND_REMINDERS);
assertThat(registry.getDefaultFilterType(LIST_TYPE_MEDIA_MANAGEMENT_APPS))
.isEqualTo(FILTER_APPS_MEDIA_MANAGEMENT);
assertThat(registry.getDefaultFilterType(LIST_TYPE_BATTERY_OPTIMIZATION))
.isEqualTo(FILTER_APPS_BATTERY_OPTIMIZED);
assertThat(registry.getDefaultFilterType(LIST_TYPE_MAIN)).isEqualTo(FILTER_APPS_ALL);
assertThat(registry.getDefaultFilterType(LIST_TYPE_NOTIFICATION))
.isEqualTo(FILTER_APPS_RECENT);
assertThat(registry.getDefaultFilterType(LIST_TYPE_STORAGE)).isEqualTo(FILTER_APPS_ALL);
assertThat(registry.getDefaultFilterType(LIST_TYPE_GAMES)).isEqualTo(FILTER_APPS_ALL);
assertThat(registry.getDefaultFilterType(LIST_TYPE_LONG_BACKGROUND_TASKS))
.isEqualTo(FILTER_LONG_BACKGROUND_TASKS);
assertThat(registry.getDefaultFilterType(LIST_TYPE_TURN_SCREEN_ON)).isEqualTo(
FILTER_APPS_TURN_SCREEN_ON);
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.specialaccess.notificationaccess;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
import static com.google.common.truth.Truth.assertThat;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ServiceInfo;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.notification.NotificationBackend;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class AlertingTypeFilterPreferenceControllerTest {
private Context mContext;
private AlertingTypeFilterPreferenceController mController;
@Mock
NotificationBackend mNm;
ComponentName mCn = new ComponentName("a", "b");
ServiceInfo mSi = new ServiceInfo();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = ApplicationProvider.getApplicationContext();
mController = new AlertingTypeFilterPreferenceController(mContext, "key");
mController.setCn(mCn);
mController.setNm(mNm);
mController.setServiceInfo(mSi);
mController.setUserId(0);
}
@Test
public void getType() {
assertThat(mController.getType()).isEqualTo(FLAG_FILTER_TYPE_ALERTING);
}
}

View File

@@ -0,0 +1,201 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.specialaccess.notificationaccess;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.app.Flags;
import android.app.NotificationManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settingslib.RestrictedSwitchPreference;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class ApprovalPreferenceControllerTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(
SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
private Context mContext;
private FakeFeatureFactory mFeatureFactory;
@Mock
private NotificationAccessDetails mFragment;
private ApprovalPreferenceController mController;
@Mock
NotificationManager mNm;
@Mock
AppOpsManager mAppOpsManager;
@Mock
PackageManager mPm;
PackageInfo mPkgInfo;
ComponentName mCn = new ComponentName("a", "b");
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mFeatureFactory = FakeFeatureFactory.setupForTest();
mContext = spy(ApplicationProvider.getApplicationContext());
doReturn(mContext).when(mFragment).getContext();
mPkgInfo = new PackageInfo();
mPkgInfo.applicationInfo = mock(ApplicationInfo.class);
when(mPkgInfo.applicationInfo.loadLabel(mPm)).thenReturn("LABEL");
mController = new ApprovalPreferenceController(mContext, "key");
mController.setCn(mCn);
mController.setNm(mNm);
mController.setParent(mFragment);
mController.setPkgInfo(mPkgInfo);
}
@Test
public void updateState_enabled() {
when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn(
AppOpsManager.MODE_ALLOWED);
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(
mContext);
pref.setAppOps(mAppOpsManager);
mController.updateState(pref);
assertThat(pref.isEnabled()).isTrue();
}
@Test
public void updateState_invalidCn_disabled() {
ComponentName longCn = new ComponentName("com.example.package",
com.google.common.base.Strings.repeat("Blah", 150));
mController.setCn(longCn);
when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn(
AppOpsManager.MODE_ALLOWED);
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(
mContext);
pref.setAppOps(mAppOpsManager);
mController.updateState(pref);
assertThat(pref.isEnabled()).isFalse();
}
@Test
@RequiresFlagsDisabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS)
public void updateState_checked() {
when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn(
AppOpsManager.MODE_ALLOWED);
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(
mContext);
pref.setAppOps(mAppOpsManager);
mController.updateState(pref);
assertThat(pref.isChecked()).isTrue();
assertThat(pref.isEnabled()).isTrue();
}
@Test
@RequiresFlagsDisabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS)
public void restrictedSettings_appOpsDisabled() {
Assert.assertFalse(android.security.Flags.extendEcmToAllSettings());
when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString()))
.thenReturn(AppOpsManager.MODE_ERRORED);
doReturn(mAppOpsManager).when(mContext).getSystemService(Context.APP_OPS_SERVICE);
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(false);
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(
mContext);
pref.setAppOps(mAppOpsManager);
mController.setSettingIdentifier(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS);
mController.updateState(pref);
verify(mAppOpsManager).noteOpNoThrow(anyInt(), anyInt(), anyString());
assertThat(pref.isEnabled()).isFalse();
}
@Test
@RequiresFlagsDisabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS)
public void restrictedSettings_serviceAlreadyEnabled() {
when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn(
AppOpsManager.MODE_ERRORED);
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(
mContext);
pref.setAppOps(mAppOpsManager);
mController.updateState(pref);
assertThat(pref.isEnabled()).isTrue();
}
@Test
public void enable() {
mController.enable(mCn);
verify(mFeatureFactory.metricsFeatureProvider).action(
mContext,
MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_NOTIVIEW_ALLOW,
"a");
verify(mNm).setNotificationListenerAccessGranted(mCn, true);
}
@Test
@EnableFlags(Flags.FLAG_MODES_API)
public void disable() {
mController.disable(mCn);
verify(mFeatureFactory.metricsFeatureProvider).action(
mContext,
MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_NOTIVIEW_ALLOW,
"a");
verify(mNm).removeAutomaticZenRules(eq(mCn.getPackageName()), eq(true));
verify(mNm).setNotificationListenerAccessGranted(mCn, false);
}
}

View File

@@ -0,0 +1,122 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.specialaccess.notificationaccess;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
import android.content.Context;
import android.os.Build;
import android.service.notification.NotificationListenerFilter;
import androidx.preference.Preference;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.notification.NotificationBackend;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class BridgedAppsLinkPreferenceControllerTest {
private Context mContext;
private BridgedAppsLinkPreferenceController mController;
@Mock
NotificationBackend mNm;
ComponentName mCn = new ComponentName("a", "b");
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = ApplicationProvider.getApplicationContext();
mController = new BridgedAppsLinkPreferenceController(mContext, "key");
mController.setCn(mCn);
mController.setNm(mNm);
mController.setUserId(0);
}
@Test
public void testAvailable_notGranted() {
when(mNm.isNotificationListenerAccessGranted(any())).thenReturn(false);
mController.setTargetSdk(Build.VERSION_CODES.CUR_DEVELOPMENT + 1);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
// disables field
Preference p = new Preference(mContext);
mController.updateState(p);
assertThat(p.isEnabled()).isFalse();
}
@Test
public void testAvailable_lowTargetSdk_noCustomizations() {
when(mNm.isNotificationListenerAccessGranted(any())).thenReturn(true);
mController.setTargetSdk(Build.VERSION_CODES.S);
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
// disables field
Preference p = new Preference(mContext);
mController.updateState(p);
assertThat(p.isEnabled()).isFalse();
}
@Test
public void testAvailable_lowTargetSdk_customizations() {
when(mNm.isNotificationListenerAccessGranted(any())).thenReturn(true);
mController.setTargetSdk(Build.VERSION_CODES.S);
NotificationListenerFilter nlf = new NotificationListenerFilter();
nlf.setTypes(FLAG_FILTER_TYPE_CONVERSATIONS);
when(mNm.getListenerFilter(mCn, 0)).thenReturn(nlf);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
// enables field
Preference p = new Preference(mContext);
mController.updateState(p);
assertThat(p.isEnabled()).isTrue();
}
@Test
public void testAvailable_highTargetSdk_noCustomizations() {
when(mNm.isNotificationListenerAccessGranted(any())).thenReturn(true);
mController.setTargetSdk(Build.VERSION_CODES.CUR_DEVELOPMENT + 1);
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
// enables field
Preference p = new Preference(mContext);
mController.updateState(p);
assertThat(p.isEnabled()).isTrue();
}
}

View File

@@ -0,0 +1,219 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.specialaccess.notificationaccess;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.VersionedPackage;
import android.os.Looper;
import android.service.notification.NotificationListenerFilter;
import android.util.ArraySet;
import androidx.preference.Preference;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.notification.NotificationBackend;
import com.android.settings.widget.AppCheckBoxPreference;
import com.android.settingslib.applications.ApplicationsState;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
@RunWith(AndroidJUnit4.class)
public class BridgedAppsPreferenceControllerTest {
private Context mContext;
private BridgedAppsPreferenceController mController;
@Mock
NotificationBackend mNm;
ComponentName mCn = new ComponentName("a", "b");
PreferenceScreen mScreen;
@Mock
ApplicationsState mAppState;
private ApplicationsState.AppEntry mAppEntry;
private ApplicationsState.AppEntry mAppEntry2;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = ApplicationProvider.getApplicationContext();
if (Looper.myLooper() == null) {
Looper.prepare();
}
PreferenceManager preferenceManager = new PreferenceManager(mContext);
mScreen = preferenceManager.createPreferenceScreen(mContext);
ApplicationInfo ai = new ApplicationInfo();
ai.packageName = "pkg";
ai.uid = 12300;
ai.sourceDir = "";
ApplicationInfo ai2 = new ApplicationInfo();
ai2.packageName = "another";
ai2.uid = 18800;
ai2.sourceDir = "";
mAppEntry = new ApplicationsState.AppEntry(mContext, ai, 0);
mAppEntry2 = new ApplicationsState.AppEntry(mContext, ai2, 1);
mAppEntry.info = ai;
mAppEntry.label = "hi";
mController = new BridgedAppsPreferenceController(mContext, "key");
mController.setCn(mCn);
mController.setNm(mNm);
mController.setUserId(0);
mController.setAppState(mAppState);
mController.displayPreference(mScreen);
}
@Test
public void onRebuildComplete_AddsToScreen() {
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
ArrayList<ApplicationsState.AppEntry> entries = new ArrayList<>();
entries.add(mAppEntry);
entries.add(mAppEntry2);
mController.onRebuildComplete(entries);
assertThat(mScreen.getPreferenceCount()).isEqualTo(2);
}
@Test
public void onRebuildComplete_doesNotReaddToScreen() {
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
AppCheckBoxPreference p = mock(AppCheckBoxPreference.class);
when(p.getKey()).thenReturn("pkg|12300");
mScreen.addPreference(p);
ArrayList<ApplicationsState.AppEntry> entries = new ArrayList<>();
entries.add(mAppEntry);
entries.add(mAppEntry2);
mController.onRebuildComplete(entries);
assertThat(mScreen.getPreferenceCount()).isEqualTo(2);
}
@Test
public void onRebuildComplete_removesExtras() {
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
Preference p = mock(Preference.class);
when(p.getKey()).thenReturn("pkg|123");
mScreen.addPreference(p);
ArrayList<ApplicationsState.AppEntry> entries = new ArrayList<>();
entries.add(mAppEntry);
entries.add(mAppEntry2);
mController.onRebuildComplete(entries);
assertThat((Preference) mScreen.findPreference("pkg|123")).isNull();
}
@Test
public void onRebuildComplete_buildsSetting() {
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
ArrayList<ApplicationsState.AppEntry> entries = new ArrayList<>();
entries.add(mAppEntry);
mController.onRebuildComplete(entries);
AppCheckBoxPreference actual = mScreen.findPreference("pkg|12300");
assertThat(actual.isChecked()).isTrue();
assertThat(actual.getTitle()).isEqualTo("hi");
assertThat(actual.getIcon()).isNotNull();
}
@Test
public void onPreferenceChange_false() {
VersionedPackage vp = new VersionedPackage("pkg", 10567);
ArraySet<VersionedPackage> vps = new ArraySet<>();
vps.add(vp);
NotificationListenerFilter nlf = new NotificationListenerFilter(FLAG_FILTER_TYPE_ONGOING
| FLAG_FILTER_TYPE_CONVERSATIONS, vps);
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
when(mNm.getListenerFilter(mCn, 0)).thenReturn(nlf);
AppCheckBoxPreference pref = new AppCheckBoxPreference(mContext);
pref.setKey("pkg|567");
mController.onPreferenceChange(pref, false);
ArgumentCaptor<NotificationListenerFilter> captor =
ArgumentCaptor.forClass(NotificationListenerFilter.class);
verify(mNm).setListenerFilter(eq(mCn), eq(0), captor.capture());
assertThat(captor.getValue().getDisallowedPackages()).contains(
new VersionedPackage("pkg", 567));
assertThat(captor.getValue().getDisallowedPackages()).contains(
new VersionedPackage("pkg", 10567));
}
@Test
public void onPreferenceChange_true() {
VersionedPackage vp = new VersionedPackage("pkg", 567);
VersionedPackage vp2 = new VersionedPackage("pkg", 10567);
ArraySet<VersionedPackage> vps = new ArraySet<>();
vps.add(vp);
vps.add(vp2);
NotificationListenerFilter nlf = new NotificationListenerFilter(FLAG_FILTER_TYPE_ONGOING
| FLAG_FILTER_TYPE_CONVERSATIONS, vps);
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
when(mNm.getListenerFilter(mCn, 0)).thenReturn(nlf);
AppCheckBoxPreference pref = new AppCheckBoxPreference(mContext);
pref.setKey("pkg|567");
mController.onPreferenceChange(pref, true);
ArgumentCaptor<NotificationListenerFilter> captor =
ArgumentCaptor.forClass(NotificationListenerFilter.class);
verify(mNm).setListenerFilter(eq(mCn), eq(0), captor.capture());
assertThat(captor.getValue().getDisallowedPackages().size()).isEqualTo(1);
assertThat(captor.getValue().getDisallowedPackages()).contains(
new VersionedPackage("pkg", 10567));
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.specialaccess.notificationaccess;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
import static com.google.common.truth.Truth.assertThat;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ServiceInfo;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.notification.NotificationBackend;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class ConversationTypeFilterPreferenceControllerTest {
private Context mContext;
private ConversationTypeFilterPreferenceController mController;
@Mock
NotificationBackend mNm;
ComponentName mCn = new ComponentName("a", "b");
ServiceInfo mSi = new ServiceInfo();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = ApplicationProvider.getApplicationContext();
mController = new ConversationTypeFilterPreferenceController(mContext, "key");
mController.setCn(mCn);
mController.setNm(mNm);
mController.setServiceInfo(mSi);
mController.setUserId(0);
}
@Test
public void getType() {
assertThat(mController.getType()).isEqualTo(FLAG_FILTER_TYPE_CONVERSATIONS);
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.specialaccess.notificationaccess;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.service.notification.NotificationListenerService;
import androidx.preference.Preference;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.collect.ImmutableList;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class MoreSettingsPreferenceControllerTest {
Context mContext;
private MoreSettingsPreferenceController mController;
@Mock
PackageManager mPm;
final String mPkg = "pkg";
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = ApplicationProvider.getApplicationContext();
mController = new MoreSettingsPreferenceController(mContext);
mController.setPackage(mPkg);
mController.setPackageManager(mPm);
}
@Test
public void getAvailabilityStatus_available() {
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
when(mPm.queryIntentActivities(captor.capture(), any())).thenReturn(
ImmutableList.of(mock(ResolveInfo.class)));
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
assertThat(captor.getValue().getPackage()).isEqualTo(mPkg);
assertThat(captor.getValue().getAction()).contains(
NotificationListenerService.ACTION_SETTINGS_HOME);
}
@Test
public void getAvailabilityStatus_notAvailable() {
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
when(mPm.queryIntentActivities(captor.capture(), any())).thenReturn(ImmutableList.of());
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
@Test
public void updateState() {
Preference preference = new Preference(mContext);
mController.updateState(preference);
assertThat(preference.getIntent().getPackage()).isEqualTo(mPkg);
assertThat(preference.getIntent().getAction()).isEqualTo(
NotificationListenerService.ACTION_SETTINGS_HOME);
}
}

View File

@@ -0,0 +1,4 @@
# Default reviewers for this and subdirectories.
beverlyt@google.com
dsandler@android.com
juliacr@google.com

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.specialaccess.notificationaccess;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
import static com.google.common.truth.Truth.assertThat;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ServiceInfo;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.notification.NotificationBackend;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class OngoingTypeFilterPreferenceControllerTest {
private Context mContext;
private OngoingTypeFilterPreferenceController mController;
@Mock
NotificationBackend mNm;
ComponentName mCn = new ComponentName("a", "b");
ServiceInfo mSi = new ServiceInfo();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = ApplicationProvider.getApplicationContext();
mController = new OngoingTypeFilterPreferenceController(mContext, "key");
mController.setCn(mCn);
mController.setNm(mNm);
mController.setServiceInfo(mSi);
mController.setUserId(0);
}
@Test
public void getType() {
assertThat(mController.getType()).isEqualTo(FLAG_FILTER_TYPE_ONGOING);
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.specialaccess.notificationaccess;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
import android.content.Context;
import android.os.Build;
import android.service.notification.NotificationListenerFilter;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.notification.NotificationBackend;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class PreUpgradePreferenceControllerTest {
private Context mContext;
private PreUpgradePreferenceController mController;
@Mock
NotificationBackend mNm;
ComponentName mCn = new ComponentName("a", "b");
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = ApplicationProvider.getApplicationContext();
mController = new PreUpgradePreferenceController(mContext, "key");
mController.setCn(mCn);
mController.setNm(mNm);
mController.setUserId(0);
}
@Test
public void testAvailable_lowTargetSdk_noCustomizations() {
mController.setTargetSdk(Build.VERSION_CODES.S);
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void testAvailable_lowTargetSdk_customizations() {
mController.setTargetSdk(Build.VERSION_CODES.S);
NotificationListenerFilter nlf = new NotificationListenerFilter();
nlf.setTypes(FLAG_FILTER_TYPE_CONVERSATIONS);
when(mNm.getListenerFilter(mCn, 0)).thenReturn(nlf);
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
@Test
public void testAvailable_highTargetSdk_noCustomizations() {
mController.setTargetSdk(Build.VERSION_CODES.CUR_DEVELOPMENT + 1);
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.specialaccess.notificationaccess;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
import static com.google.common.truth.Truth.assertThat;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ServiceInfo;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.notification.NotificationBackend;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class SilentTypeFilterPreferenceControllerTest {
private Context mContext;
private SilentTypeFilterPreferenceController mController;
@Mock
NotificationBackend mNm;
ComponentName mCn = new ComponentName("a", "b");
ServiceInfo mSi = new ServiceInfo();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = ApplicationProvider.getApplicationContext();
mController = new SilentTypeFilterPreferenceController(mContext, "key");
mController.setCn(mCn);
mController.setNm(mNm);
mController.setServiceInfo(mSi);
mController.setUserId(0);
}
@Test
public void getType() {
assertThat(mController.getType()).isEqualTo(FLAG_FILTER_TYPE_SILENT);
}
}

View File

@@ -0,0 +1,264 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.specialaccess.notificationaccess;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ServiceInfo;
import android.os.Build;
import android.os.Bundle;
import android.service.notification.NotificationListenerFilter;
import android.service.notification.NotificationListenerService;
import android.util.ArraySet;
import androidx.preference.CheckBoxPreference;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.notification.NotificationBackend;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Set;
@RunWith(AndroidJUnit4.class)
public class TypeFilterPreferenceControllerTest {
private Context mContext;
private TypeFilterPreferenceController mController;
@Mock
NotificationBackend mNm;
ComponentName mCn = new ComponentName("a", "b");
ServiceInfo mSi = new ServiceInfo();
private static class TestTypeFilterPreferenceController extends TypeFilterPreferenceController {
public TestTypeFilterPreferenceController(Context context, String key) {
super(context, key);
}
@Override
protected int getType() {
return 32;
}
}
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = ApplicationProvider.getApplicationContext();
mController = new TestTypeFilterPreferenceController(mContext, "key");
mController.setCn(mCn);
mController.setNm(mNm);
mController.setServiceInfo(mSi);
mController.setUserId(0);
mController.setTargetSdk(Build.VERSION_CODES.CUR_DEVELOPMENT + 1);
}
@Test
public void testAvailable_notGranted() {
when(mNm.isNotificationListenerAccessGranted(any())).thenReturn(false);
mController.setTargetSdk(Build.VERSION_CODES.CUR_DEVELOPMENT + 1);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
}
@Test
public void testAvailable_lowTargetSdk_noCustomizations() {
when(mNm.isNotificationListenerAccessGranted(any())).thenReturn(true);
mController.setTargetSdk(Build.VERSION_CODES.S);
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
}
@Test
public void testAvailable_lowTargetSdk_customizations() {
when(mNm.isNotificationListenerAccessGranted(any())).thenReturn(true);
mController.setTargetSdk(Build.VERSION_CODES.S);
NotificationListenerFilter nlf = new NotificationListenerFilter();
nlf.setTypes(FLAG_FILTER_TYPE_CONVERSATIONS);
when(mNm.getListenerFilter(mCn, 0)).thenReturn(nlf);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void testAvailable_highTargetSdk_noCustomizations() {
when(mNm.isNotificationListenerAccessGranted(any())).thenReturn(true);
mController.setTargetSdk(Build.VERSION_CODES.CUR_DEVELOPMENT + 1);
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void updateState_enabled_noMetaData() {
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
CheckBoxPreference pref = new CheckBoxPreference(mContext);
mController.updateState(pref);
assertThat(pref.isEnabled()).isTrue();
}
@Test
public void updateState_enabled_metaData_notTheDisableFilter() {
mSi.metaData = new Bundle();
mSi.metaData.putCharSequence("test", "value");
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
CheckBoxPreference pref = new CheckBoxPreference(mContext);
mController.updateState(pref);
assertThat(pref.isEnabled()).isTrue();
}
@Test
public void updateState_enabled_metaData_disableFilter_notThisField() {
mSi.metaData = new Bundle();
mSi.metaData.putCharSequence(NotificationListenerService.META_DATA_DISABLED_FILTER_TYPES,
"1|alerting");
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
CheckBoxPreference pref = new CheckBoxPreference(mContext);
mController.updateState(pref);
assertThat(pref.isEnabled()).isTrue();
}
@Test
public void updateState_enabled_metaData_disableFilter_thisField_stateIsChecked() {
mSi.metaData = new Bundle();
mSi.metaData.putCharSequence(NotificationListenerService.META_DATA_DISABLED_FILTER_TYPES,
"conversations|2|32");
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
when(mNm.getListenerFilter(mCn, 0)).thenReturn(
new NotificationListenerFilter(32, new ArraySet<>()));
CheckBoxPreference pref = new CheckBoxPreference(mContext);
mController.updateState(pref);
assertThat(pref.isEnabled()).isTrue();
}
@Test
public void updateState_disabled() {
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(false);
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
CheckBoxPreference pref = new CheckBoxPreference(mContext);
mController.updateState(pref);
assertThat(pref.isEnabled()).isFalse();
}
@Test
public void updateState_disabled_metaData_disableFilter_thisField_stateIsNotChecked() {
mSi.metaData = new Bundle();
mSi.metaData.putCharSequence(NotificationListenerService.META_DATA_DISABLED_FILTER_TYPES,
"1|2|32");
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
NotificationListenerFilter before = new NotificationListenerFilter(4, new ArraySet<>());
when(mNm.getListenerFilter(mCn, 0)).thenReturn(before);
CheckBoxPreference pref = new CheckBoxPreference(mContext);
mController.updateState(pref);
assertThat(pref.isChecked()).isFalse();
assertThat(pref.isEnabled()).isFalse();
}
@Test
public void updateState_checked() {
NotificationListenerFilter nlf = new NotificationListenerFilter(mController.getType(),
new ArraySet<>());
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
when(mNm.getListenerFilter(mCn, 0)).thenReturn(nlf);
CheckBoxPreference pref = new CheckBoxPreference(mContext);
mController.updateState(pref);
assertThat(pref.isChecked()).isTrue();
}
@Test
public void updateState_unchecked() {
NotificationListenerFilter nlf = new NotificationListenerFilter(mController.getType() - 1,
new ArraySet<>());
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
when(mNm.getListenerFilter(mCn, 0)).thenReturn(nlf);
CheckBoxPreference pref = new CheckBoxPreference(mContext);
mController.updateState(pref);
assertThat(pref.isChecked()).isFalse();
}
@Test
public void onPreferenceChange_true() {
NotificationListenerFilter nlf = new NotificationListenerFilter(FLAG_FILTER_TYPE_ONGOING
| FLAG_FILTER_TYPE_CONVERSATIONS, new ArraySet<>());
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
when(mNm.getListenerFilter(mCn, 0)).thenReturn(nlf);
CheckBoxPreference pref = new CheckBoxPreference(mContext);
mController.onPreferenceChange(pref, true);
ArgumentCaptor<NotificationListenerFilter> captor =
ArgumentCaptor.forClass(NotificationListenerFilter.class);
verify(mNm).setListenerFilter(eq(mCn), eq(0), captor.capture());
assertThat(captor.getValue().getTypes()).isEqualTo(FLAG_FILTER_TYPE_CONVERSATIONS
| FLAG_FILTER_TYPE_ONGOING | mController.getType());
}
@Test
public void onPreferenceChange_false() {
NotificationListenerFilter nlf = new NotificationListenerFilter(FLAG_FILTER_TYPE_ONGOING
| FLAG_FILTER_TYPE_CONVERSATIONS | mController.getType(), new ArraySet<>());
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
when(mNm.getListenerFilter(mCn, 0)).thenReturn(nlf);
CheckBoxPreference pref = new CheckBoxPreference(mContext);
mController.onPreferenceChange(pref, false);
ArgumentCaptor<NotificationListenerFilter> captor =
ArgumentCaptor.forClass(NotificationListenerFilter.class);
verify(mNm).setListenerFilter(eq(mCn), eq(0), captor.capture());
assertThat(captor.getValue().getTypes()).isEqualTo(FLAG_FILTER_TYPE_CONVERSATIONS
| FLAG_FILTER_TYPE_ONGOING);
}
}

View File

@@ -0,0 +1,260 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import androidx.activity.result.ActivityResultLauncher;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class BiometricNavigationUtilsTest {
private static final String SETTINGS_CLASS_NAME = "SettingsClassName";
private static final String EXTRA_KEY = "EXTRA_KEY";
private static final ComponentName COMPONENT_NAME = new ComponentName("package", "class");
private static final int ADMIN_USER_ID = 2;
@Mock
private UserManager mUserManager;
@Mock
private ActivityResultLauncher<Intent> mLauncher;
private Context mContext;
private BiometricNavigationUtils mBiometricNavigationUtils;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
doNothing().when(mContext).startActivity(any());
mBiometricNavigationUtils = new BiometricNavigationUtils(UserHandle.myUserId());
}
@Test
public void launchBiometricSettings_quietMode_launchesQuiteModeDialog() {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
mBiometricNavigationUtils.launchBiometricSettings(mContext, SETTINGS_CLASS_NAME,
Bundle.EMPTY, null);
assertQuietModeDialogLaunchRequested();
}
@Test
public void launchBiometricSettings_quietMode_returnsFalse() {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
assertThat(mBiometricNavigationUtils.launchBiometricSettings(
mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY, null)).isFalse();
}
@Test
public void launchBiometricSettings_quietMode_withLauncher_notThroughLauncher() {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
mBiometricNavigationUtils.launchBiometricSettings(mContext, SETTINGS_CLASS_NAME,
Bundle.EMPTY, mLauncher);
verify(mLauncher, never()).launch(any(Intent.class));
}
@Test
public void launchBiometricSettings_noQuietMode_emptyExtras_launchesFragmentWithoutExtras() {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
mBiometricNavigationUtils.launchBiometricSettings(
mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY, null);
assertSettingsPageLaunchRequested(false /* shouldContainExtras */);
}
@Test
public void launchBiometricSettings_noQuietMode_emptyExtras_returnsTrue() {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
assertThat(mBiometricNavigationUtils.launchBiometricSettings(
mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY, null)).isTrue();
}
@Test
public void launchBiometricSettings_noQuietMode_withExtras_launchesFragmentWithExtras() {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
final Bundle extras = createNotEmptyExtras();
mBiometricNavigationUtils.launchBiometricSettings(
mContext, SETTINGS_CLASS_NAME, extras, null);
assertSettingsPageLaunchRequested(true /* shouldContainExtras */);
}
@Test
public void launchBiometricSettings_noQuietMode_withLauncher_launchesThroughLauncher() {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
final Bundle extras = createNotEmptyExtras();
mBiometricNavigationUtils.launchBiometricSettings(
mContext, SETTINGS_CLASS_NAME, extras, mLauncher);
verify(mLauncher).launch(any(Intent.class));
}
@Test
public void launchBiometricSettings_noQuietMode_withExtras_returnsTrue() {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
assertThat(mBiometricNavigationUtils.launchBiometricSettings(
mContext, SETTINGS_CLASS_NAME, createNotEmptyExtras(), null)).isTrue();
}
@Test
public void getBiometricSettingsIntent_quietMode_returnsQuiteModeDialogIntent() {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
final Intent intent = mBiometricNavigationUtils.getBiometricSettingsIntent(
mContext, SETTINGS_CLASS_NAME, null /* enforcedAdmin */, Bundle.EMPTY);
assertQuietModeDialogIntent(intent);
}
@Test
public void getBiometricSettingsIntent_noQuietMode_emptyExtras_returnsSettingsIntent() {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
final Intent intent = mBiometricNavigationUtils.getBiometricSettingsIntent(
mContext, SETTINGS_CLASS_NAME, null /* enforcedAdmin */, Bundle.EMPTY);
assertSettingsPageIntent(intent, false /* shouldContainExtras */);
}
@Test
public void getBiometricSettingsIntent_noQuietMode_withExtras_returnsSettingsIntent() {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
final Intent intent = mBiometricNavigationUtils.getBiometricSettingsIntent(
mContext, SETTINGS_CLASS_NAME, null /* enforcedAdmin */, createNotEmptyExtras());
assertSettingsPageIntent(intent, true /* shouldContainExtras */);
}
@Test
public void getBiometricSettingsIntent_whenDisabledByAdmin_quietMode_returnsBlockedIntent() {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
final EnforcedAdmin enforcedAdmin = new EnforcedAdmin(
COMPONENT_NAME, UserHandle.of(ADMIN_USER_ID));
final Intent intent = mBiometricNavigationUtils.getBiometricSettingsIntent(
mContext, SETTINGS_CLASS_NAME, enforcedAdmin, Bundle.EMPTY);
assertBlockedByAdminDialogIntent(intent);
}
@Test
public void getBiometricSettingsIntent_whenDisabledByAdmin_emptyExtras_returnsBlockedIntent() {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
final EnforcedAdmin enforcedAdmin = new EnforcedAdmin(
COMPONENT_NAME, UserHandle.of(ADMIN_USER_ID));
final Intent intent = mBiometricNavigationUtils.getBiometricSettingsIntent(
mContext, SETTINGS_CLASS_NAME, enforcedAdmin, Bundle.EMPTY);
assertBlockedByAdminDialogIntent(intent);
}
@Test
public void getBiometricSettingsIntent_whenDisabledByAdmin_withExtras_returnsBlockedIntent() {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
final EnforcedAdmin enforcedAdmin = new EnforcedAdmin(
COMPONENT_NAME, UserHandle.of(ADMIN_USER_ID));
final Intent intent = mBiometricNavigationUtils.getBiometricSettingsIntent(
mContext, SETTINGS_CLASS_NAME, enforcedAdmin, Bundle.EMPTY);
assertBlockedByAdminDialogIntent(intent);
}
private Bundle createNotEmptyExtras() {
final Bundle bundle = new Bundle();
bundle.putInt(EXTRA_KEY, 0);
return bundle;
}
private void assertQuietModeDialogLaunchRequested() {
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext).startActivity(intentCaptor.capture());
Intent intent = intentCaptor.getValue();
assertQuietModeDialogIntent(intent);
}
private void assertQuietModeDialogIntent(Intent intent) {
assertThat(intent.getComponent().getPackageName())
.isEqualTo("android");
assertThat(intent.getComponent().getClassName())
.isEqualTo("com.android.internal.app.UnlaunchableAppActivity");
}
private void assertBlockedByAdminDialogIntent(Intent intent) {
assertThat(intent.getAction()).isEqualTo(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
assertThat(
(ComponentName) intent.getParcelableExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN))
.isEqualTo(COMPONENT_NAME);
}
private void assertSettingsPageLaunchRequested(boolean shouldContainExtras) {
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext).startActivity(intentCaptor.capture());
Intent intent = intentCaptor.getValue();
assertSettingsPageIntent(intent, shouldContainExtras);
}
private void assertSettingsPageIntent(Intent intent, boolean shouldContainExtras) {
assertThat(intent.getComponent().getPackageName())
.isEqualTo("com.android.settings");
assertThat(intent.getComponent().getClassName())
.isEqualTo(SETTINGS_CLASS_NAME);
assertThat(intent.getExtras().containsKey(EXTRA_KEY)).isEqualTo(shouldContainExtras);
}
}

View File

@@ -0,0 +1,188 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics;
import static com.android.settings.biometrics.BiometricUtils.GatekeeperCredentialNotMatchException;
import static com.android.settings.biometrics.GatekeeperPasswordProvider.containsGatekeeperPasswordHandle;
import static com.android.settings.biometrics.GatekeeperPasswordProvider.getGatekeeperPasswordHandle;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.only;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Intent;
import androidx.annotation.NonNull;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.VerifyCredentialResponse;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@RunWith(AndroidJUnit4.class)
public class GatekeeperPasswordProviderTest {
@Rule public final MockitoRule mockito = MockitoJUnit.rule();
@Mock private LockPatternUtils mLockPatternUtils;
private GatekeeperPasswordProvider mGatekeeperPasswordProvider;
@Before
public void setUp() {
mGatekeeperPasswordProvider = new GatekeeperPasswordProvider(mLockPatternUtils);
}
@Test
public void testRequestGatekeeperHatWithHandle_success() {
final long gkPwHandle = 1L;
final long challenge = 2L;
final int userId = 0;
final byte[] expectedToken = new byte[] { 3, 2, 1 };
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, challenge, userId))
.thenReturn(newGoodCredential(gkPwHandle, expectedToken));
final byte[] actualToken = mGatekeeperPasswordProvider.requestGatekeeperHat(gkPwHandle,
challenge, userId);
assertThat(actualToken).isNotNull();
assertThat(actualToken.length).isEqualTo(expectedToken.length);
for (int i = 0; i < actualToken.length; ++i) {
assertWithMessage("actualToken[" + i + "] is " + actualToken[i] + " not "
+ expectedToken[i]).that(actualToken[i]).isEqualTo(expectedToken[i]);
}
}
@Test(expected = GatekeeperCredentialNotMatchException.class)
public void testRequestGatekeeperHatWithHandle_GatekeeperCredentialNotMatchException() {
final long gkPwHandle = 10L;
final long challenge = 20L;
final int userId = 300;
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, challenge, userId))
.thenReturn(newBadCredential(0));
mGatekeeperPasswordProvider.requestGatekeeperHat(gkPwHandle, challenge, userId);
}
@Test
public void testRequestGatekeeperHatWithIntent_success() {
final long gkPwHandle = 11L;
final long challenge = 21L;
final int userId = 145;
final byte[] expectedToken = new byte[] { 4, 5, 6, 7 };
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, challenge, userId))
.thenReturn(newGoodCredential(gkPwHandle, expectedToken));
final byte[] actualToken = mGatekeeperPasswordProvider.requestGatekeeperHat(
new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle), challenge, userId);
assertThat(actualToken).isNotNull();
assertThat(actualToken.length).isEqualTo(expectedToken.length);
for (int i = 0; i < actualToken.length; ++i) {
assertWithMessage("actualToken[" + i + "] is " + actualToken[i] + " not "
+ expectedToken[i]).that(actualToken[i]).isEqualTo(expectedToken[i]);
}
}
@Test(expected = GatekeeperCredentialNotMatchException.class)
public void testRequestGatekeeperHatWithIntent_GatekeeperCredentialNotMatchException() {
final long gkPwHandle = 12L;
final long challenge = 22L;
final int userId = 0;
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, challenge, userId))
.thenReturn(newBadCredential(0));
mGatekeeperPasswordProvider.requestGatekeeperHat(
new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle), challenge, userId);
}
@Test(expected = IllegalStateException.class)
public void testRequestGatekeeperHatWithIntent_IllegalStateException() {
mGatekeeperPasswordProvider.requestGatekeeperHat(new Intent(), 1L, 0);
}
@Test
public void testContainsGatekeeperPasswordHandle() {
assertThat(containsGatekeeperPasswordHandle(null)).isEqualTo(false);
assertThat(containsGatekeeperPasswordHandle(new Intent())).isEqualTo(false);
assertThat(containsGatekeeperPasswordHandle(
new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, 2L))).isEqualTo(true);
}
@Test
public void testGetGatekeeperPasswordHandle() {
assertThat(getGatekeeperPasswordHandle(new Intent())).isEqualTo(0L);
assertThat(getGatekeeperPasswordHandle(
new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, 3L))).isEqualTo(3L);
}
@Test
public void testRemoveGatekeeperPasswordHandleAsHandle() {
final long gkPwHandle = 1L;
doNothing().when(mLockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle);
mGatekeeperPasswordProvider.removeGatekeeperPasswordHandle(gkPwHandle);
verify(mLockPatternUtils, only()).removeGatekeeperPasswordHandle(gkPwHandle);
}
@Test
public void testRemoveGatekeeperPasswordHandleAsIntent() {
final long gkPwHandle = 1234L;
final Intent intent = new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
doNothing().when(mLockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle);
mGatekeeperPasswordProvider.removeGatekeeperPasswordHandle(intent, false);
verify(mLockPatternUtils, only()).removeGatekeeperPasswordHandle(gkPwHandle);
assertThat(intent.getLongExtra(EXTRA_KEY_GK_PW_HANDLE, 0L)).isEqualTo(gkPwHandle);
}
@Test
public void testRemoveGatekeeperPasswordHandleAsIntent_removeKey() {
final long gkPwHandle = 1234L;
final Intent intent = new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
doNothing().when(mLockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle);
mGatekeeperPasswordProvider.removeGatekeeperPasswordHandle(intent, true);
verify(mLockPatternUtils, only()).removeGatekeeperPasswordHandle(gkPwHandle);
assertThat(intent.hasExtra(EXTRA_KEY_GK_PW_HANDLE)).isEqualTo(false);
}
private VerifyCredentialResponse newGoodCredential(long gkPwHandle, @NonNull byte[] hat) {
return new VerifyCredentialResponse.Builder()
.setGatekeeperPasswordHandle(gkPwHandle)
.setGatekeeperHAT(hat)
.build();
}
private VerifyCredentialResponse newBadCredential(int timeout) {
if (timeout > 0) {
return VerifyCredentialResponse.fromTimeout(timeout);
} else {
return VerifyCredentialResponse.fromError();
}
}
}

View File

@@ -0,0 +1 @@
include /src/com/android/settings/biometrics/OWNERS

View File

@@ -0,0 +1,224 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_MODALITY;
import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_CONSENT_DENIED;
import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_CONSENT_GRANTED;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Activity;
import android.content.Intent;
import android.hardware.biometrics.BiometricAuthenticator;
import android.util.Pair;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.biometrics.face.FaceEnrollParentalConsent;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollParentalConsent;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@RunWith(AndroidJUnit4.class)
public class ParentalConsentHelperTest {
private static final int REQUEST_CODE = 12;
@Rule
public final MockitoRule mMocks = MockitoJUnit.rule();
@Mock
private Activity mRootActivity;
@Mock
private Intent mRootActivityIntent;
@Captor
ArgumentCaptor<Intent> mLastStarted;
@Before
public void setup() {
when(mRootActivity.getIntent()).thenAnswer(invocation -> mRootActivityIntent);
when(mRootActivityIntent.getBundleExtra(any())).thenAnswer(invocation -> null);
when(mRootActivityIntent.getStringExtra(any())).thenAnswer(invocation -> null);
when(mRootActivityIntent.getBooleanExtra(any(), anyBoolean()))
.thenAnswer(invocation -> invocation.getArguments()[1]);
}
@Test
public void testLaunchNext__fingerprint_all_consent() {
testLaunchNext(
true /* requireFace */, true /* grantFace */,
true /* requireFingerprint */, true /* grantFace */,
90 /* gkpw */);
}
@Test
public void testLaunchNext_nothing_to_consent() {
testLaunchNext(
false /* requireFace */, false /* grantFace */,
false /* requireFingerprint */, false /* grantFace */,
80 /* gkpw */);
}
@Test
public void testLaunchNext_face_and_fingerprint_no_consent() {
testLaunchNext(
true /* requireFace */, false /* grantFace */,
true /* requireFingerprint */, false /* grantFace */,
70 /* gkpw */);
}
@Test
public void testLaunchNext_face_and_fingerprint_only_face_consent() {
testLaunchNext(
true /* requireFace */, true /* grantFace */,
true /* requireFingerprint */, false /* grantFace */,
60 /* gkpw */);
}
@Test
public void testLaunchNext_face_and_fingerprint_only_fingerprint_consent() {
testLaunchNext(
true /* requireFace */, false /* grantFace */,
true /* requireFingerprint */, true /* grantFace */,
50 /* gkpw */);
}
@Test
public void testLaunchNext_face_with_consent() {
testLaunchNext(
true /* requireFace */, true /* grantFace */,
false /* requireFingerprint */, false /* grantFace */,
40 /* gkpw */);
}
@Test
public void testLaunchNext_face_without_consent() {
testLaunchNext(
true /* requireFace */, false /* grantFace */,
false /* requireFingerprint */, false /* grantFace */,
30 /* gkpw */);
}
@Test
public void testLaunchNext_fingerprint_with_consent() {
testLaunchNext(
false /* requireFace */, false /* grantFace */,
true /* requireFingerprint */, true /* grantFace */,
20 /* gkpw */);
}
@Test
public void testLaunchNext_fingerprint_without_consent() {
testLaunchNext(
false /* requireFace */, false /* grantFace */,
true /* requireFingerprint */, false /* grantFace */,
10 /* gkpw */);
}
private void testLaunchNext(
boolean requireFace, boolean grantFace,
boolean requireFingerprint, boolean grantFingerprint,
long gkpw) {
final List<Pair<String, Boolean>> expectedLaunches = new ArrayList<>();
if (requireFingerprint) {
expectedLaunches.add(
new Pair(FingerprintEnrollParentalConsent.class.getName(), grantFingerprint));
}
if (requireFace) {
expectedLaunches.add(new Pair(FaceEnrollParentalConsent.class.getName(), grantFace));
}
// initial consent status
final ParentalConsentHelper helper = new ParentalConsentHelper(gkpw);
helper.setConsentRequirement(requireFace, requireFingerprint);
assertThat(ParentalConsentHelper.hasFaceConsent(helper.getConsentResult()))
.isFalse();
assertThat(ParentalConsentHelper.hasFingerprintConsent(helper.getConsentResult()))
.isFalse();
// check expected launches
for (int i = 0; i <= expectedLaunches.size(); i++) {
final Pair<String, Boolean> expected = i > 0 ? expectedLaunches.get(i - 1) : null;
final boolean launchedNext = i == 0
? helper.launchNext(mRootActivity, REQUEST_CODE)
: helper.launchNext(mRootActivity, REQUEST_CODE,
expected.second ? RESULT_CONSENT_GRANTED : RESULT_CONSENT_DENIED,
getResultIntent(getStartedModality(expected.first)));
assertThat(launchedNext).isEqualTo(i < expectedLaunches.size());
}
verify(mRootActivity, times(expectedLaunches.size()))
.startActivityForResult(mLastStarted.capture(), eq(REQUEST_CODE));
assertThat(mLastStarted.getAllValues()
.stream().map(i -> i.getComponent().getClassName()).collect(Collectors.toList()))
.containsExactlyElementsIn(
expectedLaunches.stream().map(i -> i.first).collect(Collectors.toList()))
.inOrder();
if (!expectedLaunches.isEmpty()) {
assertThat(mLastStarted.getAllValues()
.stream().map(BiometricUtils::getGatekeeperPasswordHandle).distinct()
.collect(Collectors.toList()))
.containsExactly(gkpw);
}
// final consent status
assertThat(ParentalConsentHelper.hasFaceConsent(helper.getConsentResult()))
.isEqualTo(requireFace && grantFace);
assertThat(ParentalConsentHelper.hasFingerprintConsent(helper.getConsentResult()))
.isEqualTo(requireFingerprint && grantFingerprint);
}
private static Intent getResultIntent(@BiometricAuthenticator.Modality int modality) {
final Intent intent = new Intent();
intent.putExtra(EXTRA_KEY_MODALITY, modality);
return intent;
}
@BiometricAuthenticator.Modality
private static int getStartedModality(String name) {
if (name.equals(FaceEnrollParentalConsent.class.getName())) {
return TYPE_FACE;
}
if (name.equals(FingerprintEnrollParentalConsent.class.getName())) {
return TYPE_FINGERPRINT;
}
return TYPE_NONE;
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics;
import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertNull;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_IRIS;
import android.hardware.biometrics.BiometricAuthenticator;
import android.os.UserHandle;
import android.os.UserManager;
import androidx.annotation.Nullable;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settingslib.RestrictedLockUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class ParentalControlsUtilsTest {
@Mock
private Context mContext;
@Mock
private DevicePolicyManager mDpm;
private ComponentName mSupervisionComponentName = new ComponentName("pkg", "cls");
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mContext.getContentResolver()).thenReturn(mock(ContentResolver.class));
}
/**
* Helper that sets the appropriate mocks and testing behavior before returning the actual
* EnforcedAdmin from ParentalControlsUtils.
*/
@Nullable
private RestrictedLockUtils.EnforcedAdmin getEnforcedAdminForCombination(
@Nullable ComponentName supervisionComponentName,
@BiometricAuthenticator.Modality int modality, int keyguardDisabledFlags) {
when(mDpm.getProfileOwnerOrDeviceOwnerSupervisionComponent(any(UserHandle.class)))
.thenReturn(supervisionComponentName);
when(mDpm.getKeyguardDisabledFeatures(eq(supervisionComponentName)))
.thenReturn(keyguardDisabledFlags);
return ParentalControlsUtils.parentConsentRequiredInternal(mDpm, modality,
new UserHandle(UserHandle.myUserId()));
}
@Test
public void testEnforcedAdmin_whenDpmDisablesBiometricsAndSupervisionComponentExists() {
int[][] tests = {
{TYPE_FINGERPRINT, DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT},
{TYPE_FACE, DevicePolicyManager.KEYGUARD_DISABLE_FACE},
{TYPE_IRIS, DevicePolicyManager.KEYGUARD_DISABLE_IRIS},
};
for (int i = 0; i < tests.length; i++) {
RestrictedLockUtils.EnforcedAdmin admin = getEnforcedAdminForCombination(
mSupervisionComponentName, tests[i][0] /* modality */,
tests[i][1] /* keyguardDisableFlags */);
assertNotNull(admin);
assertEquals(UserManager.DISALLOW_BIOMETRIC, admin.enforcedRestriction);
assertEquals(mSupervisionComponentName, admin.component);
}
}
@Test
public void testNoEnforcedAdmin_whenNoSupervisionComponent() {
// Even if DPM flag exists, returns null EnforcedAdmin when no supervision component exists
RestrictedLockUtils.EnforcedAdmin admin = getEnforcedAdminForCombination(
null /* supervisionComponentName */, TYPE_FINGERPRINT,
DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
assertNull(admin);
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics.combination;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class BiometricSettingsAppPreferenceControllerTest {
private Context mContext;
private BiometricSettingsAppPreferenceController mController;
@Before
public void setUp() {
mContext = ApplicationProvider.getApplicationContext();
mController = new BiometricSettingsAppPreferenceController(mContext, "key");
}
@Test
public void isSliceable_returnFalse() {
assertThat(mController.isSliceable()).isFalse();
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics.combination;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class BiometricSettingsKeyguardPreferenceControllerTest {
private Context mContext;
private BiometricSettingsKeyguardPreferenceController mController;
@Before
public void setUp() {
mContext = ApplicationProvider.getApplicationContext();
mController = new BiometricSettingsKeyguardPreferenceController(mContext, "key");
}
@Test
public void isSliceable_returnFalse() {
assertThat(mController.isSliceable()).isFalse();
}
}

View File

@@ -0,0 +1,326 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics.combination;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FACE;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.Settings;
import com.android.settings.testutils.ResourcesUtils;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.utils.StringUtil;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
@RunWith(AndroidJUnit4.class)
public class CombinedBiometricStatusUtilsTest {
private static final ComponentName COMPONENT_NAME =
new ComponentName("package", "class");
private static final int USER_ID = UserHandle.myUserId();
private static final UserHandle USER_HANDLE = new UserHandle(USER_ID);
@Mock
private PackageManager mPackageManager;
@Mock
private DevicePolicyManager mDevicePolicyManager;
@Mock
private FingerprintManager mFingerprintManager;
@Mock
private FaceManager mFaceManager;
private Context mApplicationContext;
private CombinedBiometricStatusUtils mCombinedBiometricStatusUtils;
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mApplicationContext = spy(ApplicationProvider.getApplicationContext());
when(mApplicationContext.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
when(mDevicePolicyManager.getProfileOwnerOrDeviceOwnerSupervisionComponent(USER_HANDLE))
.thenReturn(COMPONENT_NAME);
when(mApplicationContext.getSystemService(Context.FINGERPRINT_SERVICE))
.thenReturn(mFingerprintManager);
when(mApplicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE))
.thenReturn(mDevicePolicyManager);
when(mApplicationContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager);
mCombinedBiometricStatusUtils = new CombinedBiometricStatusUtils(
mApplicationContext, USER_ID);
}
@Test
public void isAvailable_withoutFingerprint_withoutFace_returnsFalse() {
when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
when(mFaceManager.isHardwareDetected()).thenReturn(false);
assertThat(mCombinedBiometricStatusUtils.isAvailable()).isFalse();
}
@Test
public void isAvailable_withoutFingerprint_whenFace_returnsFalse() {
when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
when(mFaceManager.isHardwareDetected()).thenReturn(true);
assertThat(mCombinedBiometricStatusUtils.isAvailable()).isFalse();
}
@Test
public void isAvailable_whenFingerprint_withoutFace_returnsFalse() {
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFaceManager.isHardwareDetected()).thenReturn(false);
assertThat(mCombinedBiometricStatusUtils.isAvailable()).isFalse();
}
@Test
public void isAvailable_whenFingerprint_whenFace_returnsTrue() {
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFaceManager.isHardwareDetected()).thenReturn(true);
assertThat(mCombinedBiometricStatusUtils.isAvailable()).isTrue();
}
@Test
public void hasEnrolled_withoutFingerprintHardware_withoutFaceHardware_returnsFalse() {
when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
when(mFaceManager.isHardwareDetected()).thenReturn(false);
assertThat(mCombinedBiometricStatusUtils.hasEnrolled()).isFalse();
}
@Test
public void hasEnrolled_withoutFingerprintEnroll_withoutFaceEnroll_returnsFalse() {
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFaceManager.isHardwareDetected()).thenReturn(true);
when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false);
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
assertThat(mCombinedBiometricStatusUtils.hasEnrolled()).isFalse();
}
@Test
public void hasEnrolled_withoutFingerprintEnroll_withFaceEnroll_returnsTrue() {
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFaceManager.isHardwareDetected()).thenReturn(true);
when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false);
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
assertThat(mCombinedBiometricStatusUtils.hasEnrolled()).isTrue();
}
@Test
public void hasEnrolled_withFingerprintEnroll_withoutFaceEnroll_returnsTrue() {
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFaceManager.isHardwareDetected()).thenReturn(true);
when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true);
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
assertThat(mCombinedBiometricStatusUtils.hasEnrolled()).isTrue();
}
@Test
public void hasEnrolled_withFingerprintEnroll_withFaceEnroll_returnsTrue() {
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFaceManager.isHardwareDetected()).thenReturn(true);
when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true);
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
assertThat(mCombinedBiometricStatusUtils.hasEnrolled()).isTrue();
}
@Test
public void getDisabledAdmin_whenFingerprintDisabled_whenFaceDisabled_returnsEnforcedAdmin() {
when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME))
.thenReturn(KEYGUARD_DISABLE_FACE | KEYGUARD_DISABLE_FINGERPRINT);
final RestrictedLockUtils.EnforcedAdmin admin =
mCombinedBiometricStatusUtils.getDisablingAdmin();
assertThat(admin).isEqualTo(new RestrictedLockUtils.EnforcedAdmin(
COMPONENT_NAME, UserManager.DISALLOW_BIOMETRIC, USER_HANDLE));
}
@Test
public void getDisabledAdmin_whenFingerprintDisabled_whenFaceEnabled_returnsNull() {
when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME))
.thenReturn(KEYGUARD_DISABLE_FINGERPRINT);
assertThat(mCombinedBiometricStatusUtils.getDisablingAdmin()).isNull();
}
@Test
public void getDisabledAdmin_whenFingerprintEnabled_whenFaceDisabled_returnsNull() {
when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME))
.thenReturn(KEYGUARD_DISABLE_FACE);
assertThat(mCombinedBiometricStatusUtils.getDisablingAdmin()).isNull();
}
@Test
public void getDisabledAdmin_whenFingerprintEnabled_whenFaceEnabled_returnsNull() {
when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0);
assertThat(mCombinedBiometricStatusUtils.getDisablingAdmin()).isNull();
}
@Test
public void getSummary_whenFaceEnrolled_whenMultipleFingerprints_returnsBothFpMultiple() {
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
.thenReturn(createFingerprintList(2));
assertThat(mCombinedBiometricStatusUtils.getSummary())
.isEqualTo(ResourcesUtils.getResourcesString(
mApplicationContext,
"security_settings_biometric_preference_summary_both_fp_multiple"));
}
@Test
public void getSummary_whenFaceEnrolled_whenSingleFingerprint_returnsBothFpSingle() {
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
.thenReturn(createFingerprintList(1));
assertThat(mCombinedBiometricStatusUtils.getSummary())
.isEqualTo(ResourcesUtils.getResourcesString(
mApplicationContext,
"security_settings_biometric_preference_summary_both_fp_single"));
}
@Test
public void getSummary_whenFaceEnrolled_whenNoFingerprints_returnsFace() {
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
.thenReturn(createFingerprintList(0));
assertThat(mCombinedBiometricStatusUtils.getSummary())
.isEqualTo(ResourcesUtils.getResourcesString(
mApplicationContext,
"security_settings_face_preference_summary"));
}
@Test
public void getSummary_whenNoFaceEnrolled_whenMultipleFingerprints_returnsFingerprints() {
final int enrolledFingerprintsCount = 2;
final int stringResId = ResourcesUtils.getResourcesId(
ApplicationProvider.getApplicationContext(), "string",
"security_settings_fingerprint_preference_summary");
final String summary = StringUtil.getIcuPluralsString(mApplicationContext,
enrolledFingerprintsCount, stringResId);
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
.thenReturn(createFingerprintList(enrolledFingerprintsCount));
assertThat(mCombinedBiometricStatusUtils.getSummary()).isEqualTo(summary);
}
@Test
public void getSummary_whenNoFaceEnrolled_whenSingleFingerprints_returnsFingerprints() {
final int enrolledFingerprintsCount = 1;
final int stringResId = ResourcesUtils.getResourcesId(
ApplicationProvider.getApplicationContext(), "string",
"security_settings_fingerprint_preference_summary");
final String summary = StringUtil.getIcuPluralsString(mApplicationContext,
enrolledFingerprintsCount, stringResId);
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
.thenReturn(createFingerprintList(enrolledFingerprintsCount));
assertThat(mCombinedBiometricStatusUtils.getSummary()).isEqualTo(summary);
}
@Test
public void getSummary_whenNoFaceEnrolled_whenNoFingerprints_returnsNoneEnrolled() {
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
.thenReturn(createFingerprintList(0));
assertThat(mCombinedBiometricStatusUtils.getSummary())
.isEqualTo(ResourcesUtils.getResourcesString(
mApplicationContext,
"security_settings_biometric_preference_summary_none_enrolled"));
}
@Test
public void getSettingsClassName_returnsCombinedBiometricSettings() {
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
assertThat(mCombinedBiometricStatusUtils.getSettingsClassName())
.isEqualTo(Settings.CombinedBiometricSettingsActivity.class.getName());
}
@Test
public void getProfileSettingsClassName_returnsCombinedBiometricProfileSettings() {
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
assertThat(mCombinedBiometricStatusUtils.getProfileSettingsClassName())
.isEqualTo(Settings.CombinedBiometricProfileSettingsActivity.class.getName());
}
@Test
public void getPrivateProfileSettingsClassName_returnsPrivateSpaceBiometricSettings() {
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
mSetFlagsRule.enableFlags(
android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE);
assertThat(mCombinedBiometricStatusUtils.getPrivateProfileSettingsClassName())
.isEqualTo(Settings.PrivateSpaceBiometricSettingsActivity.class.getName());
}
private List<Fingerprint> createFingerprintList(int size) {
final List<Fingerprint> fingerprintList = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
fingerprintList.add(new Fingerprint("fingerprint" + i, 0, 0));
}
return fingerprintList;
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics.face;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class FaceSettingsAppPreferenceControllerTest {
private Context mContext;
private FaceSettingsAppPreferenceController mController;
@Before
public void setUp() {
mContext = ApplicationProvider.getApplicationContext();
mController = new FaceSettingsAppPreferenceController(mContext);
}
@Test
public void isSliceable_returnFalse() {
assertThat(mController.isSliceable()).isFalse();
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics.face;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class FaceSettingsAttentionPreferenceControllerTest {
private Context mContext;
private FaceSettingsAttentionPreferenceController mController;
@Before
public void setUp() {
mContext = ApplicationProvider.getApplicationContext();
mController = new FaceSettingsAttentionPreferenceController(mContext);
}
@Test
public void isSliceable_returnFalse() {
assertThat(mController.isSliceable()).isFalse();
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics.face;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class FaceSettingsConfirmPreferenceControllerTest {
private Context mContext;
private FaceSettingsConfirmPreferenceController mController;
@Before
public void setUp() {
mContext = ApplicationProvider.getApplicationContext();
mController = new FaceSettingsConfirmPreferenceController(mContext);
}
@Test
public void isSliceable_returnFalse() {
assertThat(mController.isSliceable()).isFalse();
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics.face;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.widget.Button;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@RunWith(AndroidJUnit4.class)
public class FaceSettingsEnrollButtonPreferenceControllerTest {
@Rule
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Mock
private Context mContext;
@Mock
private Button mButton;
@Mock
private FaceSettingsEnrollButtonPreferenceController.Listener mListener;
private FaceSettingsEnrollButtonPreferenceController mController;
@Before
public void setUp() {
mController = new FaceSettingsEnrollButtonPreferenceController(mContext);
mController.setListener(mListener);
}
@Test
public void isSliceable_returnFalse() {
assertThat(mController.isSliceable()).isFalse();
}
@Test
public void testOnClick() {
mController.onClick(mButton);
assertThat(mController.isClicked()).isTrue();
verify(mListener).onStartEnrolling(any());
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics.face;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class FaceSettingsKeyguardPreferenceControllerTest {
private Context mContext;
private FaceSettingsKeyguardPreferenceController mController;
@Before
public void setUp() {
mContext = ApplicationProvider.getApplicationContext();
mController = new FaceSettingsKeyguardPreferenceController(mContext);
}
@Test
public void isSliceable_returnFalse() {
assertThat(mController.isSliceable()).isFalse();
}
}

View File

@@ -0,0 +1,184 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics.face;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.UserHandle;
import android.os.UserManager;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.Settings;
import com.android.settings.testutils.ResourcesUtils;
import com.android.settingslib.RestrictedLockUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class FaceStatusUtilsTest {
private static final ComponentName COMPONENT_NAME =
new ComponentName("package", "class");
private static final int USER_ID = UserHandle.myUserId();
private static final UserHandle USER_HANDLE = new UserHandle(USER_ID);
@Mock
private PackageManager mPackageManager;
@Mock
private DevicePolicyManager mDevicePolicyManager;
@Mock
private FingerprintManager mFingerprintManager;
@Mock
private FaceManager mFaceManager;
private Context mApplicationContext;
private FaceStatusUtils mFaceStatusUtils;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mApplicationContext = spy(ApplicationProvider.getApplicationContext());
when(mApplicationContext.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
when(mDevicePolicyManager.getProfileOwnerOrDeviceOwnerSupervisionComponent(USER_HANDLE))
.thenReturn(COMPONENT_NAME);
when(mApplicationContext.getSystemService(Context.FINGERPRINT_SERVICE))
.thenReturn(mFingerprintManager);
when(mApplicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE))
.thenReturn(mDevicePolicyManager);
when(mApplicationContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager);
mFaceStatusUtils = new FaceStatusUtils(mApplicationContext, mFaceManager, USER_ID);
}
@Test
public void isAvailable_withoutFingerprint_withoutFace_returnsFalse() {
when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
when(mFaceManager.isHardwareDetected()).thenReturn(false);
assertThat(mFaceStatusUtils.isAvailable()).isFalse();
}
@Test
public void isAvailable_withoutFingerprint_withFace_returnsTrue() {
when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
when(mFaceManager.isHardwareDetected()).thenReturn(true);
assertThat(mFaceStatusUtils.isAvailable()).isTrue();
}
@Test
public void isAvailable_withFingerprint_withoutFace_returnsFalse() {
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFaceManager.isHardwareDetected()).thenReturn(false);
assertThat(mFaceStatusUtils.isAvailable()).isFalse();
}
@Test
public void isAvailable_withFingerprint_withFace_returnsFalse() {
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFaceManager.isHardwareDetected()).thenReturn(true);
assertThat(mFaceStatusUtils.isAvailable()).isFalse();
}
@Test
public void hasEnrolled_withEnrolledTemplates_returnsTrue() {
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
assertThat(mFaceStatusUtils.hasEnrolled()).isTrue();
}
@Test
public void hasEnrolled_withoutEnrolledTemplates_returnsFalse() {
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
assertThat(mFaceStatusUtils.hasEnrolled()).isFalse();
}
@Test
public void getDisabledAdmin_whenFaceDisabled_returnsEnforcedAdmin() {
when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME))
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FACE);
final RestrictedLockUtils.EnforcedAdmin admin = mFaceStatusUtils.getDisablingAdmin();
assertThat(admin).isEqualTo(new RestrictedLockUtils.EnforcedAdmin(
COMPONENT_NAME, UserManager.DISALLOW_BIOMETRIC, USER_HANDLE));
}
@Test
public void getDisabledAdmin_withFaceEnabled_returnsNull() {
when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0);
assertThat(mFaceStatusUtils.getDisablingAdmin()).isNull();
}
@Test
public void getSummary_whenNotEnrolled_returnsSummaryNone() {
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
assertThat(mFaceStatusUtils.getSummary())
.isEqualTo(ResourcesUtils.getResourcesString(
mApplicationContext,
"security_settings_face_preference_summary_none"));
}
@Test
public void getSummary_whenEnrolled_returnsSummary() {
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
assertThat(mFaceStatusUtils.getSummary())
.isEqualTo(ResourcesUtils.getResourcesString(
mApplicationContext,
"security_settings_face_preference_summary"));
}
@Test
public void getSettingsClassName_whenNotEnrolled_returnsFaceEnrollInduction() {
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
assertThat(mFaceStatusUtils.getSettingsClassName())
.isEqualTo(FaceEnrollIntroductionInternal.class.getName());
}
@Test
public void getSettingsClassName_whenEnrolled_returnsFaceSettings() {
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
assertThat(mFaceStatusUtils.getSettingsClassName())
.isEqualTo(Settings.FaceSettingsInternalActivity.class.getName());
}
}

View File

@@ -0,0 +1,290 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics.face;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.content.Intent;
import android.hardware.face.Face;
import android.hardware.face.FaceEnrollCell;
import android.hardware.face.FaceEnrollStages;
import android.hardware.face.FaceManager;
import android.os.CancellationSignal;
import android.view.Surface;
import androidx.annotation.Nullable;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class FaceUpdaterTest {
private static final byte[] HARDWARE_AUTH_TOKEN = new byte[] {0};
private static final CancellationSignal CANCELLATION_SIGNAL = new CancellationSignal();
private static final int USER_ID = 0;
private static final int ERR_MSG_ID = 0;
private static final int HELP_MSG_ID = 0;
private static final String HELP_STRING = "";
private static final String ERR_STRING = "";
private static final Face FACE =
new Face(/* name= */"", /* faceId */ 0, /* deviceId= */ 0L);
private static final int[] DISABLED_FEATURES = new int[] {0};
private static final boolean DEBUG_CONSENT = false;
private static final Surface PREVIEW_SURFACE = new Surface();
private static final int HELP_CODE = 0;
private static final CharSequence HELP_MESSAGE = "";
private static final FaceEnrollCell CELL =
new FaceEnrollCell(/* x= */ 0, /* y= */ 0, /* z= */ 0);
private static final int STAGE = FaceEnrollStages.UNKNOWN;
private static final float PAN = 0;
private static final float TILT = 0;
private static final float DISTANCE = 0;
@Mock private FaceManager mFaceManager;
@Mock private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper;
private FaceUpdater mFaceUpdater;
private Context mContext;
private FaceManager.EnrollmentCallback mEnrollmentCallback;
private FaceManager.RemovalCallback mRemovalCallback;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = ApplicationProvider.getApplicationContext();
mFaceUpdater = new FaceUpdater(mContext, mFaceManager);
mEnrollmentCallback = spy(new TestEnrollmentCallback());
mRemovalCallback = spy(new TestRemovalCallback());
SafetyCenterManagerWrapper.sInstance = mSafetyCenterManagerWrapper;
}
@Test
public void enroll_firstVersion_onEnrollmentCallbacks_triggerGivenCallback() {
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class);
mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback,
DISABLED_FEATURES, new Intent());
verify(mFaceManager).enroll(
eq(USER_ID),
same(HARDWARE_AUTH_TOKEN),
same(CANCELLATION_SIGNAL),
callbackCaptor.capture(),
same(DISABLED_FEATURES),
same(null),
eq(false),
any());
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentError(ERR_MSG_ID, ERR_STRING);
callback.onEnrollmentProgress(/* remaining= */ 2);
callback.onEnrollmentHelp(HELP_MSG_ID, HELP_STRING);
callback.onEnrollmentFrame(HELP_CODE, HELP_MESSAGE, CELL, STAGE, PAN, TILT, DISTANCE);
verify(mEnrollmentCallback, atLeast(1)).onEnrollmentError(ERR_MSG_ID, ERR_STRING);
verify(mEnrollmentCallback, atLeast(1)).onEnrollmentProgress(/* remaining= */ 2);
verify(mEnrollmentCallback, atLeast(1)).onEnrollmentHelp(HELP_MSG_ID, HELP_STRING);
verify(mEnrollmentCallback, atLeast(1))
.onEnrollmentFrame(HELP_CODE, HELP_MESSAGE, CELL, STAGE, PAN, TILT, DISTANCE);
}
@Test
public void enroll_firstVersion_onEnrollmentSuccess_invokedInteractionWithSafetyCenter() {
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class);
mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback,
DISABLED_FEATURES, new Intent());
verify(mFaceManager).enroll(
eq(USER_ID),
same(HARDWARE_AUTH_TOKEN),
same(CANCELLATION_SIGNAL),
callbackCaptor.capture(),
same(DISABLED_FEATURES),
same(null),
eq(false),
any());
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentProgress(/* remaining= */ 0);
verify(mSafetyCenterManagerWrapper, atLeast(1)).isEnabled(mContext);
}
@Test
public void enroll_firstVersion_onEnrollmentNotYetFinished_didntInvokeInteractionWithSafetyCenter() {
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class);
mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback,
DISABLED_FEATURES, new Intent());
verify(mFaceManager).enroll(
eq(USER_ID),
same(HARDWARE_AUTH_TOKEN),
same(CANCELLATION_SIGNAL),
callbackCaptor.capture(),
same(DISABLED_FEATURES),
same(null),
eq(false),
any());
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentProgress(/* remaining= */ 1);
verify(mSafetyCenterManagerWrapper, never()).isEnabled(any());
}
@Test
public void enroll_secondVersion_onEnrollmentCallbacks_triggerGivenCallback() {
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class);
mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback,
DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT, new Intent());
verify(mFaceManager).enroll(
eq(USER_ID),
same(HARDWARE_AUTH_TOKEN),
same(CANCELLATION_SIGNAL),
callbackCaptor.capture(),
same(DISABLED_FEATURES),
same(PREVIEW_SURFACE),
eq(DEBUG_CONSENT),
any());
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentError(ERR_MSG_ID, ERR_STRING);
callback.onEnrollmentProgress(/* remaining= */ 2);
callback.onEnrollmentHelp(HELP_MSG_ID, HELP_STRING);
callback.onEnrollmentFrame(HELP_CODE, HELP_MESSAGE, CELL, STAGE, PAN, TILT, DISTANCE);
verify(mEnrollmentCallback, atLeast(1)).onEnrollmentError(ERR_MSG_ID, ERR_STRING);
verify(mEnrollmentCallback, atLeast(1)).onEnrollmentProgress(/* remaining= */ 2);
verify(mEnrollmentCallback, atLeast(1)).onEnrollmentHelp(HELP_MSG_ID, HELP_STRING);
verify(mEnrollmentCallback, atLeast(1))
.onEnrollmentFrame(HELP_CODE, HELP_MESSAGE, CELL, STAGE, PAN, TILT, DISTANCE);
}
@Test
public void enroll_secondVersion_onEnrollmentSuccess_invokedInteractionWithSafetyCenter() {
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class);
mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback,
DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT, new Intent());
verify(mFaceManager).enroll(
eq(USER_ID),
same(HARDWARE_AUTH_TOKEN),
same(CANCELLATION_SIGNAL),
callbackCaptor.capture(),
same(DISABLED_FEATURES),
same(PREVIEW_SURFACE),
eq(DEBUG_CONSENT),
any());
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentProgress(/* remaining= */ 0);
verify(mSafetyCenterManagerWrapper).isEnabled(mContext);
}
@Test
public void enroll_secondVersion_onEnrollmentNotYetFinished_didntInvokeInteractionWithSafetyCenter() {
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class);
mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback,
DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT, new Intent());
verify(mFaceManager).enroll(
eq(USER_ID),
same(HARDWARE_AUTH_TOKEN),
same(CANCELLATION_SIGNAL),
callbackCaptor.capture(),
same(DISABLED_FEATURES),
same(PREVIEW_SURFACE),
eq(DEBUG_CONSENT),
any());
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentProgress(/* remaining= */ 1);
verify(mSafetyCenterManagerWrapper, never()).isEnabled(any());
}
@Test
public void remove_onRemovalCallbacks_triggerGivenCallback() {
ArgumentCaptor<FaceManager.RemovalCallback> callbackCaptor =
ArgumentCaptor.forClass(FaceManager.RemovalCallback.class);
mFaceUpdater.remove(FACE, USER_ID, mRemovalCallback);
verify(mFaceManager)
.remove(same(FACE), eq(USER_ID), callbackCaptor.capture());
FaceManager.RemovalCallback callback = callbackCaptor.getValue();
callback.onRemovalSucceeded(FACE, /* remaining= */ 1);
callback.onRemovalError(FACE, ERR_MSG_ID, ERR_STRING);
verify(mRemovalCallback).onRemovalSucceeded(any(), eq(1));
verify(mRemovalCallback).onRemovalError(FACE, ERR_MSG_ID, ERR_STRING);
}
@Test
public void remove_onRemovalSuccess_invokedInteractionWithSafetyCenter() {
ArgumentCaptor<FaceManager.RemovalCallback> callbackCaptor =
ArgumentCaptor.forClass(FaceManager.RemovalCallback.class);
mFaceUpdater.remove(FACE, USER_ID, mRemovalCallback);
verify(mFaceManager)
.remove(same(FACE), eq(USER_ID), callbackCaptor.capture());
FaceManager.RemovalCallback callback = callbackCaptor.getValue();
callback.onRemovalSucceeded(FACE, /* remaining= */ 0);
verify(mSafetyCenterManagerWrapper).isEnabled(mContext);
}
public static class TestEnrollmentCallback extends FaceManager.EnrollmentCallback {
@Override
public void onEnrollmentError(int errMsgId, CharSequence errString) {}
@Override
public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {}
@Override
public void onEnrollmentProgress(int remaining) {}
@Override
public void onEnrollmentFrame(int helpCode, @Nullable CharSequence helpMessage,
@Nullable FaceEnrollCell cell, int stage, float pan, float tilt, float distance) {}
}
public static class TestRemovalCallback extends FaceManager.RemovalCallback {
@Override
public void onRemovalError(Face fp, int errMsgId, CharSequence errString) {}
@Override
public void onRemovalSucceeded(@Nullable Face fp, int remaining) {}
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics.fingerprint
import android.content.Context
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeatureImpl
import com.android.settings.biometrics.fingerprint.feature.SfpsRestToUnlockFeatureImpl
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
@RunWith(AndroidJUnit4::class)
class FingerprintFeatureProviderImplTest {
@Mock
private lateinit var mContext: Context
private lateinit var mFingerprintFeatureProviderImpl: FingerprintFeatureProviderImpl
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
mFingerprintFeatureProviderImpl = FingerprintFeatureProviderImpl()
}
@Test
fun getSfpsEnrollmentFeature_returnDefaultImpl() {
assertThat(mFingerprintFeatureProviderImpl.sfpsEnrollmentFeature)
.isInstanceOf(SfpsEnrollmentFeatureImpl::class.java)
}
@Test
fun getSfpsRestToUnlockFeature_returnDefaultImpl() {
assertThat(mFingerprintFeatureProviderImpl.getSfpsRestToUnlockFeature(mContext))
.isInstanceOf(SfpsRestToUnlockFeatureImpl::class.java)
}
}

View File

@@ -0,0 +1,204 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics.fingerprint;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.os.UserHandle;
import android.os.UserManager;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.testutils.ResourcesUtils;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.utils.StringUtil;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
@RunWith(AndroidJUnit4.class)
public class FingerprintStatusUtilsTest {
private static final ComponentName COMPONENT_NAME =
new ComponentName("package", "class");
private static final int USER_ID = UserHandle.myUserId();
private static final UserHandle USER_HANDLE = new UserHandle(USER_ID);
@Mock
private PackageManager mPackageManager;
@Mock
private DevicePolicyManager mDevicePolicyManager;
@Mock
private FingerprintManager mFingerprintManager;
@Mock
private FaceManager mFaceManager;
private Context mApplicationContext;
private FingerprintStatusUtils mFingerprintStatusUtils;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mApplicationContext = spy(ApplicationProvider.getApplicationContext());
when(mApplicationContext.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
when(mDevicePolicyManager.getProfileOwnerOrDeviceOwnerSupervisionComponent(USER_HANDLE))
.thenReturn(COMPONENT_NAME);
when(mApplicationContext.getSystemService(Context.FINGERPRINT_SERVICE))
.thenReturn(mFingerprintManager);
when(mApplicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE))
.thenReturn(mDevicePolicyManager);
when(mApplicationContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager);
mFingerprintStatusUtils =
new FingerprintStatusUtils(mApplicationContext, mFingerprintManager, USER_ID);
}
@Test
public void isAvailable_withoutFingerprint_withoutFace_returnsFalse() {
when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
when(mFaceManager.isHardwareDetected()).thenReturn(false);
assertThat(mFingerprintStatusUtils.isAvailable()).isFalse();
}
@Test
public void isAvailable_withoutFingerprint_withFace_returnsFalse() {
when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
when(mFaceManager.isHardwareDetected()).thenReturn(true);
assertThat(mFingerprintStatusUtils.isAvailable()).isFalse();
}
@Test
public void isAvailable_withFingerprint_withoutFace_returnsTrue() {
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFaceManager.isHardwareDetected()).thenReturn(false);
assertThat(mFingerprintStatusUtils.isAvailable()).isTrue();
}
@Test
public void isAvailable_withFingerprint_withFace_returnsFalse() {
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFaceManager.isHardwareDetected()).thenReturn(true);
assertThat(mFingerprintStatusUtils.isAvailable()).isFalse();
}
@Test
public void hasEnrolled_withEnrolledFingerprints_returnsTrue() {
when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true);
assertThat(mFingerprintStatusUtils.hasEnrolled()).isTrue();
}
@Test
public void hasEnrolled_withoutEnrolledFingerprints_returnsFalse() {
when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false);
assertThat(mFingerprintStatusUtils.hasEnrolled()).isFalse();
}
@Test
public void getDisabledAdmin_whenFingerprintDisabled_returnsEnforcedAdmin() {
when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME))
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
final RestrictedLockUtils.EnforcedAdmin admin =
mFingerprintStatusUtils.getDisablingAdmin();
assertThat(admin).isEqualTo(new RestrictedLockUtils.EnforcedAdmin(
COMPONENT_NAME, UserManager.DISALLOW_BIOMETRIC, USER_HANDLE));
}
@Test
public void getDisabledAdmin_withFingerprintEnabled_returnsNull() {
when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0);
assertThat(mFingerprintStatusUtils.getDisablingAdmin()).isNull();
}
@Test
public void getSummary_whenNotEnrolled_returnsSummaryNone() {
when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
assertThat(mFingerprintStatusUtils.getSummary())
.isEqualTo(ResourcesUtils.getResourcesString(
mApplicationContext,
"security_settings_fingerprint_preference_summary_none"));
}
@Test
public void getSummary_whenEnrolled_returnsSummary() {
final int enrolledFingerprintsCount = 2;
final int stringResId = ResourcesUtils.getResourcesId(
ApplicationProvider.getApplicationContext(), "string",
"security_settings_fingerprint_preference_summary");
final String summary = StringUtil.getIcuPluralsString(mApplicationContext,
enrolledFingerprintsCount, stringResId);
when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true);
when(mFingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(
createFingerprintList(enrolledFingerprintsCount));
assertThat(mFingerprintStatusUtils.getSummary()).isEqualTo(summary);
}
@Test
public void getSettingsClassName_whenNotEnrolled_returnsFingerprintSettings() {
when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false);
assertThat(mFingerprintStatusUtils.getSettingsClassName())
.isEqualTo(FingerprintSettings.class.getName());
}
@Test
public void getSettingsClassName_whenEnrolled_returnsFingerprintSettings() {
when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true);
assertThat(mFingerprintStatusUtils.getSettingsClassName())
.isEqualTo(FingerprintSettings.class.getName());
}
private List<Fingerprint> createFingerprintList(int size) {
final List<Fingerprint> fingerprintList = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
fingerprintList.add(new Fingerprint("fingerprint" + i, 0, 0));
}
return fingerprintList;
}
}

View File

@@ -0,0 +1,189 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics.fingerprint;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.content.Intent;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.os.CancellationSignal;
import androidx.annotation.Nullable;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class FingerprintUpdaterTest {
private static final byte[] HARDWARE_AUTH_TOKEN = new byte[] {0};
private static final CancellationSignal CANCELLATION_SIGNAL = new CancellationSignal();
private static final int USER_ID = 0;
private static final int ENROLL_REASON = 0;
private static final int ERR_MSG_ID = 0;
private static final int HELP_MSG_ID = 0;
private static final String HELP_STRING = "";
private static final String ERR_STRING = "";
private static final Fingerprint FINGERPRINT =
new Fingerprint(/* name= */"", /* fingerId */ 0, /* deviceId= */ 0L);
@Mock private FingerprintManager mFingerprintManager;
@Mock private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper;
private FingerprintUpdater mFingerprintUpdater;
private Context mContext;
private FingerprintManager.EnrollmentCallback mEnrollmentCallback;
private FingerprintManager.RemovalCallback mRemovalCallback;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = ApplicationProvider.getApplicationContext();
mFingerprintUpdater = new FingerprintUpdater(mContext, mFingerprintManager);
mEnrollmentCallback = spy(new TestEntrollmentCallback());
mRemovalCallback = spy(new TestRemovalCallback());
SafetyCenterManagerWrapper.sInstance = mSafetyCenterManagerWrapper;
}
@Test
public void enroll_onEnrollmentCallbacks_triggerGivenCallback() {
ArgumentCaptor<FingerprintManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FingerprintManager.EnrollmentCallback.class);
mFingerprintUpdater.enroll(HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, USER_ID,
mEnrollmentCallback, ENROLL_REASON, new Intent());
verify(mFingerprintManager).enroll(
same(HARDWARE_AUTH_TOKEN),
same(CANCELLATION_SIGNAL),
eq(USER_ID),
callbackCaptor.capture(),
eq(ENROLL_REASON),
any());
FingerprintManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentError(ERR_MSG_ID, ERR_STRING);
callback.onEnrollmentProgress(/* remaining= */ 2);
callback.onEnrollmentHelp(HELP_MSG_ID, HELP_STRING);
verify(mEnrollmentCallback).onEnrollmentError(ERR_MSG_ID, ERR_STRING);
verify(mEnrollmentCallback).onEnrollmentProgress(/* remaining= */ 2);
verify(mEnrollmentCallback).onEnrollmentHelp(HELP_MSG_ID, HELP_STRING);
}
@Test
public void enroll_onEnrollmentSuccess_invokedInteractionWithSafetyCenter() {
ArgumentCaptor<FingerprintManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FingerprintManager.EnrollmentCallback.class);
mFingerprintUpdater.enroll(HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, USER_ID,
mEnrollmentCallback, ENROLL_REASON, new Intent());
verify(mFingerprintManager).enroll(
same(HARDWARE_AUTH_TOKEN),
same(CANCELLATION_SIGNAL),
eq(USER_ID),
callbackCaptor.capture(),
eq(ENROLL_REASON),
any());
FingerprintManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentProgress(/* remaining= */ 0);
verify(mSafetyCenterManagerWrapper).isEnabled(mContext);
}
@Test
public void enroll_onEnrollmentNotYetFinished_didntInvokeInteractionWithSafetyCenter() {
ArgumentCaptor<FingerprintManager.EnrollmentCallback> callbackCaptor =
ArgumentCaptor.forClass(FingerprintManager.EnrollmentCallback.class);
mFingerprintUpdater.enroll(HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, USER_ID,
mEnrollmentCallback, ENROLL_REASON, new Intent());
verify(mFingerprintManager).enroll(
same(HARDWARE_AUTH_TOKEN),
same(CANCELLATION_SIGNAL),
eq(USER_ID),
callbackCaptor.capture(),
eq(ENROLL_REASON),
any());
FingerprintManager.EnrollmentCallback callback = callbackCaptor.getValue();
callback.onEnrollmentProgress(/* remaining= */ 1);
verify(mSafetyCenterManagerWrapper, never()).isEnabled(any());
}
@Test
public void remove_onRemovalCallbacks_triggerGivenCallback() {
ArgumentCaptor<FingerprintManager.RemovalCallback> callbackCaptor =
ArgumentCaptor.forClass(FingerprintManager.RemovalCallback.class);
mFingerprintUpdater.remove(FINGERPRINT, USER_ID, mRemovalCallback);
verify(mFingerprintManager)
.remove(same(FINGERPRINT), eq(USER_ID), callbackCaptor.capture());
FingerprintManager.RemovalCallback callback = callbackCaptor.getValue();
callback.onRemovalSucceeded(FINGERPRINT, /* remaining= */ 1);
callback.onRemovalError(FINGERPRINT, ERR_MSG_ID, ERR_STRING);
verify(mRemovalCallback).onRemovalSucceeded(any(), eq(1));
verify(mRemovalCallback).onRemovalError(FINGERPRINT, ERR_MSG_ID, ERR_STRING);
}
@Test
public void remove_onRemovalSuccess_invokedInteractionWithSafetyCenter() {
ArgumentCaptor<FingerprintManager.RemovalCallback> callbackCaptor =
ArgumentCaptor.forClass(FingerprintManager.RemovalCallback.class);
mFingerprintUpdater.remove(FINGERPRINT, USER_ID, mRemovalCallback);
verify(mFingerprintManager)
.remove(same(FINGERPRINT), eq(USER_ID), callbackCaptor.capture());
FingerprintManager.RemovalCallback callback = callbackCaptor.getValue();
callback.onRemovalSucceeded(FINGERPRINT, /* remaining= */ 0);
verify(mSafetyCenterManagerWrapper).isEnabled(mContext);
}
public static class TestEntrollmentCallback extends FingerprintManager.EnrollmentCallback {
@Override
public void onEnrollmentError(int errMsgId, CharSequence errString) {}
@Override
public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {}
@Override
public void onEnrollmentProgress(int remaining) {}
}
public static class TestRemovalCallback extends FingerprintManager.RemovalCallback {
@Override
public void onRemovalError(Fingerprint fp, int errMsgId, CharSequence errString) {}
@Override
public void onRemovalSucceeded(@Nullable Fingerprint fp, int remaining) {}
}
}

View File

@@ -0,0 +1,176 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics.fingerprint.feature
import android.animation.Animator
import android.content.Context
import android.hardware.fingerprint.FingerprintManager
import android.view.View
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.SFPS_STAGE_CENTER
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.SFPS_STAGE_FINGERTIP
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.SFPS_STAGE_LEFT_EDGE
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.SFPS_STAGE_NO_ANIMATION
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.SFPS_STAGE_RIGHT_EDGE
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.STAGE_UNKNOWN
import com.google.common.truth.Truth.assertThat
import kotlin.math.roundToInt
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
import org.mockito.Spy
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidJUnit4::class)
class SfpsEnrollmentFeatureImplTest {
private val STAGE_0 = 0
private val STAGE_1 = 1
private val STAGE_2 = 2
private val STAGE_3 = 3
private val STAGE_4 = 4
private val THRESHOLD_0 = 0f
private val THRESHOLD_1 = .36f
private val THRESHOLD_2 = .52f
private val THRESHOLD_3 = .76f
private val THRESHOLD_4 = 1f
@get:Rule
val mockito: MockitoRule = MockitoJUnit.rule()
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Mock
private lateinit var mockFingerprintManager: FingerprintManager
private val mSfpsEnrollmentFeatureImpl: SfpsEnrollmentFeatureImpl = SfpsEnrollmentFeatureImpl()
@Before
fun setUp() {
whenever(context.getSystemService(FingerprintManager::class.java))
.thenReturn(mockFingerprintManager)
doReturn(THRESHOLD_0).`when`(mockFingerprintManager).getEnrollStageThreshold(STAGE_0)
doReturn(THRESHOLD_1).`when`(mockFingerprintManager).getEnrollStageThreshold(STAGE_1)
doReturn(THRESHOLD_2).`when`(mockFingerprintManager).getEnrollStageThreshold(STAGE_2)
doReturn(THRESHOLD_3).`when`(mockFingerprintManager).getEnrollStageThreshold(STAGE_3)
doReturn(THRESHOLD_4).`when`(mockFingerprintManager).getEnrollStageThreshold(STAGE_4)
}
@Test
fun testGetEnrollStageThreshold() {
assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, STAGE_0))
.isEqualTo(THRESHOLD_0)
assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, STAGE_1))
.isEqualTo(THRESHOLD_1)
assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, STAGE_2))
.isEqualTo(THRESHOLD_2)
assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, STAGE_3))
.isEqualTo(THRESHOLD_3)
assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, STAGE_4))
.isEqualTo(THRESHOLD_4)
}
@Test
fun testGetHelpAnimator() {
val mockView: View = mock(View::class.java)
val animator: Animator = mSfpsEnrollmentFeatureImpl.getHelpAnimator(mockView)
assertThat(animator.duration).isEqualTo(SfpsEnrollmentFeatureImpl.HELP_ANIMATOR_DURATION)
}
@Test
fun testGetCurrentSfpsEnrollStage() {
assertThat(mSfpsEnrollmentFeatureImpl.getCurrentSfpsEnrollStage(0, null))
.isEqualTo(STAGE_UNKNOWN)
val mapper = { i: Int ->
(25 * mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, i)).roundToInt()
}
assertThat(mSfpsEnrollmentFeatureImpl.getCurrentSfpsEnrollStage(-1, mapper))
.isEqualTo(SFPS_STAGE_NO_ANIMATION)
assertThat(mSfpsEnrollmentFeatureImpl.getCurrentSfpsEnrollStage(0, mapper))
.isEqualTo(SFPS_STAGE_CENTER)
assertThat(mSfpsEnrollmentFeatureImpl.getCurrentSfpsEnrollStage(9, mapper))
.isEqualTo(SFPS_STAGE_FINGERTIP)
assertThat(mSfpsEnrollmentFeatureImpl.getCurrentSfpsEnrollStage(13, mapper))
.isEqualTo(SFPS_STAGE_LEFT_EDGE)
assertThat(mSfpsEnrollmentFeatureImpl.getCurrentSfpsEnrollStage(19, mapper))
.isEqualTo(SFPS_STAGE_RIGHT_EDGE)
assertThat(mSfpsEnrollmentFeatureImpl.getCurrentSfpsEnrollStage(25, mapper))
.isEqualTo(SFPS_STAGE_RIGHT_EDGE)
}
@Test
fun testGetFeaturedStageHeaderResource() {
val type = "string"
assertThat(
mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_NO_ANIMATION)
).isEqualTo(
getSettingsResourcesId(type, "security_settings_fingerprint_enroll_repeat_title")
)
assertThat(
mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_CENTER)
).isEqualTo(
getSettingsResourcesId(type, "security_settings_sfps_enroll_finger_center_title")
)
assertThat(
mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_FINGERTIP)
).isEqualTo(
getSettingsResourcesId(type, "security_settings_sfps_enroll_fingertip_title")
)
assertThat(
mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_LEFT_EDGE)
).isEqualTo(
getSettingsResourcesId(type, "security_settings_sfps_enroll_left_edge_title")
)
assertThat(
mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_RIGHT_EDGE)
).isEqualTo(
getSettingsResourcesId(type, "security_settings_sfps_enroll_right_edge_title")
)
}
@Test
fun testGetSfpsEnrollLottiePerStage() {
val type = "raw"
assertThat(
mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_NO_ANIMATION)
).isEqualTo(getSettingsResourcesId(type, "sfps_lottie_no_animation"))
assertThat(
mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_CENTER)
).isEqualTo(getSettingsResourcesId(type, "sfps_lottie_pad_center"))
assertThat(
mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_FINGERTIP)
).isEqualTo(getSettingsResourcesId(type, "sfps_lottie_tip"))
assertThat(
mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_LEFT_EDGE)
).isEqualTo(getSettingsResourcesId(type, "sfps_lottie_left_edge"))
assertThat(
mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_RIGHT_EDGE)
).isEqualTo(getSettingsResourcesId(type, "sfps_lottie_right_edge"))
}
private fun getSettingsResourcesId(type: String, name: String) : Int {
return context.resources.getIdentifier(name, type, context.packageName)
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics.fingerprint.feature
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.RestrictedSwitchPreference
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito
@RunWith(AndroidJUnit4::class)
class SfpsRestToUnlockFeatureImplTest {
private lateinit var mContext: Context
private lateinit var mSfpsRestToUnlockFeatureImpl: SfpsRestToUnlockFeatureImpl
private lateinit var mRestrictedSwitchPreferenceSpy: RestrictedSwitchPreference
@Before
fun setUp() {
mContext = ApplicationProvider.getApplicationContext()
mSfpsRestToUnlockFeatureImpl = SfpsRestToUnlockFeatureImpl()
mRestrictedSwitchPreferenceSpy = Mockito.spy(RestrictedSwitchPreference(mContext))
}
@Test
fun getDescriptionForSfps_isNotNull() {
assertThat(mSfpsRestToUnlockFeatureImpl)
.isInstanceOf(SfpsRestToUnlockFeatureImpl::class.java)
assertThat(mSfpsRestToUnlockFeatureImpl.getDescriptionForSfps(mContext))
.isNotNull()
}
@Test
fun getRestToUnlockLayout_isNull() {
assertThat(mSfpsRestToUnlockFeatureImpl)
.isInstanceOf(SfpsRestToUnlockFeatureImpl::class.java)
assertThat(mSfpsRestToUnlockFeatureImpl.getRestToUnlockLayout(mContext))
.isNull()
}
@Test
fun fingerprint_settings_setupFingerprintUnlockCategoryPreferences() {
assertThat(mSfpsRestToUnlockFeatureImpl.getRestToUnlockPreference(mContext))
.isNull()
}
}

View File

@@ -0,0 +1 @@
include /src/com/android/settings/biometrics/OWNERS

View File

@@ -0,0 +1,203 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics2.repository;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_HOME_BUTTON;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UNKNOWN;
import static com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.newFingerprintRepository;
import static com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.setupFingerprintEnrolledFingerprints;
import static com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.setupSuwMaxFingerprintsEnrollable;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.biometrics.SensorProperties;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.biometrics2.data.repository.FingerprintRepository;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
@RunWith(AndroidJUnit4.class)
public class FingerprintRepositoryTest {
@Rule public final MockitoRule mockito = MockitoJUnit.rule();
@Mock private Resources mResources;
@Mock private FingerprintManager mFingerprintManager;
private Context mContext;
@Before
public void setUp() {
mContext = ApplicationProvider.getApplicationContext();
}
@Test
public void testCanAssumeSensorType_forUnknownSensor() {
final FingerprintRepository repository = newFingerprintRepository(mFingerprintManager,
TYPE_UNKNOWN, 1);
assertThat(repository.canAssumeUdfps()).isFalse();
assertThat(repository.canAssumeSfps()).isFalse();
}
@Test
public void testCanAssumeSensorType_forRearSensor() {
final FingerprintRepository repository = newFingerprintRepository(mFingerprintManager,
TYPE_REAR, 1);
assertThat(repository.canAssumeUdfps()).isFalse();
assertThat(repository.canAssumeSfps()).isFalse();
}
@Test
public void testCanAssumeSensorType_forUdfpsUltrasonicSensor() {
final FingerprintRepository repository = newFingerprintRepository(mFingerprintManager,
TYPE_UDFPS_ULTRASONIC, 1);
assertThat(repository.canAssumeUdfps()).isTrue();
assertThat(repository.canAssumeSfps()).isFalse();
}
@Test
public void testCanAssumeSensorType_forUdfpsOpticalSensor() {
final FingerprintRepository repository = newFingerprintRepository(mFingerprintManager,
TYPE_UDFPS_OPTICAL, 1);
assertThat(repository.canAssumeUdfps()).isTrue();
assertThat(repository.canAssumeSfps()).isFalse();
}
@Test
public void testCanAssumeSensorType_forPowerButtonSensor() {
final FingerprintRepository repository = newFingerprintRepository(mFingerprintManager,
TYPE_POWER_BUTTON, 1);
assertThat(repository.canAssumeUdfps()).isFalse();
assertThat(repository.canAssumeSfps()).isTrue();
}
@Test
public void testCanAssumeSensorType_forHomeButtonSensor() {
final FingerprintRepository repository = newFingerprintRepository(mFingerprintManager,
TYPE_HOME_BUTTON, 1);
assertThat(repository.canAssumeUdfps()).isFalse();
assertThat(repository.canAssumeSfps()).isFalse();
}
@Test
public void testGetMaxFingerprints() {
final FingerprintRepository repository = newFingerprintRepository(mFingerprintManager,
TYPE_UNKNOWN, 999);
assertThat(repository.getMaxFingerprints()).isEqualTo(999);
}
@Test
public void testGetNumOfEnrolledFingerprintsSize() {
final FingerprintRepository repository = newFingerprintRepository(mFingerprintManager,
TYPE_UNKNOWN, 999);
setupFingerprintEnrolledFingerprints(mFingerprintManager, 10, 3);
setupFingerprintEnrolledFingerprints(mFingerprintManager, 22, 99);
assertThat(repository.getNumOfEnrolledFingerprintsSize(10)).isEqualTo(3);
assertThat(repository.getNumOfEnrolledFingerprintsSize(22)).isEqualTo(99);
}
@Test
public void testGetMaxFingerprintsInSuw() {
final FingerprintRepository repository = newFingerprintRepository(mFingerprintManager,
TYPE_UNKNOWN, 999);
setupSuwMaxFingerprintsEnrollable(mContext, mResources, 333);
assertThat(repository.getMaxFingerprintsInSuw(mResources))
.isEqualTo(333);
setupSuwMaxFingerprintsEnrollable(mContext, mResources, 20);
assertThat(repository.getMaxFingerprintsInSuw(mResources)).isEqualTo(20);
}
@Test
public void testGetFirstFingerprintSensorPropertiesInternal() {
final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
final FingerprintSensorPropertiesInternal prop = new FingerprintSensorPropertiesInternal(
0 /* sensorId */,
SensorProperties.STRENGTH_STRONG,
5,
new ArrayList<>() /* componentInfo */,
TYPE_UDFPS_OPTICAL,
true /* resetLockoutRequiresHardwareAuthToken */);
props.add(prop);
doAnswer(invocation -> {
final IFingerprintAuthenticatorsRegisteredCallback callback =
invocation.getArgument(0);
callback.onAllAuthenticatorsRegistered(props);
return null;
}).when(mFingerprintManager).addAuthenticatorsRegisteredCallback(any());
final FingerprintRepository repository = new FingerprintRepository(mFingerprintManager);
assertThat(repository.getFirstFingerprintSensorPropertiesInternal()).isEqualTo(prop);
}
@Test
public void testGetEnrollStageCount() {
final FingerprintRepository repository = newFingerprintRepository(mFingerprintManager,
TYPE_UNKNOWN, 999);
final int expectedValue = 24;
doReturn(expectedValue).when(mFingerprintManager).getEnrollStageCount();
assertThat(repository.getEnrollStageCount()).isEqualTo(expectedValue);
}
@Test
public void testGetEnrollStageThreshold() {
final FingerprintRepository repository = newFingerprintRepository(mFingerprintManager,
TYPE_UNKNOWN, 999);
final float expectedValue0 = 0.42f;
final float expectedValue1 = 0.24f;
final float expectedValue2 = 0.33f;
final float expectedValue3 = 0.90f;
doReturn(expectedValue0).when(mFingerprintManager).getEnrollStageThreshold(0);
doReturn(expectedValue1).when(mFingerprintManager).getEnrollStageThreshold(1);
doReturn(expectedValue2).when(mFingerprintManager).getEnrollStageThreshold(2);
doReturn(expectedValue3).when(mFingerprintManager).getEnrollStageThreshold(3);
assertThat(repository.getEnrollStageThreshold(2)).isEqualTo(expectedValue2);
assertThat(repository.getEnrollStageThreshold(1)).isEqualTo(expectedValue1);
assertThat(repository.getEnrollStageThreshold(3)).isEqualTo(expectedValue3);
assertThat(repository.getEnrollStageThreshold(0)).isEqualTo(expectedValue0);
}
}

View File

@@ -0,0 +1,136 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics2.ui.model
import android.content.Intent
import android.os.Bundle
import android.os.SystemClock
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.biometrics.BiometricEnrollBase
import com.android.settings.password.ChooseLockSettingsHelper
import com.google.common.truth.Truth
import org.junit.Test
import org.junit.runner.RunWith
import java.util.Arrays
@RunWith(AndroidJUnit4::class)
class CredentialModelTest {
private val clock = SystemClock.elapsedRealtimeClock()
@Test
fun testNullBundle() {
val credentialModel = CredentialModel(null, clock)
Truth.assertThat(credentialModel.userId).isEqualTo(UserHandle.myUserId())
}
companion object {
@JvmStatic
fun newCredentialModelIntentExtras(
userId: Int, challenge: Long,
token: ByteArray?, gkPwHandle: Long
): Bundle {
val bundle = Bundle()
bundle.putInt(Intent.EXTRA_USER_ID, userId)
bundle.putLong(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, challenge)
bundle.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token)
bundle.putLong(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle)
return bundle
}
@JvmStatic
fun newValidTokenCredentialIntentExtras(userId: Int): Bundle {
return newCredentialModelIntentExtras(
userId, 1L, byteArrayOf(0, 1, 2),
CredentialModel.INVALID_GK_PW_HANDLE
)
}
@JvmStatic
fun newOnlySensorValidCredentialIntentExtras(userId: Int): Bundle {
return newCredentialModelIntentExtras(
userId, CredentialModel.INVALID_CHALLENGE, null,
CredentialModel.INVALID_GK_PW_HANDLE
)
}
@JvmStatic
fun newGkPwHandleCredentialIntentExtras(userId: Int, gkPwHandle: Long): Bundle {
return newCredentialModelIntentExtras(
userId,
CredentialModel.INVALID_CHALLENGE,
null,
gkPwHandle
)
}
private fun checkBundleLongValue(
bundle1: Bundle, bundle2: Bundle,
key: String
) {
if (!bundle1.containsKey(key)) {
return
}
val value1 = bundle1.getInt(key)
val value2 = bundle2.getInt(key)
Truth.assertWithMessage(
"bundle not match, key:" + key + ", value1:" + value1 + ", value2:"
+ value2
).that(value1).isEqualTo(value2)
}
private fun checkBundleIntValue(
bundle1: Bundle, bundle2: Bundle,
key: String
) {
if (!bundle1.containsKey(key)) {
return
}
val value1 = bundle1.getLong(key)
val value2 = bundle2.getLong(key)
Truth.assertWithMessage(
"bundle not match, key:" + key + ", value1:" + value1 + ", value2:"
+ value2
).that(value1).isEqualTo(value2)
}
private fun checkBundleByteArrayValue(
bundle1: Bundle, bundle2: Bundle,
key: String
) {
if (!bundle1.containsKey(key)) {
return
}
val value1 = bundle1.getByteArray(key)
val value2 = bundle2.getByteArray(key)
val errMsg = ("bundle not match, key:" + key + ", value1:" + Arrays.toString(value1)
+ ", value2:" + Arrays.toString(value2))
if (value1 == null) {
Truth.assertWithMessage(errMsg).that(value2).isNull()
} else {
Truth.assertWithMessage(errMsg).that(value1.size).isEqualTo(
value2!!.size
)
for (i in value1.indices) {
Truth.assertWithMessage(errMsg).that(value1[i]).isEqualTo(
value2[i]
)
}
}
}
}
}

View File

@@ -0,0 +1,175 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics2.ui.model
import android.content.Context
import android.content.Intent
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.biometrics.BiometricEnrollActivity
import com.google.android.setupcompat.util.WizardManagerHelper
import com.google.common.truth.Truth
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class EnrollmentRequestTest {
private val context = ApplicationProvider.getApplicationContext<Context>()
@Test
fun testIsSuw() {
// Default false
Truth.assertThat(EnrollmentRequest(Intent(), context, true).isSuw).isFalse()
Truth.assertThat(EnrollmentRequest(Intent(), context, false).isSuw).isFalse()
val trueIntent = Intent()
trueIntent.putExtra(WizardManagerHelper.EXTRA_IS_SETUP_FLOW, true)
Truth.assertThat(EnrollmentRequest(trueIntent, context, true).isSuw).isTrue()
Truth.assertThat(EnrollmentRequest(trueIntent, context, false).isSuw).isFalse()
val falseIntent = Intent()
trueIntent.putExtra(WizardManagerHelper.EXTRA_IS_SETUP_FLOW, false)
Truth.assertThat(EnrollmentRequest(falseIntent, context, true).isSuw).isFalse()
Truth.assertThat(EnrollmentRequest(falseIntent, context, false).isSuw).isFalse()
}
@Test
fun testIsAfterSuwOrSuwSuggestedAction() {
// Default false
Truth.assertThat(
EnrollmentRequest(Intent(), context, true)
.isAfterSuwOrSuwSuggestedAction
).isFalse()
Truth.assertThat(
EnrollmentRequest(Intent(), context, false)
.isAfterSuwOrSuwSuggestedAction
).isFalse()
val deferredTrueIntent = Intent()
deferredTrueIntent.putExtra(WizardManagerHelper.EXTRA_IS_DEFERRED_SETUP, true)
Truth.assertThat(
EnrollmentRequest(deferredTrueIntent, context, true)
.isAfterSuwOrSuwSuggestedAction
).isTrue()
Truth.assertThat(
EnrollmentRequest(deferredTrueIntent, context, false)
.isAfterSuwOrSuwSuggestedAction
).isFalse()
val deferredFalseIntent = Intent()
deferredFalseIntent.putExtra(WizardManagerHelper.EXTRA_IS_DEFERRED_SETUP, false)
Truth.assertThat(
EnrollmentRequest(deferredFalseIntent, context, false)
.isAfterSuwOrSuwSuggestedAction
).isFalse()
Truth.assertThat(
EnrollmentRequest(deferredFalseIntent, context, false)
.isAfterSuwOrSuwSuggestedAction
).isFalse()
val portalTrueIntent = Intent()
portalTrueIntent.putExtra(WizardManagerHelper.EXTRA_IS_PORTAL_SETUP, true)
Truth.assertThat(
EnrollmentRequest(portalTrueIntent, context, true)
.isAfterSuwOrSuwSuggestedAction
).isTrue()
Truth.assertThat(
EnrollmentRequest(portalTrueIntent, context, false)
.isAfterSuwOrSuwSuggestedAction
).isFalse()
val portalFalseIntent = Intent()
portalFalseIntent.putExtra(WizardManagerHelper.EXTRA_IS_PORTAL_SETUP, false)
Truth.assertThat(
EnrollmentRequest(portalFalseIntent, context, false)
.isAfterSuwOrSuwSuggestedAction
).isFalse()
Truth.assertThat(
EnrollmentRequest(portalFalseIntent, context, false)
.isAfterSuwOrSuwSuggestedAction
).isFalse()
val suggestedTrueIntent = Intent()
suggestedTrueIntent.putExtra(WizardManagerHelper.EXTRA_IS_SUW_SUGGESTED_ACTION_FLOW, true)
Truth.assertThat(
EnrollmentRequest(suggestedTrueIntent, context, true)
.isAfterSuwOrSuwSuggestedAction
).isTrue()
Truth.assertThat(
EnrollmentRequest(suggestedTrueIntent, context, false)
.isAfterSuwOrSuwSuggestedAction
).isFalse()
val suggestedFalseIntent = Intent()
suggestedFalseIntent.putExtra(WizardManagerHelper.EXTRA_IS_SUW_SUGGESTED_ACTION_FLOW, false)
Truth.assertThat(
EnrollmentRequest(suggestedFalseIntent, context, false)
.isAfterSuwOrSuwSuggestedAction
).isFalse()
Truth.assertThat(
EnrollmentRequest(suggestedFalseIntent, context, false)
.isAfterSuwOrSuwSuggestedAction
).isFalse()
}
@Test
fun testGetSuwExtras_inSuw() {
val suwIntent = Intent()
suwIntent.putExtra(WizardManagerHelper.EXTRA_IS_SETUP_FLOW, true)
val setupRequest = EnrollmentRequest(suwIntent, context, true)
val bundle = setupRequest.suwExtras
Truth.assertThat(bundle).isNotNull()
Truth.assertThat(bundle.size()).isAtLeast(1)
Truth.assertThat(bundle.getBoolean(WizardManagerHelper.EXTRA_IS_SETUP_FLOW)).isTrue()
}
@Test
fun testGetSuwExtras_notInSuw() {
val suwIntent = Intent()
suwIntent.putExtra(WizardManagerHelper.EXTRA_IS_SETUP_FLOW, true)
val setupRequest = EnrollmentRequest(suwIntent, context, false)
val bundle = setupRequest.suwExtras
Truth.assertThat(bundle).isNotNull()
Truth.assertThat(bundle.size()).isEqualTo(0)
}
@Test
fun testIsSkipIntro() {
// Default false
Truth.assertThat(EnrollmentRequest(Intent(), context, true).isSkipIntro).isFalse()
Truth.assertThat(EnrollmentRequest(Intent(), context, false).isSkipIntro).isFalse()
val trueIntent = Intent()
trueIntent.putExtra(BiometricEnrollActivity.EXTRA_SKIP_INTRO, true)
Truth.assertThat(EnrollmentRequest(trueIntent, context, true).isSkipIntro).isTrue()
Truth.assertThat(EnrollmentRequest(trueIntent, context, false).isSkipIntro).isTrue()
val falseIntent = Intent()
falseIntent.putExtra(BiometricEnrollActivity.EXTRA_SKIP_INTRO, false)
Truth.assertThat(EnrollmentRequest(falseIntent, context, false).isSkipIntro).isFalse()
Truth.assertThat(EnrollmentRequest(falseIntent, context, false).isSkipIntro).isFalse()
}
@Test
fun testIsSkipFindSensor() {
// Default false
Truth.assertThat(EnrollmentRequest(Intent(), context, true).isSkipFindSensor)
.isFalse()
Truth.assertThat(EnrollmentRequest(Intent(), context, false).isSkipFindSensor)
.isFalse()
val trueIntent = Intent()
trueIntent.putExtra(EnrollmentRequest.EXTRA_SKIP_FIND_SENSOR, true)
Truth.assertThat(EnrollmentRequest(trueIntent, context, true).isSkipFindSensor).isTrue()
Truth.assertThat(EnrollmentRequest(trueIntent, context, false).isSkipFindSensor).isTrue()
val falseIntent = Intent()
falseIntent.putExtra(EnrollmentRequest.EXTRA_SKIP_FIND_SENSOR, false)
Truth.assertThat(EnrollmentRequest(falseIntent, context, false).isSkipFindSensor)
.isFalse()
Truth.assertThat(EnrollmentRequest(falseIntent, context, false).isSkipFindSensor)
.isFalse()
}
}

View File

@@ -0,0 +1,519 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics2.ui.viewmodel
import android.app.Activity
import android.app.admin.DevicePolicyManager
import android.content.Intent
import android.os.Bundle
import android.os.SystemClock
import android.os.UserHandle
import androidx.activity.result.ActivityResult
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.VerifyCredentialResponse
import com.android.settings.biometrics.BiometricEnrollBase
import com.android.settings.biometrics2.ui.model.CredentialModel
import com.android.settings.biometrics2.ui.model.CredentialModelTest.Companion.newGkPwHandleCredentialIntentExtras
import com.android.settings.biometrics2.ui.model.CredentialModelTest.Companion.newOnlySensorValidCredentialIntentExtras
import com.android.settings.biometrics2.ui.model.CredentialModelTest.Companion.newValidTokenCredentialIntentExtras
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.ChallengeGenerator
import com.android.settings.password.ChooseLockPattern
import com.android.settings.password.ChooseLockSettingsHelper
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.atomic.AtomicBoolean
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidJUnit4::class)
class AutoCredentialViewModelTest {
@get:Rule val mockito: MockitoRule = MockitoJUnit.rule()
@Mock private lateinit var lockPatternUtils: LockPatternUtils
private var challengeGenerator: TestChallengeGenerator = TestChallengeGenerator()
private lateinit var viewModel: AutoCredentialViewModel
private fun newAutoCredentialViewModel(bundle: Bundle?): AutoCredentialViewModel {
return AutoCredentialViewModel(
ApplicationProvider.getApplicationContext(),
lockPatternUtils,
challengeGenerator,
CredentialModel(bundle, SystemClock.elapsedRealtimeClock())
)
}
@Before
fun setUp() {
challengeGenerator = TestChallengeGenerator()
}
private fun setupGenerateChallenge(userId: Int, newSensorId: Int, newChallenge: Long) {
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
)
challengeGenerator.userId = userId
challengeGenerator.sensorId = newSensorId
challengeGenerator.challenge = newChallenge
}
@Test
fun testCheckCredential_validCredentialCase() = runTest {
val userId = 99
viewModel = newAutoCredentialViewModel(newValidTokenCredentialIntentExtras(userId))
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
)
val generateFails = listOfGenerateChallengeFailedFlow()
// Run credential check
val action = viewModel.checkCredential(backgroundScope)
runCurrent()
// Check viewModel behavior
assertThat(action).isEqualTo(CredentialAction.CREDENTIAL_VALID)
assertThat(generateFails.size).isEqualTo(0)
// Check createGeneratingChallengeExtras()
assertThat(viewModel.createGeneratingChallengeExtras()).isNull()
}
@Test
fun testCheckCredential_needToChooseLock() = runTest {
val userId = 100
viewModel = newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED
)
val generateFails = listOfGenerateChallengeFailedFlow()
// Run credential check
val action = viewModel.checkCredential(backgroundScope)
runCurrent()
// Check viewModel behavior
assertThat(action).isEqualTo(CredentialAction.FAIL_NEED_TO_CHOOSE_LOCK)
assertThat(generateFails.size).isEqualTo(0)
// Check createGeneratingChallengeExtras()
assertThat(viewModel.createGeneratingChallengeExtras()).isNull()
}
@Test
fun testCheckCredential_needToConfirmLockForSomething() = runTest {
val userId = 101
viewModel =
newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
)
val generateFails = listOfGenerateChallengeFailedFlow()
// Run credential check
val action = viewModel.checkCredential(backgroundScope)
runCurrent()
// Check viewModel behavior
assertThat(action).isEqualTo(CredentialAction.FAIL_NEED_TO_CONFIRM_LOCK)
assertThat(generateFails.size).isEqualTo(0)
// Check createGeneratingChallengeExtras()
assertThat(viewModel.createGeneratingChallengeExtras()).isNull()
}
@Test
fun testCheckCredential_needToConfirmLockForNumeric() = runTest {
val userId = 102
viewModel =
newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
)
val generateFails = listOfGenerateChallengeFailedFlow()
// Run credential check
val action = viewModel.checkCredential(backgroundScope)
runCurrent()
// Check viewModel behavior
assertThat(action).isEqualTo(CredentialAction.FAIL_NEED_TO_CONFIRM_LOCK)
assertThat(generateFails.size).isEqualTo(0)
// Check createGeneratingChallengeExtras()
assertThat(viewModel.createGeneratingChallengeExtras()).isNull()
}
@Test
fun testCheckCredential_needToConfirmLockForAlphabetic() = runTest {
val userId = 103
viewModel =
newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
)
val generateFails = listOfGenerateChallengeFailedFlow()
// Run credential check
val action = viewModel.checkCredential(this)
runCurrent()
// Check viewModel behavior
assertThat(action).isEqualTo(CredentialAction.FAIL_NEED_TO_CONFIRM_LOCK)
assertThat(generateFails.size).isEqualTo(0)
// Check createGeneratingChallengeExtras()
assertThat(viewModel.createGeneratingChallengeExtras()).isNull()
}
@Test
fun testCheckCredential_generateChallenge() = runTest {
val userId = 104
val gkPwHandle = 1111L
viewModel =
newAutoCredentialViewModel(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle))
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
)
val newSensorId = 10
val newChallenge = 20L
setupGenerateChallenge(userId, newSensorId, newChallenge)
whenever(
lockPatternUtils.verifyGatekeeperPasswordHandle(
gkPwHandle,
newChallenge,
userId
)
)
.thenReturn(newGoodCredential(gkPwHandle, byteArrayOf(1)))
val hasCalledRemoveGkPwHandle = AtomicBoolean()
Mockito.doAnswer {
hasCalledRemoveGkPwHandle.set(true)
null
}.`when`(lockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle)
val generateFails = listOfGenerateChallengeFailedFlow()
// Run credential check
val action = viewModel.checkCredential(backgroundScope)
runCurrent()
// Check viewModel behavior
assertThat(action).isEqualTo(CredentialAction.IS_GENERATING_CHALLENGE)
assertThat(generateFails.size).isEqualTo(0)
// Check data inside CredentialModel
assertThat(viewModel.token).isNotNull()
assertThat(challengeGenerator.callbackRunCount).isEqualTo(1)
assertThat(hasCalledRemoveGkPwHandle.get()).isFalse()
// Check createGeneratingChallengeExtras()
val generatingChallengeExtras = viewModel.createGeneratingChallengeExtras()
assertThat(generatingChallengeExtras).isNotNull()
assertThat(generatingChallengeExtras!!.getLong(BiometricEnrollBase.EXTRA_KEY_CHALLENGE))
.isEqualTo(newChallenge)
val tokens =
generatingChallengeExtras.getByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN)
assertThat(tokens).isNotNull()
assertThat(tokens!!.size).isEqualTo(1)
assertThat(tokens[0]).isEqualTo(1)
}
@Test
fun testCheckCredential_generateChallengeFail() = runTest {
backgroundScope.launch {
val userId = 104
val gkPwHandle = 1111L
viewModel =
newAutoCredentialViewModel(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle))
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
)
val newSensorId = 10
val newChallenge = 20L
setupGenerateChallenge(userId, newSensorId, newChallenge)
whenever(
lockPatternUtils.verifyGatekeeperPasswordHandle(
gkPwHandle,
newChallenge,
userId
)
)
.thenReturn(newBadCredential(0))
val generateFails = listOfGenerateChallengeFailedFlow()
// Run credential check
val action = viewModel.checkCredential(this)
runCurrent()
assertThat(action).isEqualTo(CredentialAction.IS_GENERATING_CHALLENGE)
assertThat(generateFails.size).isEqualTo(1)
assertThat(generateFails[0]).isEqualTo(true)
assertThat(challengeGenerator.callbackRunCount).isEqualTo(1)
// Check createGeneratingChallengeExtras()
assertThat(viewModel.createGeneratingChallengeExtras()).isNull()
}
}
@Test
fun testGetUserId_fromIntent() {
val userId = 106
viewModel = newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
// Get userId
assertThat(viewModel.userId).isEqualTo(userId)
}
@Test
fun testGenerateChallengeAsCredentialActivityResult_invalidChooseLock() = runTest {
backgroundScope.launch {
val userId = 107
val gkPwHandle = 3333L
viewModel =
newAutoCredentialViewModel(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle))
val intent = Intent()
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle)
val generateFails = listOfGenerateChallengeFailedFlow()
// Run generateChallengeAsCredentialActivityResult()
val ret = viewModel.generateChallengeAsCredentialActivityResult(
true,
ActivityResult(ChooseLockPattern.RESULT_FINISHED + 1, intent),
backgroundScope
)
runCurrent()
assertThat(ret).isFalse()
assertThat(generateFails.size).isEqualTo(0)
}
}
@Test
fun testGenerateChallengeAsCredentialActivityResult_invalidConfirmLock() = runTest {
backgroundScope.launch {
val userId = 107
val gkPwHandle = 3333L
viewModel =
newAutoCredentialViewModel(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle))
val intent = Intent()
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle)
val generateFails = listOfGenerateChallengeFailedFlow()
// Run generateChallengeAsCredentialActivityResult()
val ret = viewModel.generateChallengeAsCredentialActivityResult(
false,
ActivityResult(Activity.RESULT_OK + 1, intent),
backgroundScope
)
runCurrent()
assertThat(ret).isFalse()
assertThat(generateFails.size).isEqualTo(0)
}
}
@Test
fun testGenerateChallengeAsCredentialActivityResult_nullDataChooseLock() = runTest {
val userId = 108
val gkPwHandle = 4444L
viewModel =
newAutoCredentialViewModel(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle))
val generateFails = listOfGenerateChallengeFailedFlow()
// Run generateChallengeAsCredentialActivityResult()
val ret = viewModel.generateChallengeAsCredentialActivityResult(
true,
ActivityResult(ChooseLockPattern.RESULT_FINISHED, null),
backgroundScope
)
runCurrent()
assertThat(ret).isFalse()
assertThat(generateFails.size).isEqualTo(0)
}
@Test
fun testGenerateChallengeAsCredentialActivityResult_nullDataConfirmLock() = runTest {
val userId = 109
viewModel =
newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
val generateFails = listOfGenerateChallengeFailedFlow()
// Run generateChallengeAsCredentialActivityResult()
val ret = viewModel.generateChallengeAsCredentialActivityResult(
false,
ActivityResult(Activity.RESULT_OK, null),
backgroundScope
)
runCurrent()
assertThat(ret).isFalse()
assertThat(generateFails.size).isEqualTo(0)
}
@Test
fun testGenerateChallengeAsCredentialActivityResult_validChooseLock() = runTest {
val userId = 108
viewModel =
newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
)
val gkPwHandle = 6666L
val newSensorId = 50
val newChallenge = 60L
setupGenerateChallenge(userId, newSensorId, newChallenge)
whenever(
lockPatternUtils.verifyGatekeeperPasswordHandle(
gkPwHandle,
newChallenge,
userId
)
)
.thenReturn(newGoodCredential(gkPwHandle, byteArrayOf(1)))
val hasCalledRemoveGkPwHandle = AtomicBoolean()
Mockito.doAnswer {
hasCalledRemoveGkPwHandle.set(true)
null
}.`when`(lockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle)
val generateFails = listOfGenerateChallengeFailedFlow()
// Run generateChallengeAsCredentialActivityResult()
val intent =
Intent().putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle)
val ret = viewModel.generateChallengeAsCredentialActivityResult(
true,
ActivityResult(ChooseLockPattern.RESULT_FINISHED, intent),
backgroundScope
)
runCurrent()
assertThat(ret).isTrue()
assertThat(generateFails.size).isEqualTo(0)
assertThat(viewModel.token).isNotNull()
assertThat(challengeGenerator.callbackRunCount).isEqualTo(1)
assertThat(hasCalledRemoveGkPwHandle.get()).isTrue()
}
@Test
fun testGenerateChallengeAsCredentialActivityResult_validConfirmLock() = runTest {
val userId = 109
viewModel =
newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
)
val gkPwHandle = 5555L
val newSensorId = 80
val newChallenge = 90L
setupGenerateChallenge(userId, newSensorId, newChallenge)
whenever(
lockPatternUtils.verifyGatekeeperPasswordHandle(
gkPwHandle,
newChallenge,
userId
)
)
.thenReturn(newGoodCredential(gkPwHandle, byteArrayOf(1)))
val hasCalledRemoveGkPwHandle = AtomicBoolean()
Mockito.doAnswer {
hasCalledRemoveGkPwHandle.set(true)
null
}.`when`(lockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle)
val generateFails = listOfGenerateChallengeFailedFlow()
// Run generateChallengeAsCredentialActivityResult()
val intent =
Intent().putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle)
val ret = viewModel.generateChallengeAsCredentialActivityResult(
false,
ActivityResult(Activity.RESULT_OK, intent),
backgroundScope
)
runCurrent()
assertThat(ret).isTrue()
assertThat(generateFails.size).isEqualTo(0)
assertThat(viewModel.token).isNotNull()
assertThat(challengeGenerator.callbackRunCount).isEqualTo(1)
assertThat(hasCalledRemoveGkPwHandle.get()).isTrue()
}
private fun TestScope.listOfGenerateChallengeFailedFlow(): List<Boolean> =
mutableListOf<Boolean>().also {
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
viewModel.generateChallengeFailedFlow.toList(it)
}
}
class TestChallengeGenerator : ChallengeGenerator {
var sensorId = -1
var userId = UserHandle.myUserId()
var challenge = CredentialModel.INVALID_CHALLENGE
var callbackRunCount = 0
override var callback: AutoCredentialViewModel.GenerateChallengeCallback? = null
override fun generateChallenge(userId: Int) {
callback?.let {
it.onChallengeGenerated(sensorId, this.userId, challenge)
++callbackRunCount
}
}
}
private fun newGoodCredential(gkPwHandle: Long, hat: ByteArray): VerifyCredentialResponse {
return VerifyCredentialResponse.Builder()
.setGatekeeperPasswordHandle(gkPwHandle)
.setGatekeeperHAT(hat)
.build()
}
private fun newBadCredential(timeout: Int): VerifyCredentialResponse {
return if (timeout > 0) {
VerifyCredentialResponse.fromTimeout(timeout)
} else {
VerifyCredentialResponse.fromError()
}
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics2.ui.viewmodel;
import static com.google.common.truth.Truth.assertThat;
import android.app.Application;
import android.content.res.Configuration;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.testutils.InstantTaskExecutorRule;
import com.android.systemui.unfold.compat.ScreenSizeFoldProvider;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class DeviceFoldedViewModelTest {
@Rule public final InstantTaskExecutorRule mTaskExecutorRule = new InstantTaskExecutorRule();
private DeviceFoldedViewModel mViewModel;
@Before
public void setUp() {
final Application application = ApplicationProvider.getApplicationContext();
mViewModel = new DeviceFoldedViewModel(new ScreenSizeFoldProvider(application),
application.getMainExecutor());
}
@Test
public void testLiveData() {
final Configuration config1 = new Configuration();
config1.smallestScreenWidthDp = 601;
mViewModel.onConfigurationChanged(config1);
assertThat(mViewModel.getLiveData().getValue()).isFalse();
final Configuration config2 = new Configuration();
config2.smallestScreenWidthDp = 599;
mViewModel.onConfigurationChanged(config2);
assertThat(mViewModel.getLiveData().getValue()).isTrue();
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics2.ui.viewmodel;
import static com.google.common.truth.Truth.assertThat;
import android.app.Application;
import android.view.Surface;
import androidx.annotation.NonNull;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.testutils.InstantTaskExecutorRule;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@RunWith(AndroidJUnit4.class)
public class DeviceRotationViewModelTest {
@Rule public final MockitoRule mockito = MockitoJUnit.rule();
@Rule public final InstantTaskExecutorRule mTaskExecutorRule = new InstantTaskExecutorRule();
private TestDeviceRotationViewModel mViewModel;
@Before
public void setUp() {
TestDeviceRotationViewModel.sTestRotation = 3;
mViewModel = new TestDeviceRotationViewModel(ApplicationProvider.getApplicationContext());
}
@Test
public void testDefaultLiveDataNotNull() {
assertThat(mViewModel.getLiveData().getValue()).isEqualTo(mViewModel.sTestRotation);
}
@Test
public void testOnDisplayChange() {
mViewModel.sTestRotation = 3;
mViewModel.triggerOnDisplayChanged();
assertThat(mViewModel.getLiveData().getValue()).isEqualTo(mViewModel.sTestRotation);
}
public static class TestDeviceRotationViewModel extends DeviceRotationViewModel {
@Surface.Rotation static int sTestRotation = 0;
public TestDeviceRotationViewModel(@NonNull Application application) {
super(application);
}
void triggerOnDisplayChanged() {
mDisplayListener.onDisplayChanged(0);
}
@Override
protected int getRotation() {
return sTestRotation;
}
}
}

View File

@@ -0,0 +1,173 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics2.ui.viewmodel;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP;
import static com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.newFingerprintRepository;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import android.app.Application;
import android.hardware.biometrics.SensorProperties;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
import androidx.lifecycle.LiveData;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.biometrics2.data.repository.FingerprintRepository;
import com.android.settings.testutils.InstantTaskExecutorRule;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
@RunWith(AndroidJUnit4.class)
public class FingerprintEnrollEnrollingViewModelTest {
private static final int TEST_USER_ID = 33;
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
@Rule
public final InstantTaskExecutorRule mTaskExecutorRule = new InstantTaskExecutorRule();
@Mock
private FingerprintManager mFingerprintManager;
private Application mApplication;
private FingerprintEnrollEnrollingViewModel mViewModel;
@Before
public void setUp() {
mApplication = ApplicationProvider.getApplicationContext();
mViewModel = new FingerprintEnrollEnrollingViewModel(
mApplication,
TEST_USER_ID,
newFingerprintRepository(mFingerprintManager, TYPE_UDFPS_OPTICAL, 5)
);
}
@Test
public void testIconTouchDialog() {
final LiveData<Integer> actionLiveData = mViewModel.getActionLiveData();
assertThat(actionLiveData.getValue()).isEqualTo(null);
mViewModel.showIconTouchDialog();
assertThat(actionLiveData.getValue()).isEqualTo(
FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG);
}
@Test
public void tesBackPressedScenario() {
final LiveData<Integer> actionLiveData = mViewModel.getActionLiveData();
assertThat(actionLiveData.getValue()).isEqualTo(null);
assertThat(mViewModel.getOnBackPressed()).isEqualTo(false);
mViewModel.setOnBackPressed();
assertThat(mViewModel.getOnBackPressed()).isEqualTo(true);
mViewModel.onCancelledDueToOnBackPressed();
assertThat(mViewModel.getOnBackPressed()).isEqualTo(false);
assertThat(actionLiveData.getValue()).isEqualTo(
FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED);
}
@Test
public void testSkipPressedScenario() {
final LiveData<Integer> actionLiveData = mViewModel.getActionLiveData();
assertThat(actionLiveData.getValue()).isEqualTo(null);
assertThat(mViewModel.getOnSkipPressed()).isEqualTo(false);
mViewModel.setOnSkipPressed();
assertThat(mViewModel.getOnSkipPressed()).isEqualTo(true);
mViewModel.onCancelledDueToOnSkipPressed();
assertThat(mViewModel.getOnSkipPressed()).isEqualTo(false);
assertThat(actionLiveData.getValue()).isEqualTo(
FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP);
}
@Test
public void testGetFirstFingerprintSensorPropertiesInternal() {
final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
final FingerprintSensorPropertiesInternal prop = new FingerprintSensorPropertiesInternal(
0 /* sensorId */,
SensorProperties.STRENGTH_STRONG,
5,
new ArrayList<>() /* componentInfo */,
TYPE_UDFPS_OPTICAL,
true /* resetLockoutRequiresHardwareAuthToken */);
props.add(prop);
doAnswer(invocation -> {
final IFingerprintAuthenticatorsRegisteredCallback callback =
invocation.getArgument(0);
callback.onAllAuthenticatorsRegistered(props);
return null;
}).when(mFingerprintManager).addAuthenticatorsRegisteredCallback(any());
mViewModel = new FingerprintEnrollEnrollingViewModel(
mApplication,
TEST_USER_ID,
new FingerprintRepository(mFingerprintManager)
);
assertThat(mViewModel.getFirstFingerprintSensorPropertiesInternal()).isEqualTo(prop);
}
@Test
public void testGetEnrollStageCount() {
final int expectedValue = 24;
doReturn(expectedValue).when(mFingerprintManager).getEnrollStageCount();
assertThat(mViewModel.getEnrollStageCount()).isEqualTo(expectedValue);
}
@Test
public void testGetEnrollStageThreshold() {
final float expectedValue0 = 0.42f;
final float expectedValue1 = 0.24f;
final float expectedValue2 = 0.33f;
final float expectedValue3 = 0.90f;
doReturn(expectedValue0).when(mFingerprintManager).getEnrollStageThreshold(0);
doReturn(expectedValue1).when(mFingerprintManager).getEnrollStageThreshold(1);
doReturn(expectedValue2).when(mFingerprintManager).getEnrollStageThreshold(2);
doReturn(expectedValue3).when(mFingerprintManager).getEnrollStageThreshold(3);
assertThat(mViewModel.getEnrollStageThreshold(2)).isEqualTo(expectedValue2);
assertThat(mViewModel.getEnrollStageThreshold(1)).isEqualTo(expectedValue1);
assertThat(mViewModel.getEnrollStageThreshold(3)).isEqualTo(expectedValue3);
assertThat(mViewModel.getEnrollStageThreshold(0)).isEqualTo(expectedValue0);
}
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics2.ui.viewmodel
import android.app.Application
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.biometrics2.ui.viewmodel.FingerprintErrorDialogSetResultAction.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class FingerprintEnrollErrorDialogViewModelTest {
private val application = ApplicationProvider.getApplicationContext<Application>()
private var viewModel: FingerprintEnrollErrorDialogViewModel =
FingerprintEnrollErrorDialogViewModel(application, false)
@Before
fun setUp() {
// Make sure viewModel is new for each test
viewModel = FingerprintEnrollErrorDialogViewModel(application, false)
}
@Test
fun testIsDialogNotShownDefaultFalse() {
assertThat(viewModel.isDialogShown).isFalse()
}
@Test
fun testIsSuw() {
assertThat(FingerprintEnrollErrorDialogViewModel(application, false).isSuw).isFalse()
assertThat(FingerprintEnrollErrorDialogViewModel(application, true).isSuw).isTrue()
}
@Test
fun testNewDialog() = runTest {
val newDialogs: List<Int> = mutableListOf<Int>().also {
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
viewModel.newDialogFlow.toList(it)
}
}
runCurrent()
// Default values
assertThat(viewModel.isDialogShown).isFalse()
assertThat(newDialogs.size).isEqualTo(0)
val testErrorMsgId = 3456
viewModel.newDialog(testErrorMsgId)
runCurrent()
// verify after emit
assertThat(viewModel.isDialogShown).isTrue()
assertThat(newDialogs.size).isEqualTo(1)
assertThat(newDialogs[0]).isEqualTo(testErrorMsgId)
}
@Test
fun testTriggerRetry() = runTest {
val triggerRetries: List<Any> = mutableListOf<Any>().also {
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
viewModel.triggerRetryFlow.toList(it)
}
}
runCurrent()
// Default values
assertThat(triggerRetries.size).isEqualTo(0)
viewModel.triggerRetry()
runCurrent()
// verify after emit
assertThat(triggerRetries.size).isEqualTo(1)
}
@Test
fun testSetResultFinish() = runTest {
val setResults: List<FingerprintErrorDialogSetResultAction> =
mutableListOf<FingerprintErrorDialogSetResultAction>().also {
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
viewModel.setResultFlow.toList(it)
}
}
runCurrent()
// Default values
assertThat(setResults.size).isEqualTo(0)
viewModel.setResultAndFinish(FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH)
runCurrent()
// verify after emit
assertThat(setResults.size).isEqualTo(1)
assertThat(setResults[0]).isEqualTo(FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH)
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics2.ui.viewmodel;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_DIALOG;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_SKIP;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_START;
import static com.google.common.truth.Truth.assertThat;
import android.app.Application;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.testutils.InstantTaskExecutorRule;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class FingerprintEnrollFindSensorViewModelTest {
@Rule public final InstantTaskExecutorRule mTaskExecutorRule = new InstantTaskExecutorRule();
private Application mApplication;
private FingerprintEnrollFindSensorViewModel mViewModel;
@Before
public void setUp() {
mApplication = ApplicationProvider.getApplicationContext();
mViewModel = new FingerprintEnrollFindSensorViewModel(mApplication, false);
}
@Test
public void testClickSkipButtonNotInSuw() {
mViewModel = new FingerprintEnrollFindSensorViewModel(mApplication, false);
mViewModel.onSkipButtonClick();
assertThat(mViewModel.getActionLiveData().getValue()).isEqualTo(
FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_SKIP);
}
@Test
public void testClickSkipButtonInSuw() {
mViewModel = new FingerprintEnrollFindSensorViewModel(mApplication, true);
mViewModel.onSkipButtonClick();
assertThat(mViewModel.getActionLiveData().getValue()).isEqualTo(
FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_DIALOG);
}
@Test
public void testClickSkipDialogButton() {
mViewModel.onSkipDialogButtonClick();
assertThat(mViewModel.getActionLiveData().getValue()).isEqualTo(
FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_SKIP);
}
@Test
public void testClickStartDialogButton() {
mViewModel.onStartButtonClick();
assertThat(mViewModel.getActionLiveData().getValue()).isEqualTo(
FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_START);
}
@Test
public void testClearActionLiveData() {
assertThat(mViewModel.getActionLiveData().getValue()).isNull();
mViewModel.onStartButtonClick();
assertThat(mViewModel.getActionLiveData().getValue()).isNotNull();
mViewModel.clearActionLiveData();
assertThat(mViewModel.getActionLiveData().getValue()).isNull();
}
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics2.ui.viewmodel;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFinishViewModel.FINGERPRINT_ENROLL_FINISH_ACTION_ADD_BUTTON_CLICK;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFinishViewModel.FINGERPRINT_ENROLL_FINISH_ACTION_NEXT_BUTTON_CLICK;
import static com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.newFingerprintRepository;
import static com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.setupFingerprintEnrolledFingerprints;
import static com.google.common.truth.Truth.assertThat;
import android.app.Application;
import android.content.Intent;
import android.hardware.fingerprint.FingerprintManager;
import androidx.lifecycle.LiveData;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.biometrics2.ui.model.EnrollmentRequest;
import com.android.settings.testutils.InstantTaskExecutorRule;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@RunWith(AndroidJUnit4.class)
public class FingerprintEnrollFinishViewModelTest {
private static final int USER_ID = 334;
private static final int MAX_ENROLLABLE = 5;
@Rule public final MockitoRule mockito = MockitoJUnit.rule();
@Rule public final InstantTaskExecutorRule mTaskExecutorRule = new InstantTaskExecutorRule();
@Mock private FingerprintManager mFingerprintManager;
private Application mApplication;
private EnrollmentRequest mRequest;
private FingerprintEnrollFinishViewModel mViewModel;
@Before
public void setUp() {
mApplication = ApplicationProvider.getApplicationContext();
mRequest = new EnrollmentRequest(new Intent(), mApplication, true);
mViewModel = new FingerprintEnrollFinishViewModel(mApplication, USER_ID, mRequest,
newFingerprintRepository(mFingerprintManager, TYPE_UDFPS_OPTICAL, MAX_ENROLLABLE));
}
@Test
public void testCanAssumeSfps() {
mViewModel = new FingerprintEnrollFinishViewModel(mApplication, USER_ID, mRequest,
newFingerprintRepository(mFingerprintManager, TYPE_UDFPS_OPTICAL, MAX_ENROLLABLE));
assertThat(mViewModel.canAssumeSfps()).isFalse();
mViewModel = new FingerprintEnrollFinishViewModel(mApplication, USER_ID, mRequest,
newFingerprintRepository(mFingerprintManager, TYPE_REAR, MAX_ENROLLABLE));
assertThat(mViewModel.canAssumeSfps()).isFalse();
mViewModel = new FingerprintEnrollFinishViewModel(mApplication, USER_ID, mRequest,
newFingerprintRepository(mFingerprintManager, TYPE_POWER_BUTTON, MAX_ENROLLABLE));
assertThat(mViewModel.canAssumeSfps()).isTrue();
}
@Test
public void testIsAnotherFingerprintEnrollable() {
setupFingerprintEnrolledFingerprints(mFingerprintManager, USER_ID, MAX_ENROLLABLE);
assertThat(mViewModel.isAnotherFingerprintEnrollable()).isFalse();
setupFingerprintEnrolledFingerprints(mFingerprintManager, USER_ID, MAX_ENROLLABLE - 1);
assertThat(mViewModel.isAnotherFingerprintEnrollable()).isTrue();
}
@Test
public void testGetRequest() {
assertThat(mViewModel.getRequest()).isEqualTo(mRequest);
}
@Test
public void testOnAddButtonClick() {
final LiveData<Integer> actionLiveData = mViewModel.getActionLiveData();
// Test init value
assertThat(actionLiveData.getValue()).isNull();
// Test onAddButtonClick()
mViewModel.onAddButtonClick();
assertThat(actionLiveData.getValue()).isEqualTo(
FINGERPRINT_ENROLL_FINISH_ACTION_ADD_BUTTON_CLICK);
// Clear
mViewModel.clearActionLiveData();
assertThat(actionLiveData.getValue()).isNull();
}
@Test
public void testOnNextButtonClick() {
final LiveData<Integer> actionLiveData = mViewModel.getActionLiveData();
// Test init value
assertThat(actionLiveData.getValue()).isNull();
// Test onNextButtonClick()
mViewModel.onNextButtonClick();
assertThat(actionLiveData.getValue()).isEqualTo(
FINGERPRINT_ENROLL_FINISH_ACTION_NEXT_BUTTON_CLICK);
// Clear
mViewModel.clearActionLiveData();
assertThat(actionLiveData.getValue()).isNull();
}
}

View File

@@ -0,0 +1,357 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.biometrics2.ui.viewmodel
import android.app.Application
import android.content.res.Resources
import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.biometrics2.data.repository.FingerprintRepository
import com.android.settings.biometrics2.ui.model.EnrollmentRequest
import com.android.settings.biometrics2.ui.model.FingerprintEnrollIntroStatus
import com.android.settings.biometrics2.ui.model.FingerprintEnrollable.FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX
import com.android.settings.biometrics2.ui.model.FingerprintEnrollable.FINGERPRINT_ENROLLABLE_OK
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroAction.CONTINUE_ENROLL
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroAction.DONE_AND_FINISH
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroAction.SKIP_OR_CANCEL
import com.android.settings.biometrics2.utils.EnrollmentRequestUtils.newAllFalseRequest
import com.android.settings.biometrics2.utils.EnrollmentRequestUtils.newIsSuwDeferredRequest
import com.android.settings.biometrics2.utils.EnrollmentRequestUtils.newIsSuwPortalRequest
import com.android.settings.biometrics2.utils.EnrollmentRequestUtils.newIsSuwRequest
import com.android.settings.biometrics2.utils.EnrollmentRequestUtils.newIsSuwSuggestedActionFlowRequest
import com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.newFingerprintRepository
import com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.setupFingerprintEnrolledFingerprints
import com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.setupSuwMaxFingerprintsEnrollable
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.junit.MockitoJUnit
@RunWith(AndroidJUnit4::class)
class FingerprintEnrollIntroViewModelTest {
@get:Rule val mockito = MockitoJUnit.rule()
@Mock private lateinit var resources: Resources
@Mock private lateinit var fingerprintManager: FingerprintManager
private var application: Application = ApplicationProvider.getApplicationContext()
private fun newFingerprintEnrollIntroViewModel(
fingerprintRepository: FingerprintRepository,
enrollmentRequest: EnrollmentRequest
) = FingerprintEnrollIntroViewModel(
application,
fingerprintRepository,
enrollmentRequest,
TEST_USER_ID
)
@Before
fun setUp() {
application = ApplicationProvider.getApplicationContext()
}
@Test
fun testPageStatusFlowDefaultAndUpdate() = runTest {
val viewModel = newFingerprintEnrollIntroViewModel(
newFingerprintRepository(fingerprintManager, TYPE_UDFPS_OPTICAL, 1),
newAllFalseRequest(application)
)
val statusList = listOfPageStatusFlow(viewModel)
runCurrent()
// assert default values
assertThat(statusList.size).isEqualTo(1)
assertThat(statusList[0].hasScrollToBottom()).isFalse()
assertThat(statusList[0].enrollableStatus).isEqualTo(FINGERPRINT_ENROLLABLE_OK)
setupFingerprintEnrolledFingerprints(fingerprintManager, TEST_USER_ID, 1)
viewModel.updateEnrollableStatus(backgroundScope)
runCurrent()
// assert new updated value
assertThat(statusList.size).isEqualTo(2)
assertThat(statusList[1].hasScrollToBottom()).isFalse()
assertThat(statusList[1].enrollableStatus).isEqualTo(FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX)
}
fun testOnStartToUpdateEnrollableStatusOk_isSuw() = runTest {
setupFingerprintEnrolledFingerprints(fingerprintManager, TEST_USER_ID, 0)
setupSuwMaxFingerprintsEnrollable(application, resources, 1)
val viewModel = newFingerprintEnrollIntroViewModel(
newFingerprintRepository(fingerprintManager, TYPE_UDFPS_OPTICAL, 5),
newIsSuwRequest(application)
)
val statusList = listOfPageStatusFlow(viewModel)
runCurrent()
assertThat(statusList.size).isEqualTo(1)
assertThat(statusList[0].enrollableStatus).isEqualTo(FINGERPRINT_ENROLLABLE_OK)
}
@Test
fun testOnStartToUpdateEnrollableStatusReachMax_isSuw() = runTest {
setupFingerprintEnrolledFingerprints(fingerprintManager, TEST_USER_ID, 1)
setupSuwMaxFingerprintsEnrollable(application, resources, 1)
val viewModel = newFingerprintEnrollIntroViewModel(
newFingerprintRepository(fingerprintManager, TYPE_UDFPS_OPTICAL, 5),
newIsSuwRequest(application)
)
val statusList = listOfPageStatusFlow(viewModel)
runCurrent()
assertThat(statusList.size).isEqualTo(1)
assertThat(statusList[0].enrollableStatus).isEqualTo(FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX)
}
@Test
fun testOnStartToUpdateEnrollableStatusOk_isNotSuw() = runTest {
testOnStartToUpdateEnrollableStatusOk(newAllFalseRequest(application))
}
@Test
fun testOnStartToUpdateEnrollableStatusReachMax_isNotSuw() = runTest {
testOnStartToUpdateEnrollableStatusReachMax(newAllFalseRequest(application))
}
@Test
fun testOnStartToUpdateEnrollableStatusOk_isSuwDeferred() = runTest {
testOnStartToUpdateEnrollableStatusOk(newIsSuwDeferredRequest(application))
}
@Test
fun testOnStartToUpdateEnrollableStatusReachMax_isSuwDeferred() = runTest {
testOnStartToUpdateEnrollableStatusReachMax(newIsSuwDeferredRequest(application))
}
@Test
fun testOnStartToUpdateEnrollableStatusOk_isSuwPortal() = runTest {
testOnStartToUpdateEnrollableStatusOk(newIsSuwPortalRequest(application))
}
@Test
fun testOnStartToUpdateEnrollableStatusReachMax_isSuwPortal() = runTest {
testOnStartToUpdateEnrollableStatusReachMax(newIsSuwPortalRequest(application))
}
@Test
fun testOnStartToUpdateEnrollableStatusOk_isSuwSuggestedActionFlow() = runTest {
testOnStartToUpdateEnrollableStatusOk(newIsSuwSuggestedActionFlowRequest(application))
}
@Test
fun testOnStartToUpdateEnrollableStatusReachMax_isSuwSuggestedActionFlow() = runTest {
testOnStartToUpdateEnrollableStatusReachMax(
newIsSuwSuggestedActionFlowRequest(application)
)
}
private fun TestScope.testOnStartToUpdateEnrollableStatusOk(request: EnrollmentRequest) {
setupFingerprintEnrolledFingerprints(fingerprintManager, TEST_USER_ID, 0)
val viewModel = newFingerprintEnrollIntroViewModel(
newFingerprintRepository(fingerprintManager, TYPE_UDFPS_OPTICAL, 5),
request
)
val statusList = listOfPageStatusFlow(viewModel)
runCurrent()
assertThat(statusList.size).isEqualTo(1)
assertThat(statusList[0].enrollableStatus).isEqualTo(FINGERPRINT_ENROLLABLE_OK)
}
private fun TestScope.testOnStartToUpdateEnrollableStatusReachMax(request: EnrollmentRequest) {
setupFingerprintEnrolledFingerprints(fingerprintManager, TEST_USER_ID, 5)
val viewModel = newFingerprintEnrollIntroViewModel(
newFingerprintRepository(fingerprintManager, TYPE_UDFPS_OPTICAL, 5),
request
)
val statusList = listOfPageStatusFlow(viewModel)
runCurrent()
assertThat(statusList.size).isEqualTo(1)
assertThat(statusList[0].enrollableStatus).isEqualTo(FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX)
}
@Test
fun testIsParentalConsentRequired() {
// We shall not mock FingerprintRepository, but
// FingerprintRepository.isParentalConsentRequired() calls static method inside, we can't
// mock static method
val fingerprintRepository = Mockito.mock(
FingerprintRepository::class.java
)
val viewModel = FingerprintEnrollIntroViewModel(
application,
fingerprintRepository,
newAllFalseRequest(application),
TEST_USER_ID
)
Mockito.`when`(
fingerprintRepository.isParentalConsentRequired(application)
).thenReturn(true)
assertThat(viewModel.isParentalConsentRequired).isEqualTo(true)
Mockito.`when`(
fingerprintRepository.isParentalConsentRequired(application)
).thenReturn(false)
assertThat(viewModel.isParentalConsentRequired).isEqualTo(false)
}
@Test
fun testIsBiometricUnlockDisabledByAdmin() {
// We shall not mock FingerprintRepository, but
// FingerprintRepository.isDisabledByAdmin() calls static method inside, we can't mock
// static method
val fingerprintRepository = Mockito.mock(FingerprintRepository::class.java)
val viewModel = FingerprintEnrollIntroViewModel(
application,
fingerprintRepository,
newAllFalseRequest(application),
TEST_USER_ID
)
Mockito.`when`(
fingerprintRepository.isDisabledByAdmin(application, TEST_USER_ID)
).thenReturn(true)
assertThat(viewModel.isBiometricUnlockDisabledByAdmin).isEqualTo(true)
Mockito.`when`(
fingerprintRepository.isDisabledByAdmin(application, TEST_USER_ID)
).thenReturn(false)
assertThat(viewModel.isBiometricUnlockDisabledByAdmin).isEqualTo(false)
}
@Test
fun testSetHasScrolledToBottom() = runTest {
val viewModel = newFingerprintEnrollIntroViewModel(
newFingerprintRepository(fingerprintManager, TYPE_UDFPS_OPTICAL, 5),
newAllFalseRequest(application)
)
val pageStatusList = listOfPageStatusFlow(viewModel)
viewModel.setHasScrolledToBottom(true, backgroundScope)
runCurrent()
assertThat(pageStatusList[pageStatusList.size-1].hasScrollToBottom()).isEqualTo(true)
}
@Test
fun testOnNextButtonClick_enrollNext() = runTest {
// Set latest status to FINGERPRINT_ENROLLABLE_OK
setupFingerprintEnrolledFingerprints(fingerprintManager, TEST_USER_ID, 0)
setupSuwMaxFingerprintsEnrollable(application, resources, 1)
val viewModel = newFingerprintEnrollIntroViewModel(
newFingerprintRepository(fingerprintManager, TYPE_UDFPS_OPTICAL, 5),
newIsSuwRequest(application)
)
val actions = listOfActionFlow(viewModel)
// Perform click on `next`
viewModel.onNextButtonClick(backgroundScope)
runCurrent()
assertThat(actions.size).isEqualTo(1)
assertThat(actions[0]).isEqualTo(CONTINUE_ENROLL)
}
@Test
fun testOnNextButtonClick_doneAndFinish() = runTest {
// Set latest status to FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX
setupFingerprintEnrolledFingerprints(fingerprintManager, TEST_USER_ID, 1)
setupSuwMaxFingerprintsEnrollable(application, resources, 1)
val viewModel = newFingerprintEnrollIntroViewModel(
newFingerprintRepository(fingerprintManager, TYPE_UDFPS_OPTICAL, 5),
newIsSuwRequest(application)
)
val statusList = listOfPageStatusFlow(viewModel)
val actionList = listOfActionFlow(viewModel)
runCurrent()
assertThat(statusList.size).isEqualTo(1)
assertThat(statusList[0].enrollableStatus).isEqualTo(FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX)
val actions = listOfActionFlow(viewModel)
// Perform click on `next`
viewModel.onNextButtonClick(backgroundScope)
runCurrent()
assertThat(actionList.size).isEqualTo(1)
assertThat(actionList[0]).isEqualTo(DONE_AND_FINISH)
}
@Test
fun testOnSkipOrCancelButtonClick() = runTest {
val viewModel = newFingerprintEnrollIntroViewModel(
newFingerprintRepository(fingerprintManager, TYPE_UDFPS_OPTICAL, 5),
newAllFalseRequest(application)
)
val actions = listOfActionFlow(viewModel)
viewModel.onSkipOrCancelButtonClick(backgroundScope)
runCurrent()
assertThat(actions.size).isEqualTo(1)
assertThat(actions[0]).isEqualTo(SKIP_OR_CANCEL)
}
private fun TestScope.listOfActionFlow(
viewModel: FingerprintEnrollIntroViewModel
): List<FingerprintEnrollIntroAction> =
mutableListOf<FingerprintEnrollIntroAction>().also {
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
viewModel.actionFlow.toList(it)
}
}
private fun TestScope.listOfPageStatusFlow(
viewModel: FingerprintEnrollIntroViewModel
): List<FingerprintEnrollIntroStatus> =
mutableListOf<FingerprintEnrollIntroStatus>().also {
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
viewModel.pageStatusFlow.toList(it)
}
}
companion object {
private const val TEST_USER_ID = 33
}
}

Some files were not shown because too many files have changed in this diff Show More