fix: 首次提交

This commit is contained in:
2024-12-09 11:25:23 +08:00
parent d0c01071e9
commit 2c2109a5f3
4741 changed files with 290641 additions and 0 deletions

View File

@@ -0,0 +1,103 @@
// 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.
//###########################################################
// SettingsLib Shell app just for Robolectric test target. #
//###########################################################
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["frameworks_base_license"],
}
android_app {
name: "SettingsLibShell",
use_resource_processor: true,
defaults: ["SettingsLibDefaults"],
platform_apis: true,
privileged: true,
resource_dirs: ["res"],
}
//###########################################################
// SettingsLib Robolectric test target. #
//###########################################################
android_robolectric_test {
name: "SettingsLibRoboTests",
srcs: ["src/**/*.java"],
static_libs: [
"Settings_robolectric_meta_service_file",
"Robolectric_shadows_androidx_fragment_upstream",
"SettingsLib-robo-testutils",
"androidx.fragment_fragment",
"androidx.test.core",
"androidx.core_core",
"flag-junit",
"settingslib_media_flags_lib",
"testng", // TODO: remove once JUnit on Android provides assertThrows
],
java_resource_dirs: ["config"],
instrumentation_for: "SettingsLibShell",
coverage_libs: ["SettingsLib"],
test_options: {
timeout: 36000,
},
upstream: true,
}
java_genrule {
name: "Settings_robolectric_meta_service_file",
out: ["robolectric_meta_service_file.jar"],
tools: ["soong_zip"],
cmd: "mkdir -p $(genDir)/META-INF/services/ && touch $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider &&" +
"echo -e 'org.robolectric.Shadows' >> $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider && " +
"echo -e 'org.robolectric.shadows.multidex.Shadows' >> $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider && " +
"echo -e 'org.robolectric.shadows.httpclient.Shadows' >> $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider && " +
//"echo -e 'com.android.settings.testutils.shadow.Shadows' >> $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider && " +
"echo -e 'com.android.settingslib.testutils.shadow.Shadows' >> $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider && " +
"$(location soong_zip) -o $(out) -C $(genDir) -D $(genDir)/META-INF/services/",
}
java_library {
name: "SettingsLib-robo-testutils",
srcs: [
"testutils/com/android/settingslib/testutils/**/*.java",
],
javacflags: [
"-Aorg.robolectric.annotation.processing.shadowPackage=com.android.settingslib.testutils.shadow",
"-Aorg.robolectric.annotation.processing.sdkCheckMode=ERROR",
// Uncomment the below to debug annotation processors not firing.
//"-verbose",
//"-XprintRounds",
//"-XprintProcessorInfo",
//"-Xlint",
//"-J-verbose",
],
plugins: [
"auto_value_plugin_1.9",
"auto_value_builder_plugin_1.9",
"Robolectric_processor_upstream",
],
libs: [
"Robolectric_all-target_upstream",
"mockito-robolectric-prebuilt",
"truth",
],
}

View File

@@ -0,0 +1,24 @@
<?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"
coreApp="true"
package="com.android.settingslib.robotests">
<application/>
</manifest>

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,2 @@
sdk=NEWEST_SDK
instrumentedPackages=androidx.preference

View File

@@ -0,0 +1,40 @@
//#############################################
// Compile Robolectric shadows framework misapplied to androidx
//#############################################
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["frameworks_base_license"],
}
java_library {
name: "Robolectric_shadows_androidx_fragment_upstream",
srcs: [
"src/main/java/**/*.java",
"src/main/java/**/*.kt",
],
javacflags: [
"-Aorg.robolectric.annotation.processing.shadowPackage=org.robolectric.shadows.androidx.fragment",
"-Aorg.robolectric.annotation.processing.sdkCheckMode=ERROR",
// Uncomment the below to debug annotation processors not firing.
//"-verbose",
//"-XprintRounds",
//"-XprintProcessorInfo",
//"-Xlint",
//"-J-verbose",
],
libs: [
"Robolectric_all-target_upstream",
"androidx.fragment_fragment",
],
plugins: [
"auto_value_plugin_1.9",
"auto_value_builder_plugin_1.9",
"Robolectric_processor_upstream",
],
}

View File

@@ -0,0 +1,69 @@
load("//third_party/java/android/android_sdk_linux/extras/android/compatibility/jetify:jetify.bzl", "jetify_android_library", "jetify_android_local_test")
package(
default_applicable_licenses = ["//third_party/java_src/robolectric:license"],
default_visibility = ["//third_party/java_src/robolectric:__subpackages__"],
)
licenses(["notice"])
#==============================================================================
# Test resources library
#==============================================================================
jetify_android_library(
name = "test_resources",
custom_package = "org.robolectric.shadows.androidx.fragment",
manifest = "src/test/AndroidManifest.xml",
resource_files = glob(
["src/test/resources/**/*"],
),
)
#==============================================================================
# AndroidX fragment module library
#==============================================================================
jetify_android_library(
name = "androidx_fragment",
testonly = 1,
srcs = glob(
["src/main/java/**"],
),
custom_package = "org.robolectric.shadows.androidx.fragment",
javacopts = [
"-Aorg.robolectric.annotation.processing.shadowPackage=org.robolectric.shadows.androidx.fragment",
],
jetify_sources = True,
plugins = [
"//java/com/google/thirdparty/robolectric/processor",
],
deps = [
"//third_party/java/androidx/core",
"//third_party/java/androidx/fragment",
"//third_party/java/androidx/lifecycle",
"//third_party/java_src/robolectric/shadowapi",
"//third_party/java_src/robolectric/shadows/framework",
],
)
[
jetify_android_local_test(
name = "test_" + src.rstrip(".java"),
size = "small",
srcs = glob(
["src/test/java/**/*.java"],
),
jetify_sources = True,
deps = [
":androidx_fragment",
":test_resources",
"//third_party/java/androidx/fragment",
"//third_party/java/androidx/loader",
"//third_party/java/mockito",
"//third_party/java/robolectric",
"//third_party/java/truth",
],
)
for src in glob(
["src/test/java/**/*Test.java"],
)
]

View File

@@ -0,0 +1,48 @@
plugins {
id "net.ltgt.errorprone" version "0.0.13"
}
apply plugin: 'com.android.library'
android {
compileSdkVersion 28
android {
sourceSets {
main {
res.srcDirs = ['src/test/resources/res']
}
}
testOptions {
unitTests {
includeAndroidResources = true
}
}
}
}
dependencies {
// Project dependencies
compileOnly project(":robolectric")
// Compile dependencies
compileOnly AndroidSdk.MAX_SDK.coordinates
compileOnly "androidx.core:core:1.0.0-rc02"
compileOnly 'androidx.fragment:fragment:1.0.0-rc02'
compileOnly "androidx.lifecycle:lifecycle-viewmodel:2.0.0-rc01"
compileOnly "androidx.lifecycle:lifecycle-common:2.0.0-beta01"
// Testing dependencies
testImplementation "com.google.truth:truth:0.44"
testImplementation "org.mockito:mockito-core:2.5.4"
testImplementation "androidx.arch.core:core-common:2.0.0-beta01"
testImplementation "androidx.arch.core:core-runtime:2.0.0-rc01"
testImplementation "androidx.collection:collection:1.0.0-rc01"
testImplementation "androidx.core:core:1.0.0-rc02"
testImplementation 'androidx.fragment:fragment:1.0.0-rc02'
testImplementation "androidx.lifecycle:lifecycle-viewmodel:2.0.0-rc01"
testImplementation "androidx.lifecycle:lifecycle-common:2.0.0-beta01"
testImplementation "androidx.lifecycle:lifecycle-runtime:2.0.0-rc01"
testImplementation "androidx.lifecycle:lifecycle-livedata-core:2.0.0-rc01"
testImplementation "androidx.loader:loader:1.0.0-rc02"
}

View File

@@ -0,0 +1,348 @@
/*
* 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 org.robolectric.shadows.androidx.fragment;
import android.content.Intent;
import android.os.Bundle;
import android.widget.LinearLayout;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.android.controller.ComponentController;
import org.robolectric.util.ReflectionHelpers;
/** A Controller that can be used to drive the lifecycle of a {@link Fragment} */
public class FragmentController<F extends Fragment>
extends ComponentController<FragmentController<F>, F> {
private final F mFragment;
private final ActivityController<? extends FragmentActivity> mActivityController;
private FragmentController(F fragment, Class<? extends FragmentActivity> activityClass) {
this(fragment, activityClass, null /*intent*/, null /*arguments*/);
}
private FragmentController(
F fragment, Class<? extends FragmentActivity> activityClass, Intent intent) {
this(fragment, activityClass, intent, null /*arguments*/);
}
private FragmentController(
F fragment, Class<? extends FragmentActivity> activityClass, Bundle arguments) {
this(fragment, activityClass, null /*intent*/, arguments);
}
private FragmentController(
F fragment,
Class<? extends FragmentActivity> activityClass,
Intent intent,
Bundle arguments) {
super(fragment, intent);
this.mFragment = fragment;
if (arguments != null) {
this.mFragment.setArguments(arguments);
}
this.mActivityController =
ActivityController.of(ReflectionHelpers.callConstructor(activityClass), intent);
}
/**
* Generate the {@link FragmentController} for specific fragment.
*
* @param fragment the fragment which you'd like to drive lifecycle
* @return {@link FragmentController}
*/
public static <F extends Fragment> FragmentController<F> of(F fragment) {
return new FragmentController<>(fragment, FragmentControllerActivity.class);
}
/**
* Generate the {@link FragmentController} for specific fragment and intent.
*
* @param fragment the fragment which you'd like to drive lifecycle
* @param intent the intent which will be retained by activity
* @return {@link FragmentController}
*/
public static <F extends Fragment> FragmentController<F> of(F fragment, Intent intent) {
return new FragmentController<>(fragment, FragmentControllerActivity.class, intent);
}
/**
* Generate the {@link FragmentController} for specific fragment and arguments.
*
* @param fragment the fragment which you'd like to drive lifecycle
* @param arguments the arguments which will be retained by fragment
* @return {@link FragmentController}
*/
public static <F extends Fragment> FragmentController<F> of(F fragment, Bundle arguments) {
return new FragmentController<>(fragment, FragmentControllerActivity.class, arguments);
}
/**
* Generate the {@link FragmentController} for specific fragment and activity class.
*
* @param fragment the fragment which you'd like to drive lifecycle
* @param activityClass the activity which will be attached by fragment
* @return {@link FragmentController}
*/
public static <F extends Fragment> FragmentController<F> of(
F fragment, Class<? extends FragmentActivity> activityClass) {
return new FragmentController<>(fragment, activityClass);
}
/**
* Generate the {@link FragmentController} for specific fragment, intent and arguments.
*
* @param fragment the fragment which you'd like to drive lifecycle
* @param intent the intent which will be retained by activity
* @param arguments the arguments which will be retained by fragment
* @return {@link FragmentController}
*/
public static <F extends Fragment> FragmentController<F> of(
F fragment, Intent intent, Bundle arguments) {
return new FragmentController<>(fragment, FragmentControllerActivity.class, intent,
arguments);
}
/**
* Generate the {@link FragmentController} for specific fragment, activity class and intent.
*
* @param fragment the fragment which you'd like to drive lifecycle
* @param activityClass the activity which will be attached by fragment
* @param intent the intent which will be retained by activity
* @return {@link FragmentController}
*/
public static <F extends Fragment> FragmentController<F> of(
F fragment, Class<? extends FragmentActivity> activityClass, Intent intent) {
return new FragmentController<>(fragment, activityClass, intent);
}
/**
* Generate the {@link FragmentController} for specific fragment, activity class and arguments.
*
* @param fragment the fragment which you'd like to drive lifecycle
* @param activityClass the activity which will be attached by fragment
* @param arguments the arguments which will be retained by fragment
* @return {@link FragmentController}
*/
public static <F extends Fragment> FragmentController<F> of(
F fragment, Class<? extends FragmentActivity> activityClass, Bundle arguments) {
return new FragmentController<>(fragment, activityClass, arguments);
}
/**
* Generate the {@link FragmentController} for specific fragment, activity class, intent and
* arguments.
*
* @param fragment the fragment which you'd like to drive lifecycle
* @param activityClass the activity which will be attached by fragment
* @param intent the intent which will be retained by activity
* @param arguments the arguments which will be retained by fragment
* @return {@link FragmentController}
*/
public static <F extends Fragment> FragmentController<F> of(
F fragment,
Class<? extends FragmentActivity> activityClass,
Intent intent,
Bundle arguments) {
return new FragmentController<>(fragment, activityClass, intent, arguments);
}
/**
* Sets up the given fragment by attaching it to an activity, calling its onCreate() through
* onResume() lifecycle methods, and then making it visible. Note that the fragment will be
* added
* to the view with ID 1.
*/
public static <F extends Fragment> F setupFragment(F fragment) {
return FragmentController.of(fragment).create().start().resume().visible().get();
}
/**
* Sets up the given fragment by attaching it to an activity, calling its onCreate() through
* onResume() lifecycle methods, and then making it visible. Note that the fragment will be
* added
* to the view with ID 1.
*/
public static <F extends Fragment> F setupFragment(
F fragment, Class<? extends FragmentActivity> fragmentActivityClass) {
return FragmentController.of(fragment, fragmentActivityClass)
.create()
.start()
.resume()
.visible()
.get();
}
/**
* Sets up the given fragment by attaching it to an activity created with the given bundle,
* calling its onCreate() through onResume() lifecycle methods, and then making it visible. Note
* that the fragment will be added to the view with ID 1.
*/
public static <F extends Fragment> F setupFragment(
F fragment, Class<? extends FragmentActivity> fragmentActivityClass, Bundle bundle) {
return FragmentController.of(fragment, fragmentActivityClass)
.create(bundle)
.start()
.resume()
.visible()
.get();
}
/**
* Sets up the given fragment by attaching it to an activity created with the given bundle and
* container id, calling its onCreate() through onResume() lifecycle methods, and then making it
* visible.
*/
public static <F extends Fragment> F setupFragment(
F fragment,
Class<? extends FragmentActivity> fragmentActivityClass,
int containerViewId,
Bundle bundle) {
return FragmentController.of(fragment, fragmentActivityClass)
.create(containerViewId, bundle)
.start()
.resume()
.visible()
.get();
}
/**
* Creates the activity with {@link Bundle} and adds the fragment to the view with ID {@code
* contentViewId}.
*/
public FragmentController<F> create(final int contentViewId, final Bundle bundle) {
shadowMainLooper.runPaused(
new Runnable() {
@Override
public void run() {
mActivityController
.create(bundle)
.get()
.getSupportFragmentManager()
.beginTransaction()
.add(contentViewId, mFragment)
.commit();
}
});
return this;
}
/**
* Creates the activity with {@link Bundle} and adds the fragment to it. Note that the fragment
* will be added to the view with ID 1.
*/
public FragmentController<F> create(final Bundle bundle) {
return create(1, bundle);
}
/**
* Creates the {@link Fragment} in a newly initialized state and hence will receive a null
* savedInstanceState {@link Bundle parameter}
*/
@Override
public FragmentController<F> create() {
return create(null);
}
/** Drive lifecycle of activity to Start lifetime */
public FragmentController<F> start() {
shadowMainLooper.runPaused(
new Runnable() {
@Override
public void run() {
mActivityController.start();
}
});
return this;
}
/** Drive lifecycle of activity to Resume lifetime */
public FragmentController<F> resume() {
shadowMainLooper.runPaused(
new Runnable() {
@Override
public void run() {
mActivityController.resume();
}
});
return this;
}
/** Drive lifecycle of activity to Pause lifetime */
public FragmentController<F> pause() {
shadowMainLooper.runPaused(
new Runnable() {
@Override
public void run() {
mActivityController.pause();
}
});
return this;
}
/** Drive lifecycle of activity to Stop lifetime */
public FragmentController<F> stop() {
shadowMainLooper.runPaused(
new Runnable() {
@Override
public void run() {
mActivityController.stop();
}
});
return this;
}
/** Drive lifecycle of activity to Destroy lifetime */
@Override
public FragmentController<F> destroy() {
shadowMainLooper.runPaused(
new Runnable() {
@Override
public void run() {
mActivityController.destroy();
}
});
return this;
}
/** Let activity can be visible lifetime */
public FragmentController<F> visible() {
shadowMainLooper.runPaused(
new Runnable() {
@Override
public void run() {
mActivityController.visible();
}
});
return this;
}
private static class FragmentControllerActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout view = new LinearLayout(this);
view.setId(1);
setContentView(view);
}
}
}

View File

@@ -0,0 +1,23 @@
/*
* 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.
*/
/**
* Testing infrastructure for androidx.fragment library.
*
* <p>To use this in your project, add the artifact {@code
* org.robolectric:shadows-androidx-fragment} to your project.
*/
package org.robolectric.shadows.androidx.fragment;

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.robolectric.shadows.androidx.fragment">
<uses-sdk android:targetSdkVersion="28"/>
</manifest>

View File

@@ -0,0 +1,360 @@
/*
* 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 org.robolectric.shadows.androidx.fragment;
import static android.os.Looper.getMainLooper;
import static com.google.common.truth.Truth.assertThat;
import static org.robolectric.Shadows.shadowOf;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import java.util.ArrayList;
import java.util.List;
/** Tests for {@link FragmentController} */
@RunWith(RobolectricTestRunner.class)
public class FragmentControllerTest {
@After
public void tearDown() {
TranscriptFragment.clearLifecycleEvents();
}
@Test
public void initialNotAttached() {
final FragmentController<TranscriptFragment> controller =
FragmentController.of(new TranscriptFragment());
assertThat(controller.get().getView()).isNull();
assertThat(controller.get().getActivity()).isNull();
assertThat(controller.get().isAdded()).isFalse();
}
@Test
public void initialNotAttached_customActivity() {
final FragmentController<TranscriptFragment> controller =
FragmentController.of(new TranscriptFragment(), TestActivity.class);
assertThat(controller.get().getView()).isNull();
assertThat(controller.get().getActivity()).isNull();
assertThat(controller.get().isAdded()).isFalse();
}
@Test
public void attachedAfterCreate() {
final FragmentController<TranscriptFragment> controller =
FragmentController.of(new TranscriptFragment());
controller.create();
shadowOf(getMainLooper()).idle();
assertThat(controller.get().getActivity()).isNotNull();
assertThat(controller.get().isAdded()).isTrue();
assertThat(controller.get().isResumed()).isFalse();
}
@Test
public void attachedAfterCreate_customActivity() {
final FragmentController<TranscriptFragment> controller =
FragmentController.of(new TranscriptFragment(), TestActivity.class);
controller.create();
shadowOf(getMainLooper()).idle();
assertThat(controller.get().getActivity()).isNotNull();
assertThat(controller.get().getActivity()).isInstanceOf(TestActivity.class);
assertThat(controller.get().isAdded()).isTrue();
assertThat(controller.get().isResumed()).isFalse();
}
@Test
public void attachedAfterCreate_customizedViewId() {
final FragmentController<TranscriptFragment> controller =
FragmentController.of(new TranscriptFragment(), CustomizedViewIdTestActivity.class);
controller.create(R.id.custom_activity_view, null).start();
assertThat(controller.get().getView()).isNotNull();
assertThat(controller.get().getActivity()).isNotNull();
assertThat(controller.get().isAdded()).isTrue();
assertThat(controller.get().isResumed()).isFalse();
assertThat((TextView) controller.get().getView().findViewById(R.id.tacos)).isNotNull();
}
@Test
public void hasViewAfterStart() {
final FragmentController<TranscriptFragment> controller =
FragmentController.of(new TranscriptFragment());
controller.create().start();
assertThat(controller.get().getView()).isNotNull();
}
@Test
public void isResumed() {
final FragmentController<TranscriptFragment> controller =
FragmentController.of(new TranscriptFragment(), TestActivity.class);
controller.create().start().resume();
assertThat(controller.get().getView()).isNotNull();
assertThat(controller.get().getActivity()).isNotNull();
assertThat(controller.get().isAdded()).isTrue();
assertThat(controller.get().isResumed()).isTrue();
assertThat((TextView) controller.get().getView().findViewById(R.id.tacos)).isNotNull();
}
@Test
public void isPaused() {
final FragmentController<TranscriptFragment> controller =
FragmentController.of(new TranscriptFragment(), TestActivity.class);
controller.create().start().resume().pause();
assertThat(controller.get().getView()).isNotNull();
assertThat(controller.get().getActivity()).isNotNull();
assertThat(controller.get().isAdded()).isTrue();
assertThat(controller.get().isResumed()).isFalse();
assertThat(controller.get().getLifecycleEvents())
.containsExactly("onCreate", "onStart", "onResume", "onPause")
.inOrder();
}
@Test
public void isStopped() {
final FragmentController<TranscriptFragment> controller =
FragmentController.of(new TranscriptFragment(), TestActivity.class);
controller.create().start().resume().pause().stop();
assertThat(controller.get().getView()).isNotNull();
assertThat(controller.get().getActivity()).isNotNull();
assertThat(controller.get().isAdded()).isTrue();
assertThat(controller.get().isResumed()).isFalse();
assertThat(controller.get().getLifecycleEvents())
.containsExactly("onCreate", "onStart", "onResume", "onPause", "onStop")
.inOrder();
}
@Test
public void withIntent() {
final Intent intent = generateTestIntent();
final FragmentController<TranscriptFragment> controller =
FragmentController.of(new TranscriptFragment(), TestActivity.class, intent);
controller.create();
shadowOf(getMainLooper()).idle();
final Intent intentInFragment = controller.get().getActivity().getIntent();
assertThat(intentInFragment.getAction()).isEqualTo("test_action");
assertThat(intentInFragment.getExtras().getString("test_key")).isEqualTo("test_value");
}
@Test
public void withArguments() {
final Bundle bundle = generateTestBundle();
final FragmentController<TranscriptFragment> controller =
FragmentController.of(new TranscriptFragment(), TestActivity.class, bundle);
controller.create();
final Bundle args = controller.get().getArguments();
assertThat(args.getString("test_key")).isEqualTo("test_value");
}
@Test
public void withIntentAndArguments() {
final Bundle bundle = generateTestBundle();
final Intent intent = generateTestIntent();
final FragmentController<TranscriptFragment> controller =
FragmentController.of(new TranscriptFragment(), TestActivity.class, intent, bundle);
controller.create();
shadowOf(getMainLooper()).idle();
final Intent intentInFragment = controller.get().getActivity().getIntent();
final Bundle args = controller.get().getArguments();
assertThat(intentInFragment.getAction()).isEqualTo("test_action");
assertThat(intentInFragment.getExtras().getString("test_key")).isEqualTo("test_value");
assertThat(args.getString("test_key")).isEqualTo("test_value");
}
@Test
public void visible() {
final FragmentController<TranscriptFragment> controller =
FragmentController.of(new TranscriptFragment(), TestActivity.class);
controller.create().start().resume();
assertThat(controller.get().isVisible()).isFalse();
controller.visible();
assertThat(controller.get().isVisible()).isTrue();
}
@Test
public void setupFragmentWithFragment_fragmentHasCorrectLifecycle() {
TranscriptFragment fragment = FragmentController.setupFragment(new TranscriptFragment());
assertThat(fragment.getLifecycleEvents())
.containsExactly("onCreate", "onStart", "onResume")
.inOrder();
assertThat(fragment.isVisible()).isTrue();
}
@Test
public void setupFragmentWithFragmentAndActivity_fragmentHasCorrectLifecycle() {
TranscriptFragment fragment =
FragmentController.setupFragment(new TranscriptFragment(), TestActivity.class);
assertThat(fragment.getLifecycleEvents())
.containsExactly("onCreate", "onStart", "onResume")
.inOrder();
assertThat(fragment.isVisible()).isTrue();
}
@Test
public void setupFragmentWithFragmentAndActivityAndBundle_HasCorrectLifecycle() {
Bundle testBundle = generateTestBundle();
TranscriptFragment fragment =
FragmentController.setupFragment(new TranscriptFragment(), TestActivity.class,
testBundle);
assertThat(fragment.getLifecycleEvents())
.containsExactly("onCreate", "onStart", "onResume")
.inOrder();
assertThat(fragment.isVisible()).isTrue();
}
@Test
public void
setupFragmentWithFragment_Activity_ContainViewIdAndBundle_HasCorrectLifecycle() {
Bundle testBundle = generateTestBundle();
TranscriptFragment fragment =
FragmentController.setupFragment(
new TranscriptFragment(),
CustomizedViewIdTestActivity.class,
R.id.custom_activity_view,
testBundle);
assertThat(fragment.getLifecycleEvents())
.containsExactly("onCreate", "onStart", "onResume")
.inOrder();
assertThat(fragment.isVisible()).isTrue();
}
private Intent generateTestIntent() {
final Intent testIntent = new Intent("test_action").putExtra("test_key", "test_value");
return testIntent;
}
private Bundle generateTestBundle() {
final Bundle testBundle = new Bundle();
testBundle.putString("test_key", "test_value");
return testBundle;
}
/** A Fragment which can record lifecycle status for test. */
public static class TranscriptFragment extends Fragment {
public static final List<String> sLifecycleEvents = new ArrayList<>();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sLifecycleEvents.add("onCreate");
}
@Override
public void onStart() {
super.onStart();
sLifecycleEvents.add("onStart");
}
@Override
public void onResume() {
super.onResume();
sLifecycleEvents.add("onResume");
}
@Override
public void onPause() {
super.onPause();
sLifecycleEvents.add("onPause");
}
@Override
public void onStop() {
super.onStop();
sLifecycleEvents.add("onStop");
}
@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_contents, container, false);
}
public List<String> getLifecycleEvents() {
return sLifecycleEvents;
}
public static void clearLifecycleEvents() {
sLifecycleEvents.clear();
}
}
/** A Activity which set a default view for test. */
public static class TestActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout view = new LinearLayout(this);
view.setId(1);
setContentView(view);
}
}
/** A Activity which has a custom view for test. */
public static class CustomizedViewIdTestActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.custom_activity_view);
}
}
}

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/custom_activity_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
</LinearLayout>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tacos"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TACOS"/>
<TextView
android:id="@+id/burritos"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="BURRITOS"/>
</LinearLayout>

View File

@@ -0,0 +1,6 @@
Unit test suite for SettingsLib using Robolectric.
```
$ croot
$ make RunSettingsLibRoboTests -j40
```

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<com.android.settingslib.collapsingtoolbar.widget.CollapsingCoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:id="@+id/id_collapsing_test"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/text_hello_world"
android:text="Hello World!"/>
</com.android.settingslib.collapsingtoolbar.widget.CollapsingCoordinatorLayout>

View File

@@ -0,0 +1,68 @@
<?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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:background="?android:attr/selectableItemBackground"
android:orientation="vertical"
android:clipToPadding="false">
<LinearLayout
android:id="@+id/icon_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="56dp"
android:gravity="start|top"
android:orientation="horizontal"
android:paddingEnd="12dp"
android:paddingTop="16dp"
android:paddingBottom="4dp">
<ImageView
android:id="@android:id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingBottom="8dp"
android:textColor="?android:attr/textColorSecondary"
android:ellipsize="marquee" />
<com.android.settingslib.widget.LinkTextView
android:id="@+id/settingslib_learn_more"
android:text="@string/settingslib_learn_more_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:visibility="gone"
style="@style/TextAppearance.Footer.Title.SettingsLib"/>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,78 @@
/*
* 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.
*/
package com.android.settingslib;
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.content.Context;
import android.view.View;
import android.widget.EditText;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class)
public class CustomEditTextPreferenceComaptTest {
@Mock
private View mView;
private TestPreference mPreference;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mPreference = new TestPreference(RuntimeEnvironment.application);
}
@Test
public void bindDialogView_shouldRequestFocus() {
final String testText = "";
final EditText editText = spy(new EditText(RuntimeEnvironment.application));
editText.setText(testText);
when(mView.findViewById(android.R.id.edit)).thenReturn(editText);
mPreference.onBindDialogView(mView);
verify(editText).requestFocus();
}
@Test
public void getEditText_noDialog_shouldNotCrash() {
ReflectionHelpers.setField(mPreference, "mFragment",
mock(CustomEditTextPreferenceCompat.CustomPreferenceDialogFragment.class));
mPreference.getEditText();
// no crash
}
private static class TestPreference extends CustomEditTextPreferenceCompat {
private TestPreference(Context context) {
super(context);
}
}
}

View File

@@ -0,0 +1,78 @@
/*
* 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.
*/
package com.android.settingslib;
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.content.Context;
import android.view.View;
import android.widget.EditText;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class)
public class CustomEditTextPreferenceTest {
@Mock
private View mView;
private TestPreference mPreference;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mPreference = new TestPreference(RuntimeEnvironment.application);
}
@Test
public void bindDialogView_shouldRequestFocus() {
final String testText = "";
final EditText editText = spy(new EditText(RuntimeEnvironment.application));
editText.setText(testText);
when(mView.findViewById(android.R.id.edit)).thenReturn(editText);
mPreference.onBindDialogView(mView);
verify(editText).requestFocus();
}
@Test
public void getEditText_noDialog_shouldNotCrash() {
ReflectionHelpers.setField(mPreference, "mFragment",
mock(CustomEditTextPreference.CustomPreferenceDialogFragment.class));
mPreference.getEditText();
// no crash
}
private static class TestPreference extends CustomEditTextPreference {
private TestPreference(Context context) {
super(context);
}
}
}

View File

@@ -0,0 +1,74 @@
/*
* 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.
*/
package com.android.settingslib;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.system.StructUtsname;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class DeviceInfoUtilsTest {
private Context mContext;
@Before
public void setup() {
mContext = RuntimeEnvironment.application;
}
@Test
public void formatKernelVersion_regularInputVersion_shouldStripOptionalValues() {
final String sysName = "Linux";
final String nodeName = "localhost";
final String release = "4.4.88-g134be430baab";
final String version = "#1 SMP PREEMPT Tue Dec 31 12:00:00 UTC 2017";
final String machine = "aarch64";
final StructUtsname uname = new StructUtsname(sysName, nodeName, release, version, machine);
final String expected = release + "\n" + "#1 Tue Dec 31 12:00:00 UTC 2017";
assertThat(DeviceInfoUtils.formatKernelVersion(mContext, uname)).isEqualTo(expected);
}
@Test
public void formatKernelVersion_nonRegularInputVersion_shouldBeUnavailable() {
final String sysName = "Linux";
final String nodeName = "localhost";
final String release = "4.4.88-g134be430baab";
final String version = "%@%!asd%#@!$" + "\n " + "fasdfasdfa13ta";
final String machine = "aarch64";
final StructUtsname uname = new StructUtsname(sysName, nodeName, release, version, machine);
final String expected = mContext.getString(R.string.status_unavailable);
assertThat(DeviceInfoUtils.formatKernelVersion(mContext, uname)).isEqualTo(expected);
}
@Test
public void formatKernelVersion_nullInputVersion_shouldBeUnavailable() {
final String expected = mContext.getString(R.string.status_unavailable);
assertThat(DeviceInfoUtils.formatKernelVersion(mContext, null)).isEqualTo(expected);
}
}

View File

@@ -0,0 +1,209 @@
/*
* 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.
*/
package com.android.settingslib;
import static com.android.settingslib.HelpUtils.MENU_HELP;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.R;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
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.content.res.TypedArray;
import android.provider.Settings;
import android.view.Menu;
import android.view.MenuItem;
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;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
/**
* Tests for {@link HelpUtils}.
*/
@RunWith(RobolectricTestRunner.class)
public class HelpUtilsTest {
private static final String TEST_HELP_URL = "intent:#Intent;action=com.android.test;end";
private static final String PACKAGE_NAME_KEY = "package-name-key";
private static final String PACKAGE_NAME_VALUE = "package-name-value";
private static final String HELP_INTENT_EXTRA_KEY = "help-intent-extra";
private static final String HELP_INTENT_NAME_KEY = "help-intent-name";
private static final String FEEDBACK_INTENT_EXTRA_KEY = "feedback-intent-extra";
private static final String FEEDBACK_INTENT_NAME_KEY = "feedback-intent-name";
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@Mock
private Activity mActivity;
@Mock
private PackageManager mPackageManager;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mContext.getResources().getString(R.string.config_helpPackageNameKey))
.thenReturn(PACKAGE_NAME_KEY);
when(mContext.getResources().getString(R.string.config_helpPackageNameValue))
.thenReturn(PACKAGE_NAME_VALUE);
when(mContext.getResources().getString(R.string.config_helpIntentExtraKey))
.thenReturn(HELP_INTENT_EXTRA_KEY);
when(mContext.getResources().getString(R.string.config_helpIntentNameKey))
.thenReturn(HELP_INTENT_NAME_KEY);
when(mContext.getResources().getString(R.string.config_feedbackIntentExtraKey))
.thenReturn(FEEDBACK_INTENT_EXTRA_KEY);
when(mContext.getResources().getString(R.string.config_feedbackIntentNameKey))
.thenReturn(FEEDBACK_INTENT_NAME_KEY);
when(mActivity.getPackageManager()).thenReturn(mPackageManager);
}
@Test
public void addIntentParameters_configTrue_argumentTrue() {
when(mContext.getResources().getBoolean(R.bool.config_sendPackageName)).thenReturn(true);
Intent intent = new Intent();
HelpUtils.addIntentParameters(
mContext, intent, null /* backupContext */, true /* sendPackageName */);
assertThat(intent.getStringArrayExtra(HELP_INTENT_EXTRA_KEY)).asList()
.containsExactly(PACKAGE_NAME_KEY);
assertThat(intent.getStringArrayExtra(HELP_INTENT_NAME_KEY)).asList()
.containsExactly(PACKAGE_NAME_VALUE);
assertThat(intent.getStringArrayExtra(FEEDBACK_INTENT_EXTRA_KEY)).asList()
.containsExactly(PACKAGE_NAME_KEY);
assertThat(intent.getStringArrayExtra(FEEDBACK_INTENT_NAME_KEY)).asList()
.containsExactly(PACKAGE_NAME_VALUE);
}
@Test
public void addIntentParameters_configTrue_argumentFalse() {
when(mContext.getResources().getBoolean(R.bool.config_sendPackageName)).thenReturn(true);
Intent intent = new Intent();
HelpUtils.addIntentParameters(
mContext, intent, null /* backupContext */, false /* sendPackageName */);
assertThat(intent.hasExtra(HELP_INTENT_EXTRA_KEY)).isFalse();
assertThat(intent.hasExtra(HELP_INTENT_NAME_KEY)).isFalse();
assertThat(intent.hasExtra(FEEDBACK_INTENT_EXTRA_KEY)).isFalse();
assertThat(intent.hasExtra(FEEDBACK_INTENT_NAME_KEY)).isFalse();
}
@Test
public void addIntentParameters_configFalse_argumentTrue() {
when(mContext.getResources().getBoolean(R.bool.config_sendPackageName)).thenReturn(false);
Intent intent = new Intent();
HelpUtils.addIntentParameters(
mContext, intent, null /* backupContext */, true /* sendPackageName */);
assertThat(intent.hasExtra(HELP_INTENT_EXTRA_KEY)).isFalse();
assertThat(intent.hasExtra(HELP_INTENT_NAME_KEY)).isFalse();
assertThat(intent.hasExtra(FEEDBACK_INTENT_EXTRA_KEY)).isFalse();
assertThat(intent.hasExtra(FEEDBACK_INTENT_NAME_KEY)).isFalse();
}
@Test
public void addIntentParameters_configFalse_argumentFalse() {
when(mContext.getResources().getBoolean(R.bool.config_sendPackageName)).thenReturn(false);
Intent intent = new Intent();
HelpUtils.addIntentParameters(
mContext, intent, null /* backupContext */, false /* sendPackageName */);
assertThat(intent.hasExtra(HELP_INTENT_EXTRA_KEY)).isFalse();
assertThat(intent.hasExtra(HELP_INTENT_NAME_KEY)).isFalse();
assertThat(intent.hasExtra(FEEDBACK_INTENT_EXTRA_KEY)).isFalse();
assertThat(intent.hasExtra(FEEDBACK_INTENT_NAME_KEY)).isFalse();
}
@Test
public void prepareHelpMenuItem_shouldShowIcon() {
Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
Settings.Global.DEVICE_PROVISIONED, 1);
final Resources res = mock(Resources.class);
final ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.activityInfo = new ActivityInfo();
resolveInfo.activityInfo.applicationInfo = new ApplicationInfo();
resolveInfo.activityInfo.applicationInfo.packageName = "pkg";
resolveInfo.activityInfo.name = "name";
final MenuItem item = mock(MenuItem.class);
when(mActivity.getContentResolver())
.thenReturn(RuntimeEnvironment.application.getContentResolver());
when(mActivity.getResources()).thenReturn(res);
when(mActivity.obtainStyledAttributes(any(int[].class)))
.thenReturn(mock(TypedArray.class));
when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
.thenReturn(resolveInfo);
HelpUtils.prepareHelpMenuItem(mActivity, item, TEST_HELP_URL, "backup_url");
verify(item).setVisible(true);
verify(item).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
@Test
public void prepareHelpMenuItem_noItem_addItem() {
final Menu item = mock(Menu.class);
when(item.findItem(MENU_HELP)).thenReturn(null);
when(item.add(0, MENU_HELP, 0,
com.android.settingslib.widget.help.R.string.help_feedback_label)).thenReturn(
mock(MenuItem.class));
HelpUtils.prepareHelpMenuItem(mActivity, item, TEST_HELP_URL, "backup_url");
HelpUtils.prepareHelpMenuItem(mActivity, item, 0, "backup_url");
verify(item, times(2)).add(0, MENU_HELP, 0,
com.android.settingslib.widget.help.R.string.help_feedback_label);
}
@Test
public void prepareHelpMenuItem_hasItem_notAddItem() {
final Menu item = mock(Menu.class);
when(item.findItem(MENU_HELP)).thenReturn(mock(MenuItem.class));
when(item.add(0, MENU_HELP, 0,
com.android.settingslib.widget.help.R.string.help_feedback_label)).thenReturn(
mock(MenuItem.class));
HelpUtils.prepareHelpMenuItem(mActivity, item, TEST_HELP_URL, "backup_url");
HelpUtils.prepareHelpMenuItem(mActivity, item, 0, "backup_url");
verify(item, never()).add(0, MENU_HELP, 0,
com.android.settingslib.widget.help.R.string.help_feedback_label);
}
}

View File

@@ -0,0 +1,59 @@
/*
* 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.settingslib;
import static com.google.common.truth.Truth.assertThat;
import com.android.settingslib.mobile.TelephonyIcons;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class MobileNetworkTypeIconsTest {
@Test
public void getNetworkTypeIcon_hPlus_returnsHPlus() {
MobileNetworkTypeIcon icon =
MobileNetworkTypeIcons.getNetworkTypeIcon(TelephonyIcons.H_PLUS);
assertThat(icon.getName()).isEqualTo(TelephonyIcons.H_PLUS.name);
assertThat(icon.getIconResId()).isEqualTo(TelephonyIcons.ICON_H_PLUS);
}
@Test
public void getNetworkTypeIcon_fourG_returnsFourG() {
MobileNetworkTypeIcon icon =
MobileNetworkTypeIcons.getNetworkTypeIcon(TelephonyIcons.FOUR_G);
assertThat(icon.getName()).isEqualTo(TelephonyIcons.FOUR_G.name);
assertThat(icon.getIconResId()).isEqualTo(TelephonyIcons.ICON_4G);
}
@Test
public void getNetworkTypeIcon_unknown_returnsUnknown() {
SignalIcon.MobileIconGroup unknownGroup = new SignalIcon.MobileIconGroup(
"testUnknownNameHere", /* dataContentDesc= */ 45, /* dataType= */ 6);
MobileNetworkTypeIcon icon = MobileNetworkTypeIcons.getNetworkTypeIcon(unknownGroup);
assertThat(icon.getName()).isEqualTo("testUnknownNameHere");
assertThat(icon.getIconResId()).isEqualTo(6);
assertThat(icon.getContentDescriptionResId()).isEqualTo(45);
}
}

View File

@@ -0,0 +1,180 @@
/*
* 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.settingslib;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.view.LayoutInflater;
import android.widget.CompoundButton;
import android.widget.LinearLayout;
import androidx.preference.Preference.OnPreferenceChangeListener;
import androidx.preference.PreferenceViewHolder;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.testutils.shadow.ShadowInteractionJankMonitor;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowInteractionJankMonitor.class})
public class PrimarySwitchPreferenceTest {
private Context mContext;
private PrimarySwitchPreference mPreference;
private PreferenceViewHolder mHolder;
private LinearLayout mWidgetView;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mPreference = new PrimarySwitchPreference(mContext);
LayoutInflater inflater = LayoutInflater.from(mContext);
mHolder = PreferenceViewHolder.createInstanceForTests(inflater.inflate(
com.android.settingslib.widget.preference.twotarget.R.layout.preference_two_target,
null));
mWidgetView = mHolder.itemView.findViewById(android.R.id.widget_frame);
inflater.inflate(androidx.preference.R.layout.preference_widget_switch_compat, mWidgetView,
true);
}
@Test
public void setChecked_shouldUpdateButtonCheckedState() {
final CompoundButton toggle =
(CompoundButton) mHolder.findViewById(androidx.preference.R.id.switchWidget);
mPreference.onBindViewHolder(mHolder);
mPreference.setChecked(true);
assertThat(toggle.isChecked()).isTrue();
mPreference.setChecked(false);
assertThat(toggle.isChecked()).isFalse();
}
@Test
public void setSwitchEnabled_shouldUpdateButtonEnabledState() {
final CompoundButton toggle =
(CompoundButton) mHolder.findViewById(androidx.preference.R.id.switchWidget);
mPreference.onBindViewHolder(mHolder);
mPreference.setSwitchEnabled(true);
assertThat(toggle.isEnabled()).isTrue();
mPreference.setSwitchEnabled(false);
assertThat(toggle.isEnabled()).isFalse();
}
@Test
public void setSwitchEnabled_shouldUpdateButtonEnabledState_beforeViewBound() {
final CompoundButton toggle =
(CompoundButton) mHolder.findViewById(androidx.preference.R.id.switchWidget);
mPreference.setSwitchEnabled(false);
mPreference.onBindViewHolder(mHolder);
assertThat(toggle.isEnabled()).isFalse();
}
@Test
public void clickWidgetView_shouldToggleButton() {
assertThat(mWidgetView).isNotNull();
final CompoundButton toggle =
(CompoundButton) mHolder.findViewById(androidx.preference.R.id.switchWidget);
mPreference.onBindViewHolder(mHolder);
toggle.performClick();
assertThat(toggle.isChecked()).isTrue();
toggle.performClick();
assertThat(toggle.isChecked()).isFalse();
}
@Test
public void clickWidgetView_shouldNotToggleButtonIfDisabled() {
assertThat(mWidgetView).isNotNull();
final CompoundButton toggle =
(CompoundButton) mHolder.findViewById(androidx.preference.R.id.switchWidget);
mPreference.onBindViewHolder(mHolder);
toggle.setEnabled(false);
mWidgetView.performClick();
assertThat(toggle.isChecked()).isFalse();
}
@Test
public void clickWidgetView_shouldNotifyPreferenceChanged() {
final CompoundButton toggle =
(CompoundButton) mHolder.findViewById(androidx.preference.R.id.switchWidget);
final OnPreferenceChangeListener listener = mock(OnPreferenceChangeListener.class);
mPreference.setOnPreferenceChangeListener(listener);
mPreference.onBindViewHolder(mHolder);
mPreference.setChecked(false);
toggle.performClick();
verify(listener).onPreferenceChange(mPreference, true);
mPreference.setChecked(true);
toggle.performClick();
verify(listener).onPreferenceChange(mPreference, false);
}
@Test
public void setDisabledByAdmin_hasEnforcedAdmin_shouldDisableButton() {
final CompoundButton toggle =
(CompoundButton) mHolder.findViewById(androidx.preference.R.id.switchWidget);
toggle.setEnabled(true);
mPreference.onBindViewHolder(mHolder);
mPreference.setDisabledByAdmin(mock(EnforcedAdmin.class));
assertThat(toggle.isEnabled()).isFalse();
}
@Test
public void setDisabledByAdmin_noEnforcedAdmin_shouldEnableButton() {
final CompoundButton toggle =
(CompoundButton) mHolder.findViewById(androidx.preference.R.id.switchWidget);
toggle.setEnabled(false);
mPreference.onBindViewHolder(mHolder);
mPreference.setDisabledByAdmin(null);
assertThat(toggle.isEnabled()).isTrue();
}
@Test
public void onBindViewHolder_toggleButtonShouldHaveContentDescription() {
final CompoundButton toggle =
(CompoundButton) mHolder.findViewById(androidx.preference.R.id.switchWidget);
final String label = "TestButton";
mPreference.setTitle(label);
mPreference.onBindViewHolder(mHolder);
assertThat(toggle.getContentDescription()).isEqualTo(label);
}
}

View File

@@ -0,0 +1,348 @@
/*
* 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.
*/
package com.android.settingslib;
import static android.app.admin.DevicePolicyManager.EXTRA_RESTRICTION;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
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.doReturn;
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.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.os.UserManager;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import java.util.Arrays;
import java.util.Collections;
@RunWith(RobolectricTestRunner.class)
public class RestrictedLockUtilsTest {
@Mock
private Context mContext;
@Mock
private DevicePolicyManager mDevicePolicyManager;
@Mock
private UserManager mUserManager;
@Mock
private PackageManager mPackageManager;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private RestrictedLockUtilsInternal.Proxy mProxy;
private final int mUserId = 194;
private final int mProfileId = 160;
private final ComponentName mAdmin1 = new ComponentName("admin1", "admin1class");
private final ComponentName mAdmin2 = new ComponentName("admin2", "admin2class");
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE))
.thenReturn(mDevicePolicyManager);
when(mContext.getSystemService(DevicePolicyManager.class))
.thenReturn(mDevicePolicyManager);
when(mContext.getSystemService(Context.USER_SERVICE))
.thenReturn(mUserManager);
when(mContext.getPackageManager())
.thenReturn(mPackageManager);
RestrictedLockUtilsInternal.sProxy = mProxy;
}
@Test
public void checkIfRestrictionEnforced_deviceOwner()
throws PackageManager.NameNotFoundException {
UserManager.EnforcingUser enforcingUser = new UserManager.EnforcingUser(mUserId,
UserManager.RESTRICTION_SOURCE_DEVICE_OWNER);
final String userRestriction = UserManager.DISALLOW_UNINSTALL_APPS;
when(mUserManager.getUserRestrictionSources(userRestriction,
UserHandle.of(mUserId))).
thenReturn(Collections.singletonList(enforcingUser));
when(mContext.createPackageContextAsUser(any(), eq(0),
eq(UserHandle.of(mUserId))))
.thenReturn(mContext);
setUpDeviceOwner(mAdmin1, mUserId);
EnforcedAdmin enforcedAdmin = RestrictedLockUtilsInternal
.checkIfRestrictionEnforced(mContext, userRestriction, mUserId);
assertThat(enforcedAdmin).isNotNull();
assertThat(enforcedAdmin.enforcedRestriction).isEqualTo(userRestriction);
assertThat(enforcedAdmin.component).isEqualTo(mAdmin1);
}
@Test
public void checkIfRestrictionEnforced_profileOwner()
throws PackageManager.NameNotFoundException {
UserManager.EnforcingUser enforcingUser = new UserManager.EnforcingUser(mUserId,
UserManager.RESTRICTION_SOURCE_PROFILE_OWNER);
final String userRestriction = UserManager.DISALLOW_UNINSTALL_APPS;
when(mUserManager.getUserRestrictionSources(userRestriction,
UserHandle.of(mUserId))).
thenReturn(Collections.singletonList(enforcingUser));
when(mContext.createPackageContextAsUser(any(), eq(0),
eq(UserHandle.of(mUserId))))
.thenReturn(mContext);
setUpProfileOwner(mAdmin1);
EnforcedAdmin enforcedAdmin = RestrictedLockUtilsInternal
.checkIfRestrictionEnforced(mContext, userRestriction, mUserId);
assertThat(enforcedAdmin).isNotNull();
assertThat(enforcedAdmin.enforcedRestriction).isEqualTo(userRestriction);
assertThat(enforcedAdmin.component).isEqualTo(mAdmin1);
}
@Test
public void checkIfDevicePolicyServiceDisabled_noEnforceAdminForManagedProfile() {
when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(null);
final EnforcedAdmin enforcedAdmin = RestrictedLockUtilsInternal
.checkIfAccountManagementDisabled(mContext, "account_type", mUserId);
assertThat(enforcedAdmin).isEqualTo(null);
}
@Test
public void checkIfDeviceAdminFeatureDisabled_noEnforceAdminForManagedProfile() {
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN))
.thenReturn(false);
final EnforcedAdmin enforcedAdmin = RestrictedLockUtilsInternal
.checkIfAccountManagementDisabled(mContext, "account_type", mUserId);
assertThat(enforcedAdmin).isEqualTo(null);
}
@Test
public void checkIfKeyguardFeaturesDisabled_noEnforcedAdminForManagedProfile() {
setUpManagedProfile(mUserId, new ComponentName[] {mAdmin1, mAdmin2});
final EnforcedAdmin enforcedAdmin = RestrictedLockUtilsInternal
.checkIfKeyguardFeaturesDisabled(mContext, KEYGUARD_DISABLE_FINGERPRINT, mUserId);
assertThat(enforcedAdmin).isEqualTo(null);
}
@Test
public void checkIfKeyguardFeaturesDisabled_oneEnforcedAdminForManagedProfile() {
setUpManagedProfile(mUserId, new ComponentName[] {mAdmin1, mAdmin2});
when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin1, mUserId))
.thenReturn(KEYGUARD_DISABLE_FINGERPRINT);
final EnforcedAdmin enforcedAdmin = RestrictedLockUtilsInternal
.checkIfKeyguardFeaturesDisabled(mContext, KEYGUARD_DISABLE_FINGERPRINT, mUserId);
assertThat(enforcedAdmin).isEqualTo(new EnforcedAdmin(mAdmin1, UserHandle.of(mUserId)));
}
@Test
public void checkIfKeyguardFeaturesDisabled_multipleEnforcedAdminForManagedProfile() {
setUpManagedProfile(mUserId, new ComponentName[] {mAdmin1, mAdmin2});
when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin1, mUserId))
.thenReturn(KEYGUARD_DISABLE_REMOTE_INPUT);
when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin2, mUserId))
.thenReturn(KEYGUARD_DISABLE_REMOTE_INPUT);
final EnforcedAdmin enforcedAdmin = RestrictedLockUtilsInternal
.checkIfKeyguardFeaturesDisabled(mContext, KEYGUARD_DISABLE_REMOTE_INPUT, mUserId);
assertThat(enforcedAdmin).isEqualTo(EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN);
}
@Test
public void checkIfKeyguardFeaturesAreDisabled_doesMatchAllowedFeature_unifiedManagedProfile() {
UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1});
UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2});
when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(userInfo, profileInfo));
when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin1, mUserId))
.thenReturn(KEYGUARD_DISABLE_FEATURES_NONE);
when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin2, mProfileId))
.thenReturn(KEYGUARD_DISABLE_FINGERPRINT);
// Querying the parent should return the policy, since it affects the parent.
EnforcedAdmin parent = RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(
mContext, KEYGUARD_DISABLE_FINGERPRINT, mUserId);
assertThat(parent).isEqualTo(new EnforcedAdmin(mAdmin2, UserHandle.of(mProfileId)));
// Querying the child should return that too.
EnforcedAdmin profile = RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(
mContext, KEYGUARD_DISABLE_FINGERPRINT, mProfileId);
assertThat(profile).isEqualTo(new EnforcedAdmin(mAdmin2, UserHandle.of(mProfileId)));
// Querying for some unrelated feature should return nothing. Nothing!
assertThat(RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(
mContext, KEYGUARD_DISABLE_REMOTE_INPUT, mUserId)).isNull();
assertThat(RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(
mContext, KEYGUARD_DISABLE_REMOTE_INPUT, mProfileId)).isNull();
}
@Test
public void checkIfKeyguardFeaturesAreDisabled_notMatchOtherFeatures_unifiedManagedProfile() {
UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1});
UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2});
when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(userInfo, profileInfo));
when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin1, mUserId))
.thenReturn(KEYGUARD_DISABLE_FEATURES_NONE);
when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin2, mProfileId))
.thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
// Querying the parent should not return the policy, because it's not a policy that should
// affect parents even when the lock screen is unified.
EnforcedAdmin primary = RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(
mContext, KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS, mUserId);
assertThat(primary).isNull();
// Querying the child should still return the policy.
EnforcedAdmin profile = RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(
mContext, KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS, mProfileId);
assertThat(profile).isEqualTo(new EnforcedAdmin(mAdmin2, UserHandle.of(mProfileId)));
}
@Test
public void checkIfKeyguardFeaturesAreDisabled_onlyMatchesProfile_separateManagedProfile() {
UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1});
UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2});
when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(userInfo, profileInfo));
when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin1, mUserId))
.thenReturn(KEYGUARD_DISABLE_FEATURES_NONE);
when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin2, mProfileId))
.thenReturn(KEYGUARD_DISABLE_FINGERPRINT);
// Crucially for this test, isSeparateWorkChallengeEnabled => true.
doReturn(true).when(mProxy).isSeparateProfileChallengeEnabled(any(), eq(mProfileId));
// Querying the parent should not return the policy, even though it's shared by default,
// because the parent doesn't share a lock screen with the profile any more.
EnforcedAdmin parent = RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(
mContext, KEYGUARD_DISABLE_FINGERPRINT, mUserId);
assertThat(parent).isNull();
// Querying the child should still return the policy.
EnforcedAdmin profile = RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(
mContext, KEYGUARD_DISABLE_FINGERPRINT, mProfileId);
assertThat(profile).isEqualTo(new EnforcedAdmin(mAdmin2, UserHandle.of(mProfileId)));
}
/**
* This test works great. The real world implementation is sketchy though.
* <p>
* DevicePolicyManager.getParentProfileInstance(UserInfo) does not do what it looks like it does
* (which would be to get an instance for the parent of the user that's passed in to it.)
* <p>
* Instead it just always returns a parent instance for the current user.
* <p>
* Still, the test works.
*/
@Test
public void checkIfKeyguardFeaturesAreDisabled_onlyMatchesParent_profileParentPolicy() {
UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1});
UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2});
when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(userInfo, profileInfo));
when(mProxy.getParentProfileInstance(any(DevicePolicyManager.class), any())
.getKeyguardDisabledFeatures(mAdmin2, mProfileId))
.thenReturn(KEYGUARD_DISABLE_FINGERPRINT);
// Parent should get the policy.
EnforcedAdmin parent = RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(
mContext, KEYGUARD_DISABLE_FINGERPRINT, mUserId);
assertThat(parent).isEqualTo(new EnforcedAdmin(mAdmin2, UserHandle.of(mProfileId)));
// Profile should not get the policy.
EnforcedAdmin profile = RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(
mContext, KEYGUARD_DISABLE_FINGERPRINT, mProfileId);
assertThat(profile).isNull();
}
@Test
public void sendShowAdminSupportDetailsIntent_extraRestrictionProvided() {
EnforcedAdmin enforcedAdmin = new EnforcedAdmin();
enforcedAdmin.enforcedRestriction = "Fake";
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, enforcedAdmin);
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext).startActivityAsUser(intentCaptor.capture(), any());
assertThat(intentCaptor.getValue().getExtra(EXTRA_RESTRICTION)).isEqualTo("Fake");
}
@Test
public void sendShowAdminSupportDetailsIntent_noExtraRestriction() {
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, null);
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext).startActivityAsUser(intentCaptor.capture(), any());
assertThat(intentCaptor.getValue().getExtra(EXTRA_RESTRICTION)).isNull();
}
private UserInfo setUpUser(int userId, ComponentName[] admins) {
UserInfo userInfo = new UserInfo(userId, "primary", 0);
when(mUserManager.getUserInfo(userId)).thenReturn(userInfo);
setUpActiveAdmins(userId, admins);
return userInfo;
}
private UserInfo setUpManagedProfile(int userId, ComponentName[] admins) {
UserInfo userInfo = new UserInfo(userId, "profile", UserInfo.FLAG_MANAGED_PROFILE);
when(mUserManager.getUserInfo(userId)).thenReturn(userInfo);
setUpActiveAdmins(userId, admins);
return userInfo;
}
private void setUpActiveAdmins(int userId, ComponentName[] activeAdmins) {
when(mDevicePolicyManager.getActiveAdminsAsUser(userId))
.thenReturn(Arrays.asList(activeAdmins));
}
private void setUpDeviceOwner(ComponentName admin, int userId) {
when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()).thenReturn(admin);
when(mDevicePolicyManager.getDeviceOwnerUser()).thenReturn(UserHandle.of(userId));
}
private void setUpProfileOwner(ComponentName admin) {
when(mDevicePolicyManager.getProfileOwner()).thenReturn(admin);
}
}

View File

@@ -0,0 +1,160 @@
/*
* 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.
*/
package com.android.settingslib;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyResourcesManager;
import android.content.Context;
import android.content.Intent;
import android.view.View;
import android.widget.TextView;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class RestrictedPreferenceHelperTest {
@Mock
private Context mContext;
@Mock
private Preference mPreference;
@Mock
private DevicePolicyManager mDevicePolicyManager;
@Mock
private DevicePolicyResourcesManager mDevicePolicyResourcesManager;
@Mock
private RestrictedTopLevelPreference mRestrictedTopLevelPreference;
private PreferenceViewHolder mViewHolder;
private RestrictedPreferenceHelper mHelper;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
doReturn(mDevicePolicyResourcesManager).when(mDevicePolicyManager)
.getResources();
doReturn(mDevicePolicyManager).when(mContext)
.getSystemService(DevicePolicyManager.class);
mViewHolder = PreferenceViewHolder.createInstanceForTests(mock(View.class));
mHelper = new RestrictedPreferenceHelper(mContext, mPreference, null);
}
@Test
public void bindPreference_disabled_shouldDisplayDisabledSummary() {
final TextView summaryView = mock(TextView.class, RETURNS_DEEP_STUBS);
when(mViewHolder.itemView.findViewById(android.R.id.summary))
.thenReturn(summaryView);
when(summaryView.getContext().getText(R.string.disabled_by_admin_summary_text))
.thenReturn("test");
when(mDevicePolicyResourcesManager.getString(any(), any())).thenReturn("test");
mHelper.useAdminDisabledSummary(true);
mHelper.setDisabledByAdmin(new RestrictedLockUtils.EnforcedAdmin());
mHelper.onBindViewHolder(mViewHolder);
verify(summaryView).setText("test");
verify(summaryView, never()).setVisibility(View.GONE);
}
@Test
public void bindPreference_disabledByEcm_shouldDisplayDisabledSummary() {
final TextView summaryView = mock(TextView.class, RETURNS_DEEP_STUBS);
when(mViewHolder.itemView.findViewById(android.R.id.summary))
.thenReturn(summaryView);
mHelper.setDisabledByEcm(mock(Intent.class));
mHelper.onBindViewHolder(mViewHolder);
verify(mPreference).setSummary(R.string.disabled_by_app_ops_text);
verify(summaryView, never()).setVisibility(View.GONE);
}
@Test
public void bindPreference_notDisabled_shouldNotHideSummary() {
final TextView summaryView = mock(TextView.class, RETURNS_DEEP_STUBS);
when(mViewHolder.itemView.findViewById(android.R.id.summary))
.thenReturn(summaryView);
when(summaryView.getContext().getText(R.string.disabled_by_admin_summary_text))
.thenReturn("test");
when(mDevicePolicyResourcesManager.getString(any(), any())).thenReturn("test");
when(summaryView.getText()).thenReturn("test");
mHelper.useAdminDisabledSummary(true);
mHelper.setDisabledByAdmin(null);
mHelper.onBindViewHolder(mViewHolder);
verify(summaryView).setText(null);
verify(summaryView, never()).setVisibility(View.GONE);
}
@Test
public void setDisabledByAdmin_RestrictedPreference_shouldDisablePreference() {
mHelper.setDisabledByAdmin(new RestrictedLockUtils.EnforcedAdmin());
verify(mPreference).setEnabled(false);
}
@Test
public void setDisabledByAdmin_TopLevelRestrictedPreference_shouldNotDisablePreference() {
mHelper = new RestrictedPreferenceHelper(mContext,
mRestrictedTopLevelPreference, /* attrs= */ null);
mHelper.setDisabledByAdmin(new RestrictedLockUtils.EnforcedAdmin());
verify(mRestrictedTopLevelPreference, never()).setEnabled(false);
}
/**
* Tests if the instance of {@link RestrictedLockUtils.EnforcedAdmin} is received by
* {@link RestrictedPreferenceHelper#setDisabledByAdmin(RestrictedLockUtils.EnforcedAdmin)} as a
* copy or as a reference.
*/
@Test
public void setDisabledByAdmin_disablePreference_receivedEnforcedAdminIsNotAReference() {
RestrictedLockUtils.EnforcedAdmin enforcedAdmin =
new RestrictedLockUtils.EnforcedAdmin(/* component */ null,
/* enforcedRestriction */ "some_restriction",
/* userHandle */ null);
mHelper.setDisabledByAdmin(enforcedAdmin);
// If `setDisabledByAdmin` stored `enforcedAdmin` as a reference, then the following
// assignment would be propagated.
enforcedAdmin.enforcedRestriction = null;
assertThat(mHelper.mEnforcedAdmin.enforcedRestriction).isEqualTo("some_restriction");
assertThat(mHelper.isDisabledByAdmin()).isTrue();
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2019 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.settingslib;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.preference.PreferenceViewHolder;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class RestrictedSwitchPreferenceTest {
private static final int SIZE = 50;
private RestrictedSwitchPreference mPreference;
private Context mContext;
private PreferenceViewHolder mViewHolder;
private View mRootView;
private ImageView mImageView;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mPreference = new RestrictedSwitchPreference(mContext);
mRootView = View.inflate(mContext, R.layout.restricted_switch_preference,
null /* parent */);
mViewHolder = PreferenceViewHolder.createInstanceForTests(mRootView);
mImageView = (ImageView) mViewHolder.findViewById(android.R.id.icon);
}
@Test
public void onBindViewHolder_setIconSize_shouldHaveCorrectLayoutParam() {
mPreference.setIconSize(SIZE);
mPreference.onBindViewHolder(mViewHolder);
assertThat(mImageView.getLayoutParams().height).isEqualTo(SIZE);
assertThat(mImageView.getLayoutParams().width).isEqualTo(SIZE);
}
@Test
public void onBindViewHolder_notSetIconSize_shouldHaveCorrectLayoutParam() {
mPreference.onBindViewHolder(mViewHolder);
assertThat(mImageView.getLayoutParams().height).isEqualTo(
ViewGroup.LayoutParams.WRAP_CONTENT);
assertThat(mImageView.getLayoutParams().width).isEqualTo(
ViewGroup.LayoutParams.WRAP_CONTENT);
}
}

View File

@@ -0,0 +1,74 @@
/*
* 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.settingslib;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
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 org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class)
public class RestrictedTopLevelPreferenceTest {
private Context mContext;
private RestrictedTopLevelPreference mPreference;
private RestrictedPreferenceHelper mHelper;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mPreference = spy(new RestrictedTopLevelPreference(mContext));
mHelper = spy(new RestrictedPreferenceHelper(mContext, mPreference, null));
ReflectionHelpers.setField(mPreference, "mHelper", mHelper);
}
@Test
public void setEnabled_disabledByAdmin_shouldCallSetDisabledByAdmin() {
when(mHelper.isDisabledByAdmin()).thenReturn(true);
mPreference.setEnabled(true);
verify(mHelper).setDisabledByAdmin(any());
}
@Test
public void setEnabled_notDisabledByAdmin_shouldNotCallSetDisabledByAdmin() {
when(mHelper.isDisabledByAdmin()).thenReturn(false);
mPreference.setEnabled(true);
verify(mHelper, never()).setDisabledByAdmin(any());
}
@Test
public void setDisabledByAdmin_shouldNotCallSetEnabled() {
mPreference.setDisabledByAdmin(new RestrictedLockUtils.EnforcedAdmin());
verify(mPreference, never()).setEnabled(anyBoolean());
}
}

View File

@@ -0,0 +1,142 @@
/*
* 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.
*/
package com.android.settingslib;
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.net.ConnectivityManager;
import android.os.UserHandle;
import android.os.UserManager;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class TetherUtilTest {
private Context mContext;
@Mock
private ConnectivityManager mConnectivityManager;
@Mock
private UserManager mUserManager;
@Before
public void setUp() {
mContext = spy(RuntimeEnvironment.application);
MockitoAnnotations.initMocks(this);
doReturn(mConnectivityManager)
.when(mContext).getSystemService(Context.CONNECTIVITY_SERVICE);
doReturn(mUserManager)
.when(mContext).getSystemService(Context.USER_SERVICE);
}
@Test
public void isTetherAvailable_supported_configDisallowed_hasUserRestriction_returnTrue() {
setupIsTetherAvailable(true, true, true);
assertThat(TetherUtil.isTetherAvailable(mContext)).isFalse();
}
@Test
public void isTetherAvailable_notSupported_configDisallowed_hasUserRestriction_returnTrue() {
setupIsTetherAvailable(false, true, true);
assertThat(TetherUtil.isTetherAvailable(mContext)).isFalse();
}
@Test
public void isTetherAvailable_supported_configAllowed_hasUserRestriction_returnTrue() {
setupIsTetherAvailable(true, false, true);
assertThat(TetherUtil.isTetherAvailable(mContext)).isFalse();
}
@Test
public void isTetherAvailable_notSupported_configAllowed_hasUserRestriction_returnFalse() {
setupIsTetherAvailable(false, false, true);
assertThat(TetherUtil.isTetherAvailable(mContext)).isFalse();
}
@Test
public void isTetherAvailable_supported_configDisallowed_noUserRestriction_returnTrue() {
setupIsTetherAvailable(true, true, false);
assertThat(TetherUtil.isTetherAvailable(mContext)).isTrue();
}
@Test
public void isTetherAvailable_notSupported_configDisallowed_noUserRestriction_returnTrue() {
setupIsTetherAvailable(false, true, false);
assertThat(TetherUtil.isTetherAvailable(mContext)).isTrue();
}
@Test
public void isTetherAvailable_supported_configAllowed_noUserRestriction_returnTrue() {
setupIsTetherAvailable(true, false, false);
assertThat(TetherUtil.isTetherAvailable(mContext)).isTrue();
}
@Test
public void isTetherAvailable_notSupported_configAllowed_noUserRestriction_returnFalse() {
setupIsTetherAvailable(false, false, false);
assertThat(TetherUtil.isTetherAvailable(mContext)).isFalse();
}
private void setupIsTetherAvailable(boolean tetherSupported, boolean configAllowed,
boolean hasBseUserRestriction) {
when(mConnectivityManager.isTetheringSupported()).thenReturn(tetherSupported);
// For RestrictedLockUtils.checkIfRestrictionEnforced
final int userId = UserHandle.myUserId();
List<UserManager.EnforcingUser> enforcingUsers = new ArrayList<>();
if (configAllowed) {
// Add two enforcing users so that RestrictedLockUtils.checkIfRestrictionEnforced
// returns non-null
enforcingUsers.add(new UserManager.EnforcingUser(userId,
UserManager.RESTRICTION_SOURCE_DEVICE_OWNER));
enforcingUsers.add(new UserManager.EnforcingUser(userId,
UserManager.RESTRICTION_SOURCE_PROFILE_OWNER));
}
when(mUserManager.getUserRestrictionSources(
UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.of(userId)))
.thenReturn(enforcingUsers);
// For RestrictedLockUtils.hasBaseUserRestriction
when(mUserManager.hasBaseUserRestriction(
UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.of(userId)))
.thenReturn(hasBseUserRestriction);
}
}

View File

@@ -0,0 +1,654 @@
/*
* 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.
*/
package com.android.settingslib;
import static com.android.settingslib.Utils.STORAGE_MANAGER_ENABLED_PROPERTY;
import static com.android.settingslib.Utils.WIRELESS_CHARGING_DEFAULT_TIMESTAMP;
import static com.android.settingslib.Utils.shouldShowWirelessChargingWarningTip;
import static com.android.settingslib.Utils.updateWirelessChargingNotificationTimestamp;
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.spy;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;
import android.hardware.usb.flags.Flags;
import android.location.LocationManager;
import android.media.AudioManager;
import android.os.BatteryManager;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.telephony.AccessNetworkConstants;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.ServiceState;
import android.text.TextUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.shadows.ShadowSettings;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {UtilsTest.ShadowLocationManager.class})
public class UtilsTest {
private static final double[] TEST_PERCENTAGES = {0, 0.4, 0.5, 0.6, 49, 49.3, 49.8, 50, 100};
private static final String TAG = "UtilsTest";
private static final String PERCENTAGE_0 = "0%";
private static final String PERCENTAGE_1 = "1%";
private static final String PERCENTAGE_49 = "49%";
private static final String PERCENTAGE_50 = "50%";
private static final String PERCENTAGE_100 = "100%";
private static final long CURRENT_TIMESTAMP = System.currentTimeMillis();
private AudioManager mAudioManager;
private Context mContext;
@Mock private LocationManager mLocationManager;
@Mock private ServiceState mServiceState;
@Mock private NetworkRegistrationInfo mNetworkRegistrationInfo;
@Mock private UsbPort mUsbPort;
@Mock private UsbManager mUsbManager;
@Mock private UsbPortStatus mUsbPortStatus;
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
when(mContext.getSystemService(Context.LOCATION_SERVICE)).thenReturn(mLocationManager);
when(mContext.getSystemService(UsbManager.class)).thenReturn(mUsbManager);
ShadowSettings.ShadowSecure.reset();
mAudioManager = mContext.getSystemService(AudioManager.class);
}
@After
public void reset() {
Settings.Secure.putInt(
mContext.getContentResolver(), Utils.INCOMPATIBLE_CHARGER_WARNING_DISABLED, 0);
}
@Test
public void testUpdateLocationEnabled() {
int currentUserId = ActivityManager.getCurrentUser();
Utils.updateLocationEnabled(
mContext, true, currentUserId, Settings.Secure.LOCATION_CHANGER_QUICK_SETTINGS);
assertThat(
Settings.Secure.getInt(
mContext.getContentResolver(),
Settings.Secure.LOCATION_CHANGER,
Settings.Secure.LOCATION_CHANGER_UNKNOWN))
.isEqualTo(Settings.Secure.LOCATION_CHANGER_QUICK_SETTINGS);
}
@Test
public void testFormatPercentage_RoundTrue_RoundUpIfPossible() {
final String[] expectedPercentages = {
PERCENTAGE_0,
PERCENTAGE_0,
PERCENTAGE_1,
PERCENTAGE_1,
PERCENTAGE_49,
PERCENTAGE_49,
PERCENTAGE_50,
PERCENTAGE_50,
PERCENTAGE_100
};
for (int i = 0, size = TEST_PERCENTAGES.length; i < size; i++) {
final String percentage = Utils.formatPercentage(TEST_PERCENTAGES[i], true);
assertThat(percentage).isEqualTo(expectedPercentages[i]);
}
}
@Test
public void testFormatPercentage_RoundFalse_NoRound() {
final String[] expectedPercentages = {
PERCENTAGE_0,
PERCENTAGE_0,
PERCENTAGE_0,
PERCENTAGE_0,
PERCENTAGE_49,
PERCENTAGE_49,
PERCENTAGE_49,
PERCENTAGE_50,
PERCENTAGE_100
};
for (int i = 0, size = TEST_PERCENTAGES.length; i < size; i++) {
final String percentage = Utils.formatPercentage(TEST_PERCENTAGES[i], false);
assertThat(percentage).isEqualTo(expectedPercentages[i]);
}
}
@Test
public void testGetDefaultStorageManagerDaysToRetain_storageManagerDaysToRetainUsesResources() {
Resources resources = mock(Resources.class);
when(resources.getInteger(
eq(
com.android.internal.R.integer
.config_storageManagerDaystoRetainDefault)))
.thenReturn(60);
assertThat(Utils.getDefaultStorageManagerDaysToRetain(resources)).isEqualTo(60);
}
@Test
public void testIsStorageManagerEnabled_UsesSystemProperties() {
SystemProperties.set(STORAGE_MANAGER_ENABLED_PROPERTY, "true");
assertThat(Utils.isStorageManagerEnabled(mContext)).isTrue();
}
private static ArgumentMatcher<Intent> actionMatches(String expected) {
return intent -> TextUtils.equals(expected, intent.getAction());
}
@Implements(value = LocationManager.class)
public static class ShadowLocationManager {
@Implementation
public void setLocationEnabledForUser(boolean enabled, UserHandle userHandle) {
// Do nothing
}
}
@Test
public void isAudioModeOngoingCall_modeInCommunication_returnTrue() {
mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
assertThat(Utils.isAudioModeOngoingCall(mContext)).isTrue();
}
@Test
public void isAudioModeOngoingCall_modeInCall_returnTrue() {
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
assertThat(Utils.isAudioModeOngoingCall(mContext)).isTrue();
}
@Test
public void isAudioModeOngoingCall_modeRingtone_returnTrue() {
mAudioManager.setMode(AudioManager.MODE_RINGTONE);
assertThat(Utils.isAudioModeOngoingCall(mContext)).isTrue();
}
@Test
public void isAudioModeOngoingCall_modeNormal_returnFalse() {
mAudioManager.setMode(AudioManager.MODE_NORMAL);
assertThat(Utils.isAudioModeOngoingCall(mContext)).isFalse();
}
@Test
public void isInService_servicestateNull_returnFalse() {
assertThat(Utils.isInService(null)).isFalse();
}
@Test
public void isInService_voiceInService_returnTrue() {
when(mServiceState.getVoiceRegState()).thenReturn(ServiceState.STATE_IN_SERVICE);
assertThat(Utils.isInService(mServiceState)).isTrue();
}
@Test
public void isInService_voiceOutOfServiceDataInService_returnTrue() {
when(mServiceState.getVoiceRegState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
when(mServiceState.getDataRegistrationState()).thenReturn(ServiceState.STATE_IN_SERVICE);
when(mServiceState.getNetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_PS,
AccessNetworkConstants.TRANSPORT_TYPE_WWAN))
.thenReturn(mNetworkRegistrationInfo);
when(mNetworkRegistrationInfo.isInService()).thenReturn(true);
assertThat(Utils.isInService(mServiceState)).isTrue();
}
@Test
public void isInService_voiceOutOfServiceDataInServiceOnIwLan_returnFalse() {
when(mServiceState.getVoiceRegState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
when(mServiceState.getNetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_PS,
AccessNetworkConstants.TRANSPORT_TYPE_WLAN))
.thenReturn(mNetworkRegistrationInfo);
when(mServiceState.getDataRegistrationState()).thenReturn(ServiceState.STATE_IN_SERVICE);
when(mNetworkRegistrationInfo.isInService()).thenReturn(true);
assertThat(Utils.isInService(mServiceState)).isFalse();
}
@Test
public void isInService_voiceOutOfServiceDataNull_returnFalse() {
when(mServiceState.getVoiceRegState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
when(mServiceState.getNetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_PS,
AccessNetworkConstants.TRANSPORT_TYPE_WWAN))
.thenReturn(null);
assertThat(Utils.isInService(mServiceState)).isFalse();
}
@Test
public void isInService_voiceOutOfServiceDataOutOfService_returnFalse() {
when(mServiceState.getVoiceRegState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
when(mServiceState.getNetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_PS,
AccessNetworkConstants.TRANSPORT_TYPE_WWAN))
.thenReturn(mNetworkRegistrationInfo);
when(mNetworkRegistrationInfo.isInService()).thenReturn(false);
assertThat(Utils.isInService(mServiceState)).isFalse();
}
@Test
public void isInService_ServiceStatePowerOff_returnFalse() {
when(mServiceState.getVoiceRegState()).thenReturn(ServiceState.STATE_POWER_OFF);
assertThat(Utils.isInService(mServiceState)).isFalse();
}
@Test
public void getCombinedServiceState_servicestateNull_returnOutOfService() {
assertThat(Utils.getCombinedServiceState(null))
.isEqualTo(ServiceState.STATE_OUT_OF_SERVICE);
}
@Test
public void getCombinedServiceState_ServiceStatePowerOff_returnPowerOff() {
when(mServiceState.getVoiceRegState()).thenReturn(ServiceState.STATE_POWER_OFF);
assertThat(Utils.getCombinedServiceState(mServiceState))
.isEqualTo(ServiceState.STATE_POWER_OFF);
}
@Test
public void getCombinedServiceState_voiceInService_returnInService() {
when(mServiceState.getVoiceRegState()).thenReturn(ServiceState.STATE_IN_SERVICE);
assertThat(Utils.getCombinedServiceState(mServiceState))
.isEqualTo(ServiceState.STATE_IN_SERVICE);
}
@Test
public void getCombinedServiceState_voiceOutOfServiceDataInService_returnInService() {
when(mServiceState.getVoiceRegState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
when(mServiceState.getNetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_PS,
AccessNetworkConstants.TRANSPORT_TYPE_WWAN))
.thenReturn(mNetworkRegistrationInfo);
when(mNetworkRegistrationInfo.isInService()).thenReturn(true);
assertThat(Utils.getCombinedServiceState(mServiceState))
.isEqualTo(ServiceState.STATE_IN_SERVICE);
}
@Test
public void getCombinedServiceState_voiceOutOfServiceDataInServiceOnIwLan_returnOutOfService() {
when(mServiceState.getVoiceRegState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
when(mServiceState.getNetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_PS,
AccessNetworkConstants.TRANSPORT_TYPE_WLAN))
.thenReturn(mNetworkRegistrationInfo);
when(mNetworkRegistrationInfo.isInService()).thenReturn(true);
assertThat(Utils.getCombinedServiceState(mServiceState))
.isEqualTo(ServiceState.STATE_OUT_OF_SERVICE);
}
@Test
public void getCombinedServiceState_voiceOutOfServiceDataOutOfService_returnOutOfService() {
when(mServiceState.getVoiceRegState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
when(mServiceState.getDataRegistrationState())
.thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
assertThat(Utils.getCombinedServiceState(mServiceState))
.isEqualTo(ServiceState.STATE_OUT_OF_SERVICE);
}
@Test
public void getBatteryStatus_statusIsFull_returnFullString() {
final Intent intent =
new Intent()
.putExtra(BatteryManager.EXTRA_LEVEL, 100)
.putExtra(BatteryManager.EXTRA_SCALE, 100);
final Resources resources = mContext.getResources();
assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false))
.isEqualTo(resources.getString(R.string.battery_info_status_full));
}
@Test
public void getBatteryStatus_statusIsFullAndUseCompactStatus_returnFullyChargedString() {
final Intent intent =
new Intent()
.putExtra(BatteryManager.EXTRA_LEVEL, 100)
.putExtra(BatteryManager.EXTRA_SCALE, 100);
final Resources resources = mContext.getResources();
assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ true))
.isEqualTo(resources.getString(R.string.battery_info_status_full_charged));
}
@Test
public void getBatteryStatus_batteryLevelIs100_returnFullString() {
final Intent intent =
new Intent()
.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_FULL);
final Resources resources = mContext.getResources();
assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false))
.isEqualTo(resources.getString(R.string.battery_info_status_full));
}
@Test
public void getBatteryStatus_batteryLevelIs100AndUseCompactStatus_returnFullyString() {
final Intent intent =
new Intent()
.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_FULL);
final Resources resources = mContext.getResources();
assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ true))
.isEqualTo(resources.getString(R.string.battery_info_status_full_charged));
}
@Test
public void getBatteryStatus_batteryLevel99_returnChargingString() {
final Intent intent = new Intent();
intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING);
intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_USB);
final Resources resources = mContext.getResources();
assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false))
.isEqualTo(resources.getString(R.string.battery_info_status_charging));
}
@Test
public void getBatteryStatus_chargingDock_returnDockChargingString() {
final Intent intent = new Intent();
intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING);
intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_DOCK);
final Resources resources = mContext.getResources();
assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false))
.isEqualTo(resources.getString(R.string.battery_info_status_charging_dock));
}
@Test
public void getBatteryStatus_chargingWireless_returnWirelessChargingString() {
final Intent intent = new Intent();
intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING);
intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_WIRELESS);
final Resources resources = mContext.getResources();
assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false))
.isEqualTo(resources.getString(R.string.battery_info_status_charging_wireless));
}
@Test
public void getBatteryStatus_chargingAndUseCompactStatus_returnCompactString() {
final Intent intent = new Intent();
intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING);
intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_USB);
final Resources resources = mContext.getResources();
assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ true))
.isEqualTo(resources.getString(R.string.battery_info_status_charging));
}
@Test
public void getBatteryStatus_chargingWirelessAndUseCompactStatus_returnCompactString() {
final Intent intent = new Intent();
intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING);
intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_WIRELESS);
final Resources resources = mContext.getResources();
assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ true))
.isEqualTo(resources.getString(R.string.battery_info_status_charging));
}
@Test
public void containsIncompatibleChargers_nullPorts_returnFalse() {
when(mUsbManager.getPorts()).thenReturn(null);
assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
}
@Test
public void containsIncompatibleChargers_emptyPorts_returnFalse() {
when(mUsbManager.getPorts()).thenReturn(new ArrayList<>());
assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
}
@Test
public void containsIncompatibleChargers_nullPortStatus_returnFalse() {
final List<UsbPort> usbPorts = new ArrayList<>();
usbPorts.add(mUsbPort);
when(mUsbManager.getPorts()).thenReturn(usbPorts);
when(mUsbPort.getStatus()).thenReturn(null);
assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
}
@Test
public void containsIncompatibleChargers_complianeWarningOther_returnTrue_flagDisabled() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING);
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_INPUT_POWER_LIMITED_WARNING);
setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_OTHER);
assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isTrue();
}
@Test
public void containsIncompatibleChargers_complianeWarningPower_returnFalse_flagDisabled() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING);
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_INPUT_POWER_LIMITED_WARNING);
setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_INPUT_POWER_LIMITED);
assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
}
@Test
public void containsIncompatibleChargers_complianeWarningOther_returnFalse_flagEnabled() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING);
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_INPUT_POWER_LIMITED_WARNING);
setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_OTHER);
assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
}
@Test
public void containsIncompatibleChargers_complianeWarningPower_returnTrue_flagEnabled() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING);
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_INPUT_POWER_LIMITED_WARNING);
setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_INPUT_POWER_LIMITED);
assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isTrue();
}
@Test
public void containsIncompatibleChargers_complianeWarningDebug_returnTrue() {
setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY);
assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isTrue();
}
@Test
public void containsIncompatibleChargers_unexpectedWarningType_returnFalse() {
setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_BC_1_2);
assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
}
@Test
public void containsIncompatibleChargers_emptyComplianceWarnings_returnFalse() {
setupIncompatibleCharging();
when(mUsbPortStatus.getComplianceWarnings()).thenReturn(new int[1]);
assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
}
@Test
public void containsIncompatibleChargers_notSupportComplianceWarnings_returnFalse() {
setupIncompatibleCharging();
when(mUsbPort.supportsComplianceWarnings()).thenReturn(false);
assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
}
@Test
public void containsIncompatibleChargers_usbNotConnected_returnFalse() {
setupIncompatibleCharging();
when(mUsbPortStatus.isConnected()).thenReturn(false);
assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
}
@Test
public void containsIncompatibleChargers_disableWarning_returnFalse() {
setupIncompatibleCharging();
Settings.Secure.putInt(
mContext.getContentResolver(), Utils.INCOMPATIBLE_CHARGER_WARNING_DISABLED, 1);
assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
}
@Test
public void shouldShowWirelessChargingNotification_neverSendNotification_returnTrue() {
updateWirelessChargingNotificationTimestamp(
mContext, WIRELESS_CHARGING_DEFAULT_TIMESTAMP, TAG);
assertThat(Utils.shouldShowWirelessChargingNotification(mContext, TAG)).isTrue();
}
@Test
public void shouldShowNotification_neverSendNotification_updateTimestampAndEnabledState() {
updateWirelessChargingNotificationTimestamp(
mContext, WIRELESS_CHARGING_DEFAULT_TIMESTAMP, TAG);
Utils.shouldShowWirelessChargingNotification(mContext, TAG);
assertThat(getWirelessChargingNotificationTimestamp())
.isNotEqualTo(WIRELESS_CHARGING_DEFAULT_TIMESTAMP);
assertThat(shouldShowWirelessChargingWarningTip(mContext, TAG)).isTrue();
}
@Test
public void shouldShowWirelessChargingNotification_notificationDisabled_returnFalse() {
updateWirelessChargingNotificationTimestamp(mContext, CURRENT_TIMESTAMP, TAG);
assertThat(Utils.shouldShowWirelessChargingNotification(mContext, TAG)).isFalse();
}
@Test
public void shouldShowWirelessChargingNotification_withinTimeThreshold_returnFalse() {
updateWirelessChargingNotificationTimestamp(mContext, CURRENT_TIMESTAMP, TAG);
assertThat(Utils.shouldShowWirelessChargingNotification(mContext, TAG)).isFalse();
}
@Test
public void shouldShowWirelessChargingNotification_exceedTimeThreshold_returnTrue() {
final long monthAgo = Duration.ofDays(31).toMillis();
final long timestamp = CURRENT_TIMESTAMP - monthAgo;
updateWirelessChargingNotificationTimestamp(mContext, timestamp, TAG);
assertThat(Utils.shouldShowWirelessChargingNotification(mContext, TAG)).isTrue();
}
@Test
public void shouldShowNotification_exceedTimeThreshold_updateTimestampAndEnabledState() {
final long monthAgo = Duration.ofDays(31).toMillis();
final long timestamp = CURRENT_TIMESTAMP - monthAgo;
updateWirelessChargingNotificationTimestamp(mContext, timestamp, TAG);
Utils.shouldShowWirelessChargingNotification(mContext, TAG);
assertThat(getWirelessChargingNotificationTimestamp()).isNotEqualTo(timestamp);
assertThat(shouldShowWirelessChargingWarningTip(mContext, TAG)).isTrue();
}
@Test
public void updateWirelessChargingNotificationTimestamp_dismissForever_setMinValue() {
updateWirelessChargingNotificationTimestamp(mContext, Long.MIN_VALUE, TAG);
assertThat(getWirelessChargingNotificationTimestamp()).isEqualTo(Long.MIN_VALUE);
}
@Test
public void updateWirelessChargingNotificationTimestamp_notDismissForever_setTimestamp() {
updateWirelessChargingNotificationTimestamp(mContext, CURRENT_TIMESTAMP, TAG);
assertThat(getWirelessChargingNotificationTimestamp())
.isNotEqualTo(WIRELESS_CHARGING_DEFAULT_TIMESTAMP);
assertThat(getWirelessChargingNotificationTimestamp()).isNotEqualTo(Long.MIN_VALUE);
}
@Test
public void shouldShowWirelessChargingWarningTip_enabled_returnTrue() {
Utils.updateWirelessChargingWarningEnabled(mContext, true, TAG);
assertThat(shouldShowWirelessChargingWarningTip(mContext, TAG)).isTrue();
}
@Test
public void shouldShowWirelessChargingWarningTip_disabled_returnFalse() {
Utils.updateWirelessChargingWarningEnabled(mContext, false, TAG);
assertThat(shouldShowWirelessChargingWarningTip(mContext, TAG)).isFalse();
}
private void setupIncompatibleCharging() {
setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY);
}
private void setupIncompatibleCharging(int complianceWarningType) {
final List<UsbPort> usbPorts = new ArrayList<>();
usbPorts.add(mUsbPort);
when(mUsbManager.getPorts()).thenReturn(usbPorts);
when(mUsbPort.getStatus()).thenReturn(mUsbPortStatus);
when(mUsbPort.supportsComplianceWarnings()).thenReturn(true);
when(mUsbPortStatus.isConnected()).thenReturn(true);
when(mUsbPortStatus.getComplianceWarnings()).thenReturn(new int[] {complianceWarningType});
}
private long getWirelessChargingNotificationTimestamp() {
return Settings.Secure.getLong(
mContext.getContentResolver(),
Utils.WIRELESS_CHARGING_NOTIFICATION_TIMESTAMP,
WIRELESS_CHARGING_DEFAULT_TIMESTAMP);
}
}

View File

@@ -0,0 +1,85 @@
/*
* 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.
*/
package com.android.settingslib.accessibility;
import static com.google.common.truth.Truth.assertThat;
import android.content.ComponentName;
import android.content.Context;
import android.os.UserHandle;
import android.provider.Settings;
import com.android.settingslib.testutils.shadow.ShadowSecure;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowSecure.class})
public class AccessibilityUtilsTest {
private Context mContext;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
}
@Test
public void getEnabledServicesFromSettings_noService_emptyResult() {
assertThat(AccessibilityUtils.getEnabledServicesFromSettings(mContext)).isEmpty();
}
@Test
public void getEnabledServicesFromSettings_badFormat_emptyResult() {
ShadowSecure.putStringForUser(
mContext.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
":",
UserHandle.myUserId());
assertThat(AccessibilityUtils.getEnabledServicesFromSettings(mContext)).isEmpty();
}
@Test
public void getEnabledServicesFromSettings_1Service_1result() {
final ComponentName cn = new ComponentName("pkg", "serv");
ShadowSecure.putStringForUser(
mContext.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
cn.flattenToString() + ":",
UserHandle.myUserId());
assertThat(AccessibilityUtils.getEnabledServicesFromSettings(mContext))
.containsExactly(cn);
}
@Test
public void getEnabledServicesFromSettings_2Services_2results() {
final ComponentName cn1 = new ComponentName("pkg", "serv");
final ComponentName cn2 = new ComponentName("pkg", "serv2");
ShadowSecure.putStringForUser(
mContext.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
cn1.flattenToString() + ":" + cn2.flattenToString(),
UserHandle.myUserId());
assertThat(AccessibilityUtils.getEnabledServicesFromSettings(mContext))
.containsExactly(cn1, cn2);
}
}

View File

@@ -0,0 +1,179 @@
/*
* 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.settingslib.applications;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import android.graphics.drawable.Drawable;
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 org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class AppIconCacheManagerTest {
private static final String APP_PACKAGE_NAME = "com.test.app";
private static final String APP_PACKAGE_NAME1 = "com.test.app1";
private static final String APP_PACKAGE_NAME2 = "com.test.app2";
private static final String APP_PACKAGE_NAME3 = "com.test.app3";
private static final int APP_UID = 9999;
@Mock
private Drawable mIcon;
@Mock
private Drawable mIcon1;
@Mock
private Drawable mIcon2;
@Mock
private Drawable mIcon3;
private AppIconCacheManager mAppIconCacheManager;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mAppIconCacheManager = AppIconCacheManager.getInstance();
doReturn(10).when(mIcon).getIntrinsicHeight();
doReturn(10).when(mIcon).getIntrinsicWidth();
doReturn(mIcon).when(mIcon).mutate();
// Algorithm for trim memory test:
// The real maxsize is defined by AppIconCacheManager.MAX_CACHE_SIZE_IN_KB, and the size
// of each element is calculated as following:
// n * n * 4 / 1024
// In the testcase, we want to mock the maxsize of LruCache is 3, so the formula calculating
// the size of each element will be like:
// n * n * 4 / 1024 = maxsize / 3
// Thus, n = square_root(maxsize / 3 * 1024 / 4), which can be used as an icon size.
final int iconSize =
(int) Math.sqrt(AppIconCacheManager.MAX_CACHE_SIZE_IN_KB / 3f * 1024f / 4f);
doReturn(iconSize).when(mIcon1).getIntrinsicHeight();
doReturn(iconSize).when(mIcon1).getIntrinsicWidth();
doReturn(mIcon1).when(mIcon1).mutate();
doReturn(iconSize).when(mIcon2).getIntrinsicHeight();
doReturn(iconSize).when(mIcon2).getIntrinsicWidth();
doReturn(mIcon2).when(mIcon2).mutate();
doReturn(iconSize).when(mIcon3).getIntrinsicHeight();
doReturn(iconSize).when(mIcon3).getIntrinsicWidth();
doReturn(mIcon3).when(mIcon3).mutate();
}
@After
public void tearDown() {
AppIconCacheManager.release();
}
@Test
public void get_invalidPackageOrUid_shouldReturnNull() {
assertThat(mAppIconCacheManager.get(/* packageName= */ null, /* uid= */ -1)).isNull();
}
@Test
public void put_invalidPackageOrUid_shouldNotCrash() {
mAppIconCacheManager.put(/* packageName= */ null, /* uid= */ 0, mIcon);
// no crash
}
@Test
public void put_invalidIcon_shouldNotCacheIcon() {
mAppIconCacheManager.put(APP_PACKAGE_NAME, APP_UID, /* drawable= */ null);
assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME, APP_UID)).isNull();
}
@Test
public void put_invalidIconSize_shouldNotCacheIcon() {
doReturn(-1).when(mIcon).getIntrinsicHeight();
doReturn(-1).when(mIcon).getIntrinsicWidth();
mAppIconCacheManager.put(APP_PACKAGE_NAME, APP_UID, mIcon);
assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME, APP_UID)).isNull();
}
@Test
public void put_shouldCacheIcon() {
mAppIconCacheManager.put(APP_PACKAGE_NAME, APP_UID, mIcon);
assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME, APP_UID)).isEqualTo(mIcon);
}
@Test
public void release_noInstance_shouldNotCrash() {
mAppIconCacheManager = null;
AppIconCacheManager.release();
// no crash
}
@Test
public void release_existInstance_shouldClearCache() {
mAppIconCacheManager.put(APP_PACKAGE_NAME, APP_UID, mIcon);
AppIconCacheManager.release();
assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME, APP_UID)).isNull();
}
@Test
public void trimMemory_levelSatisfied_shouldNotCacheIcon() {
mAppIconCacheManager.put(APP_PACKAGE_NAME1, APP_UID, mIcon1);
mAppIconCacheManager.put(APP_PACKAGE_NAME2, APP_UID, mIcon2);
mAppIconCacheManager.put(APP_PACKAGE_NAME3, APP_UID, mIcon3);
// Expected to trim size to 0
final int level = android.content.ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
mAppIconCacheManager.trimMemory(level);
// None of the elements should be cached
assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME1, APP_UID)).isNull();
assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME2, APP_UID)).isNull();
assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME3, APP_UID)).isNull();
}
@Test
public void trimMemory_levelSatisfied_shouldCacheAtLeastHalf() {
mAppIconCacheManager.put(APP_PACKAGE_NAME1, APP_UID, mIcon1);
mAppIconCacheManager.put(APP_PACKAGE_NAME2, APP_UID, mIcon2);
mAppIconCacheManager.put(APP_PACKAGE_NAME3, APP_UID, mIcon3);
// Get the last element
mAppIconCacheManager.get(APP_PACKAGE_NAME1, APP_UID);
// Expected to trim size to half of it, which is int( 3 / 2 ) = 1
final int level = android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
mAppIconCacheManager.trimMemory(level);
// There should be only one cached element, which is the last recently used one
assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME1, APP_UID)).isNotNull();
assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME2, APP_UID)).isNull();
assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME3, APP_UID)).isNull();
}
}

View File

@@ -0,0 +1,228 @@
/*
* 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.settingslib.applications;
import static android.content.pm.Flags.FLAG_PROVIDE_INFO_OF_APK_IN_APEX;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.graphics.drawable.Drawable;
import android.os.Environment;
import android.platform.test.flag.junit.SetFlagsRule;
import com.android.settingslib.Utils;
import org.junit.After;
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 org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowPackageManager;
import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
@RunWith(RobolectricTestRunner.class)
public class AppUtilsTest {
private static final String APP_PACKAGE_NAME = "com.test.app";
private static final int APP_UID = 9999;
@Mock
private Drawable mIcon;
private Context mContext;
private AppIconCacheManager mAppIconCacheManager;
private ApplicationInfo mAppInfo;
private ApplicationsState.AppEntry mAppEntry;
private ArrayList<ApplicationsState.AppEntry> mAppEntries;
private ShadowPackageManager mShadowPackageManager;
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mAppIconCacheManager = AppIconCacheManager.getInstance();
mAppInfo = createApplicationInfo(APP_PACKAGE_NAME, APP_UID);
mAppEntry = createAppEntry(mAppInfo, /* id= */ 1);
mAppEntries = new ArrayList<>(Arrays.asList(mAppEntry));
doReturn(mIcon).when(mIcon).mutate();
mShadowPackageManager = Shadow.extract(mContext.getPackageManager());
}
@After
public void tearDown() {
AppIconCacheManager.release();
}
@Test
public void getIcon_nullAppEntry_shouldReturnNull() {
assertThat(AppUtils.getIcon(mContext, /* appEntry= */ null)).isNull();
}
@Test
@Config(shadows = ShadowUtils.class)
public void getIcon_noCachedIcon_shouldNotReturnNull() {
assertThat(AppUtils.getIcon(mContext, mAppEntry)).isNotNull();
}
@Test
public void getIcon_existCachedIcon_shouldReturnCachedIcon() {
mAppIconCacheManager.put(APP_PACKAGE_NAME, APP_UID, mIcon);
assertThat(AppUtils.getIcon(mContext, mAppEntry)).isEqualTo(mIcon);
}
@Test
public void getIconFromCache_nullAppEntry_shouldReturnNull() {
assertThat(AppUtils.getIconFromCache(/* appEntry= */ null)).isNull();
}
@Test
public void getIconFromCache_shouldReturnCachedIcon() {
mAppIconCacheManager.put(APP_PACKAGE_NAME, APP_UID, mIcon);
assertThat(AppUtils.getIconFromCache(mAppEntry)).isEqualTo(mIcon);
}
@Test
public void preloadTopIcons_nullAppEntries_shouldNotCrash() {
AppUtils.preloadTopIcons(mContext, /* appEntries= */ null, /* number= */ 1);
// no crash
}
@Test
public void preloadTopIcons_zeroPreloadIcons_shouldNotCacheIcons() {
AppUtils.preloadTopIcons(mContext, mAppEntries, /* number= */ 0);
assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME, APP_UID)).isNull();
}
@Test
@Config(shadows = ShadowUtils.class)
public void preloadTopIcons_shouldCheckIconFromCache() throws InterruptedException {
AppUtils.preloadTopIcons(mContext, mAppEntries, /* number= */ 1);
TimeUnit.SECONDS.sleep(1);
assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME, APP_UID)).isNotNull();
}
@Test
public void isAppInstalled_noAppEntry_shouldReturnFalse() {
assertThat(AppUtils.isAppInstalled(null)).isFalse();
}
@Test
public void isAppInstalled_hasAppEntryWithInstalledFlag_shouldReturnTrue() {
final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
appEntry.info = new ApplicationInfo();
appEntry.info.flags = ApplicationInfo.FLAG_INSTALLED;
assertThat(AppUtils.isAppInstalled(appEntry)).isTrue();
}
@Test
public void isAppInstalled_hasAppEntryWithoutInstalledFlag_shouldReturnFalse() {
final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
appEntry.info = new ApplicationInfo();
assertThat(AppUtils.isAppInstalled(appEntry)).isFalse();
}
@Test
public void isMainlineModule_hasApexPackageName_shouldCheckByPackageInfo() {
mSetFlagsRule.enableFlags(FLAG_PROVIDE_INFO_OF_APK_IN_APEX);
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = APP_PACKAGE_NAME;
packageInfo.setApexPackageName("com.test.apex.package");
mShadowPackageManager.installPackage(packageInfo);
assertThat(
AppUtils.isMainlineModule(mContext.getPackageManager(), APP_PACKAGE_NAME)).isTrue();
}
@Test
public void isMainlineModule_noApexPackageName_shouldCheckBySourceDirPath() {
mSetFlagsRule.disableFlags(FLAG_PROVIDE_INFO_OF_APK_IN_APEX);
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.sourceDir = Environment.getApexDirectory().getAbsolutePath();
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = APP_PACKAGE_NAME;
packageInfo.applicationInfo = applicationInfo;
mShadowPackageManager.installPackage(packageInfo);
assertThat(
AppUtils.isMainlineModule(mContext.getPackageManager(), APP_PACKAGE_NAME)).isTrue();
}
private ApplicationsState.AppEntry createAppEntry(ApplicationInfo appInfo, int id) {
ApplicationsState.AppEntry appEntry = new ApplicationsState.AppEntry(mContext, appInfo, id);
appEntry.label = "label";
appEntry.mounted = true;
final File apkFile = mock(File.class);
doReturn(true).when(apkFile).exists();
try {
Field field = ApplicationsState.AppEntry.class.getDeclaredField("apkFile");
field.setAccessible(true);
field.set(appEntry, apkFile);
} catch (NoSuchFieldException | IllegalAccessException e) {
fail("Not able to mock apkFile: " + e);
}
return appEntry;
}
private ApplicationInfo createApplicationInfo(String packageName, int uid) {
ApplicationInfo appInfo = new ApplicationInfo();
appInfo.sourceDir = "appPath";
appInfo.packageName = packageName;
appInfo.uid = uid;
return appInfo;
}
@Implements(Utils.class)
private static class ShadowUtils {
@Implementation
public static Drawable getBadgedIcon(Context context, ApplicationInfo appInfo) {
final Drawable icon = mock(Drawable.class);
doReturn(10).when(icon).getIntrinsicHeight();
doReturn(10).when(icon).getIntrinsicWidth();
doReturn(icon).when(icon).mutate();
return icon;
}
}
}

View File

@@ -0,0 +1,863 @@
/*
* 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.
*/
package com.android.settingslib.applications;
import static android.content.pm.Flags.FLAG_PROVIDE_INFO_OF_APK_IN_APEX;
import static android.os.UserHandle.MU_ENABLED;
import static android.os.UserHandle.USER_SYSTEM;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.mock;
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 static org.robolectric.shadow.api.Shadow.extract;
import android.annotation.UserIdInt;
import android.app.Application;
import android.app.ApplicationPackageManager;
import android.app.usage.StorageStats;
import android.app.usage.StorageStatsManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.UserProperties;
import android.content.res.Resources;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.flag.junit.SetFlagsRule;
import android.text.TextUtils;
import android.util.IconDrawableFactory;
import androidx.test.core.app.ApplicationProvider;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.ApplicationsState.Callbacks;
import com.android.settingslib.applications.ApplicationsState.Session;
import com.android.settingslib.testutils.shadow.ShadowUserManager;
import org.junit.After;
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.MockitoAnnotations;
import org.mockito.Spy;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowContextImpl;
import org.robolectric.shadows.ShadowLooper;
import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowUserManager.class,
ApplicationsStateRoboTest.ShadowIconDrawableFactory.class,
ApplicationsStateRoboTest.ShadowPackageManager.class})
public class ApplicationsStateRoboTest {
private final static String HOME_PACKAGE_NAME = "com.android.home";
private final static String LAUNCHABLE_PACKAGE_NAME = "com.android.launchable";
private static final int PROFILE_USERID = 10;
private static final int PROFILE_USERID2 = 11;
private static final String PKG_1 = "PKG1";
private static final int OWNER_UID_1 = 1001;
private static final int PROFILE_UID_1 = UserHandle.getUid(PROFILE_USERID, OWNER_UID_1);
private static final String PKG_2 = "PKG2";
private static final int OWNER_UID_2 = 1002;
private static final int PROFILE_UID_2 = UserHandle.getUid(PROFILE_USERID, OWNER_UID_2);
private static final String PKG_3 = "PKG3";
private static final int OWNER_UID_3 = 1003;
private static final int PROFILE_UID_3 = UserHandle.getUid(PROFILE_USERID2, OWNER_UID_3);
private static final String CLONE_USER = "clone_user";
private static final String RANDOM_USER = "random_user";
/** Class under test */
private ApplicationsState mApplicationsState;
private Session mSession;
private Application mApplication;
@Spy
Context mContext = ApplicationProvider.getApplicationContext();
@Mock
private Callbacks mCallbacks;
@Captor
private ArgumentCaptor<ArrayList<AppEntry>> mAppEntriesCaptor;
@Mock
private StorageStatsManager mStorageStatsManager;
@Mock
private IPackageManager mPackageManagerService;
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Implements(value = IconDrawableFactory.class)
public static class ShadowIconDrawableFactory {
@Implementation
protected Drawable getBadgedIcon(ApplicationInfo appInfo) {
return new ColorDrawable(0);
}
}
@Implements(value = ApplicationPackageManager.class)
public static class ShadowPackageManager extends
org.robolectric.shadows.ShadowApplicationPackageManager {
// test installed modules, 2 regular, 2 hidden
private final String[] mModuleNames = {
"test.module.1", "test.hidden.module.2", "test.hidden.module.3", "test.module.4"};
private final List<ModuleInfo> mInstalledModules = new ArrayList<>();
@Implementation
protected ComponentName getHomeActivities(List<ResolveInfo> outActivities) {
ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.activityInfo = new ActivityInfo();
resolveInfo.activityInfo.packageName = HOME_PACKAGE_NAME;
resolveInfo.activityInfo.enabled = true;
outActivities.add(resolveInfo);
return ComponentName.createRelative(resolveInfo.activityInfo.packageName, "foo");
}
@Implementation
public List<ModuleInfo> getInstalledModules(int flags) {
if (mInstalledModules.isEmpty()) {
for (String moduleName : mModuleNames) {
mInstalledModules.add(
createModuleInfo(moduleName,
TextUtils.concat(moduleName, ".apex").toString()));
}
}
return mInstalledModules;
}
public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent,
@PackageManager.ResolveInfoFlagsBits int flags, @UserIdInt int userId) {
List<ResolveInfo> resolveInfos = new ArrayList<>();
ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.activityInfo = new ActivityInfo();
resolveInfo.activityInfo.packageName = LAUNCHABLE_PACKAGE_NAME;
resolveInfo.activityInfo.enabled = true;
resolveInfo.filter = new IntentFilter();
resolveInfo.filter.addCategory(Intent.CATEGORY_LAUNCHER);
resolveInfos.add(resolveInfo);
return resolveInfos;
}
private ModuleInfo createModuleInfo(String packageName, String apexPackageName) {
final ModuleInfo info = new ModuleInfo();
info.setName(packageName);
info.setPackageName(packageName);
info.setApkInApexPackageNames(Collections.singletonList(apexPackageName));
// will treat any app with package name that contains "hidden" as hidden module
info.setHidden(!TextUtils.isEmpty(packageName) && packageName.contains("hidden"));
return info;
}
}
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
// Robolectric does not know about the StorageStatsManager as a system service.
// Registering a mock of this service as a replacement.
ShadowContextImpl shadowContext = Shadow.extract(
RuntimeEnvironment.application.getBaseContext());
shadowContext.setSystemService(Context.STORAGE_STATS_SERVICE, mStorageStatsManager);
mApplication = spy(RuntimeEnvironment.application);
StorageStats storageStats = new StorageStats();
storageStats.codeBytes = 10;
storageStats.cacheBytes = 30;
// Data bytes are a superset of cache bytes.
storageStats.dataBytes = storageStats.cacheBytes + 20;
when(mStorageStatsManager.queryStatsForPackage(any(UUID.class),
anyString(), any(UserHandle.class))).thenReturn(storageStats);
// Set up 3 installed apps, in which 1 is hidden module
final List<ApplicationInfo> infos = new ArrayList<>();
infos.add(createApplicationInfo("test.package.1"));
infos.add(createApplicationInfo("test.hidden.module.2"));
infos.add(createApplicationInfo("test.package.3"));
when(mPackageManagerService.getInstalledApplications(
anyLong() /* flags */, anyInt() /* userId */)).thenReturn(new ParceledListSlice(infos));
ApplicationsState.sInstance = null;
mApplicationsState = ApplicationsState.getInstance(mApplication, mPackageManagerService);
mApplicationsState.clearEntries();
mSession = mApplicationsState.newSession(mCallbacks);
}
@After
public void tearDown() {
mSession.onDestroy();
}
private ApplicationInfo createApplicationInfo(String packageName) {
return createApplicationInfo(packageName, 0);
}
private ApplicationInfo createApplicationInfo(String packageName, int uid) {
ApplicationInfo appInfo = new ApplicationInfo();
appInfo.sourceDir = "foo";
appInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
appInfo.storageUuid = UUID.randomUUID();
appInfo.packageName = packageName;
appInfo.uid = uid;
return appInfo;
}
private AppEntry createAppEntry(ApplicationInfo appInfo, int id) {
AppEntry appEntry = new AppEntry(RuntimeEnvironment.application, appInfo, id);
appEntry.label = "label";
appEntry.mounted = true;
return appEntry;
}
private void addApp(String packageName, int id) {
addApp(packageName, id, 0);
}
private void addApp(String packageName, int id, int userId) {
ApplicationInfo appInfo = createApplicationInfo(packageName, id);
AppEntry appEntry = createAppEntry(appInfo, id);
mApplicationsState.mAppEntries.add(appEntry);
mApplicationsState.mEntriesMap.get(userId).put(appInfo.packageName, appEntry);
}
private void processAllMessages() {
Handler mainHandler = mApplicationsState.mMainHandler;
Handler bkgHandler = mApplicationsState.mBackgroundHandler;
ShadowLooper shadowBkgLooper = extract(bkgHandler.getLooper());
ShadowLooper shadowMainLooper = extract(mainHandler.getLooper());
shadowBkgLooper.idle();
shadowMainLooper.idle();
}
private AppEntry findAppEntry(List<AppEntry> appEntries, long id) {
for (AppEntry appEntry : appEntries) {
if (appEntry.id == id) {
return appEntry;
}
}
return null;
}
@Test
public void testDefaultSession_isResumed_LoadsAll() {
mSession.onResume();
addApp(HOME_PACKAGE_NAME, 1);
addApp(LAUNCHABLE_PACKAGE_NAME, 2);
mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
processAllMessages();
verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
assertThat(appEntries.size()).isEqualTo(2);
for (AppEntry appEntry : appEntries) {
assertThat(appEntry.size).isGreaterThan(0L);
assertThat(appEntry.icon).isNotNull();
}
AppEntry homeEntry = findAppEntry(appEntries, 1);
assertThat(homeEntry.isHomeApp).isTrue();
assertThat(homeEntry.hasLauncherEntry).isFalse();
AppEntry launchableEntry = findAppEntry(appEntries, 2);
assertThat(launchableEntry.hasLauncherEntry).isTrue();
assertThat(launchableEntry.launcherEntryEnabled).isTrue();
}
@Test
public void testDefaultSession_isPaused_NotLoadsAll() {
mSession.onResume();
addApp(HOME_PACKAGE_NAME, 1);
addApp(LAUNCHABLE_PACKAGE_NAME, 2);
mSession.mResumed = false;
mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
processAllMessages();
verify(mCallbacks, never()).onRebuildComplete(mAppEntriesCaptor.capture());
}
@Test
public void testCustomSessionLoadsIconsOnly() {
mSession.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_ICONS);
mSession.onResume();
addApp(LAUNCHABLE_PACKAGE_NAME, 1);
mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
processAllMessages();
verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
assertThat(appEntries.size()).isEqualTo(1);
AppEntry launchableEntry = findAppEntry(appEntries, 1);
assertThat(launchableEntry.icon).isNotNull();
assertThat(launchableEntry.size).isEqualTo(-1);
assertThat(launchableEntry.hasLauncherEntry).isFalse();
}
@Test
public void testCustomSessionLoadsSizesOnly() {
mSession.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_SIZES);
mSession.onResume();
addApp(LAUNCHABLE_PACKAGE_NAME, 1);
mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
processAllMessages();
verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
assertThat(appEntries.size()).isEqualTo(1);
AppEntry launchableEntry = findAppEntry(appEntries, 1);
assertThat(launchableEntry.hasLauncherEntry).isFalse();
assertThat(launchableEntry.size).isGreaterThan(0L);
}
@Test
public void testCustomSessionLoadsHomeOnly() {
mSession.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_HOME_APP);
mSession.onResume();
addApp(HOME_PACKAGE_NAME, 1);
mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
processAllMessages();
verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
assertThat(appEntries.size()).isEqualTo(1);
AppEntry launchableEntry = findAppEntry(appEntries, 1);
assertThat(launchableEntry.hasLauncherEntry).isFalse();
assertThat(launchableEntry.size).isEqualTo(-1);
assertThat(launchableEntry.isHomeApp).isTrue();
}
@Test
public void testCustomSessionLoadsLeanbackOnly() {
mSession.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER);
mSession.onResume();
addApp(LAUNCHABLE_PACKAGE_NAME, 1);
mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
processAllMessages();
verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
assertThat(appEntries.size()).isEqualTo(1);
AppEntry launchableEntry = findAppEntry(appEntries, 1);
assertThat(launchableEntry.size).isEqualTo(-1);
assertThat(launchableEntry.isHomeApp).isFalse();
assertThat(launchableEntry.hasLauncherEntry).isTrue();
assertThat(launchableEntry.launcherEntryEnabled).isTrue();
}
@Test
public void onResume_shouldNotIncludeSystemHiddenModule() {
mSession.onResume();
final List<ApplicationInfo> mApplications = mApplicationsState.mApplications;
assertThat(mApplications).hasSize(2);
assertThat(mApplications.get(0).packageName).isEqualTo("test.package.1");
assertThat(mApplications.get(1).packageName).isEqualTo("test.package.3");
}
@Test
public void removeAndInstall_noWorkprofile_doResumeIfNeededLocked_shouldClearEntries()
throws RemoteException {
// scenario: only owner user
// (PKG_1, PKG_2) -> (PKG_2, PKG_3)
// PKG_1 is removed and PKG_3 is installed before app is resumed.
ApplicationsState.sInstance = null;
mApplicationsState = spy(
ApplicationsState
.getInstance(RuntimeEnvironment.application, mock(IPackageManager.class)));
// Previous Applications:
ApplicationInfo appInfo;
final ArrayList<ApplicationInfo> prevAppList = new ArrayList<>();
appInfo = createApplicationInfo(PKG_1, OWNER_UID_1);
prevAppList.add(appInfo);
appInfo = createApplicationInfo(PKG_2, OWNER_UID_2);
prevAppList.add(appInfo);
mApplicationsState.mApplications = prevAppList;
// Previous Entries:
// (PKG_1, PKG_2)
addApp(PKG_1, OWNER_UID_1, 0);
addApp(PKG_2, OWNER_UID_2, 0);
// latest Applications:
// (PKG_2, PKG_3)
final ArrayList<ApplicationInfo> appList = new ArrayList<>();
appInfo = createApplicationInfo(PKG_2, OWNER_UID_2);
appList.add(appInfo);
appInfo = createApplicationInfo(PKG_3, OWNER_UID_3);
appList.add(appInfo);
setupDoResumeIfNeededLocked(appList, null);
mApplicationsState.doResumeIfNeededLocked();
verify(mApplicationsState).clearEntries();
}
@Test
public void noAppRemoved_noWorkprofile_doResumeIfNeededLocked_shouldNotClearEntries()
throws RemoteException {
// scenario: only owner user
// (PKG_1, PKG_2)
ApplicationsState.sInstance = null;
mApplicationsState = spy(
ApplicationsState
.getInstance(RuntimeEnvironment.application, mock(IPackageManager.class)));
ApplicationInfo appInfo;
// Previous Applications
final ArrayList<ApplicationInfo> prevAppList = new ArrayList<>();
appInfo = createApplicationInfo(PKG_1, OWNER_UID_1);
prevAppList.add(appInfo);
appInfo = createApplicationInfo(PKG_2, OWNER_UID_2);
prevAppList.add(appInfo);
mApplicationsState.mApplications = prevAppList;
// Previous Entries:
// (pk1, PKG_2)
addApp(PKG_1, OWNER_UID_1, 0);
addApp(PKG_2, OWNER_UID_2, 0);
// latest Applications:
// (PKG_2, PKG_3)
final ArrayList<ApplicationInfo> appList = new ArrayList<>();
appInfo = createApplicationInfo(PKG_1, OWNER_UID_1);
appList.add(appInfo);
appInfo = createApplicationInfo(PKG_2, OWNER_UID_2);
appList.add(appInfo);
setupDoResumeIfNeededLocked(appList, null);
mApplicationsState.doResumeIfNeededLocked();
verify(mApplicationsState, never()).clearEntries();
}
@Test
public void removeProfileApp_workprofileExists_doResumeIfNeededLocked_shouldClearEntries()
throws RemoteException {
if (!MU_ENABLED) {
return;
}
// [Preconditions]
// 2 apps (PKG_1, PKG_2) for owner, PKG_1 is not in installed state
// 2 apps (PKG_1, PKG_2) for non-owner.
//
// [Actions]
// profile user's PKG_2 is removed before resume
//
// Applications:
// owner - (PKG_1 - uninstalled, PKG_2) -> (PKG_1 - uninstalled, PKG_2)
// profile - (PKG_1, PKG_2) -> (PKG_1)
//
// Previous Entries:
// owner - (PKG_2)
// profile - (PKG_1, PKG_2)
ShadowUserManager shadowUserManager = Shadow
.extract(RuntimeEnvironment.application.getSystemService(UserManager.class));
shadowUserManager.addProfile(PROFILE_USERID, "profile");
ApplicationsState.sInstance = null;
mApplicationsState = spy(
ApplicationsState
.getInstance(RuntimeEnvironment.application, mock(IPackageManager.class)));
ApplicationInfo appInfo;
// Previous Applications
// owner - (PKG_1 - uninstalled, PKG_2)
// profile - (PKG_1, PKG_2)
final ArrayList<ApplicationInfo> prevAppList = new ArrayList<>();
appInfo = createApplicationInfo(PKG_1, OWNER_UID_1);
appInfo.flags ^= ApplicationInfo.FLAG_INSTALLED;
prevAppList.add(appInfo);
appInfo = createApplicationInfo(PKG_2, OWNER_UID_2);
prevAppList.add(appInfo);
appInfo = createApplicationInfo(PKG_1, PROFILE_UID_1);
prevAppList.add(appInfo);
appInfo = createApplicationInfo(PKG_2, PROFILE_UID_2);
prevAppList.add(appInfo);
mApplicationsState.mApplications = prevAppList;
// Previous Entries:
// owner (PKG_2), profile (pk1, PKG_2)
// PKG_1 is not installed for owner, hence it's removed from entries
addApp(PKG_2, OWNER_UID_2, 0);
addApp(PKG_1, PROFILE_UID_1, PROFILE_USERID);
addApp(PKG_2, PROFILE_UID_2, PROFILE_USERID);
// latest Applications:
// owner (PKG_1, PKG_2), profile (PKG_1)
// owner's PKG_1 is still listed and is in non-installed state
// profile user's PKG_2 is removed by a user before resume
//owner
final ArrayList<ApplicationInfo> ownerAppList = new ArrayList<>();
appInfo = createApplicationInfo(PKG_1, OWNER_UID_1);
appInfo.flags ^= ApplicationInfo.FLAG_INSTALLED;
ownerAppList.add(appInfo);
appInfo = createApplicationInfo(PKG_2, OWNER_UID_2);
ownerAppList.add(appInfo);
//profile
appInfo = createApplicationInfo(PKG_1, PROFILE_UID_1);
setupDoResumeIfNeededLocked(ownerAppList, new ArrayList<>(Arrays.asList(appInfo)));
mApplicationsState.doResumeIfNeededLocked();
verify(mApplicationsState).clearEntries();
}
@Test
public void removeOwnerApp_workprofileExists_doResumeIfNeededLocked_shouldClearEntries()
throws RemoteException {
if (!MU_ENABLED) {
return;
}
// [Preconditions]
// 2 apps (PKG_1, PKG_2) for owner, PKG_1 is not in installed state
// 2 apps (PKG_1, PKG_2) for non-owner.
//
// [Actions]
// Owner user's PKG_2 is removed before resume
//
// Applications:
// owner - (PKG_1 - uninstalled, PKG_2) -> (PKG_1 - uninstalled, PKG_2 - uninstalled)
// profile - (PKG_1, PKG_2) -> (PKG_1, PKG_2)
//
// Previous Entries:
// owner - (PKG_2)
// profile - (PKG_1, PKG_2)
ShadowUserManager shadowUserManager = Shadow
.extract(RuntimeEnvironment.application.getSystemService(UserManager.class));
shadowUserManager.addProfile(PROFILE_USERID, "profile");
ApplicationsState.sInstance = null;
mApplicationsState = spy(
ApplicationsState
.getInstance(RuntimeEnvironment.application, mock(IPackageManager.class)));
ApplicationInfo appInfo;
// Previous Applications:
// owner - (PKG_1 - uninstalled, PKG_2)
// profile - (PKG_1, PKG_2)
final ArrayList<ApplicationInfo> prevAppList = new ArrayList<>();
appInfo = createApplicationInfo(PKG_1, OWNER_UID_1);
appInfo.flags ^= ApplicationInfo.FLAG_INSTALLED;
prevAppList.add(appInfo);
appInfo = createApplicationInfo(PKG_2, OWNER_UID_2);
prevAppList.add(appInfo);
appInfo = createApplicationInfo(PKG_1, PROFILE_UID_1);
prevAppList.add(appInfo);
appInfo = createApplicationInfo(PKG_2, PROFILE_UID_2);
prevAppList.add(appInfo);
mApplicationsState.mApplications = prevAppList;
// Previous Entries:
// owner (PKG_2), profile (pk1, PKG_2)
// PKG_1 is not installed for owner, hence it's removed from entries
addApp(PKG_2, OWNER_UID_2, 0);
addApp(PKG_1, PROFILE_UID_1, PROFILE_USERID);
addApp(PKG_2, PROFILE_UID_2, PROFILE_USERID);
// latest Applications:
// owner (PKG_1 - uninstalled, PKG_2 - uninstalled), profile (PKG_1, PKG_2)
// owner's PKG_1, PKG_2 is still listed and is in non-installed state
// profile user's PKG_2 is removed before resume
//owner
final ArrayList<ApplicationInfo> ownerAppList = new ArrayList<>();
appInfo = createApplicationInfo(PKG_1, OWNER_UID_1);
appInfo.flags ^= ApplicationInfo.FLAG_INSTALLED;
ownerAppList.add(appInfo);
appInfo = createApplicationInfo(PKG_2, OWNER_UID_2);
appInfo.flags ^= ApplicationInfo.FLAG_INSTALLED;
ownerAppList.add(appInfo);
//profile
final ArrayList<ApplicationInfo> profileAppList = new ArrayList<>();
appInfo = createApplicationInfo(PKG_1, PROFILE_UID_1);
profileAppList.add(appInfo);
appInfo = createApplicationInfo(PKG_2, PROFILE_UID_2);
profileAppList.add(appInfo);
setupDoResumeIfNeededLocked(ownerAppList, profileAppList);
mApplicationsState.doResumeIfNeededLocked();
verify(mApplicationsState).clearEntries();
}
@Test
public void noAppRemoved_workprofileExists_doResumeIfNeededLocked_shouldNotClearEntries()
throws RemoteException {
if (!MU_ENABLED) {
return;
}
// [Preconditions]
// 2 apps (PKG_1, PKG_2) for owner, PKG_1 is not in installed state
// 2 apps (PKG_1, PKG_2) for non-owner.
//
// Applications:
// owner - (PKG_1 - uninstalled, PKG_2)
// profile - (PKG_1, PKG_2)
//
// Previous Entries:
// owner - (PKG_2)
// profile - (PKG_1, PKG_2)
ShadowUserManager shadowUserManager = Shadow
.extract(RuntimeEnvironment.application.getSystemService(UserManager.class));
shadowUserManager.addProfile(PROFILE_USERID, "profile");
ApplicationsState.sInstance = null;
mApplicationsState = spy(
ApplicationsState
.getInstance(RuntimeEnvironment.application, mock(IPackageManager.class)));
ApplicationInfo appInfo;
// Previous Applications:
// owner - (PKG_1 - uninstalled, PKG_2)
// profile - (PKG_1, PKG_2)
final ArrayList<ApplicationInfo> prevAppList = new ArrayList<>();
appInfo = createApplicationInfo(PKG_1, OWNER_UID_1);
appInfo.flags ^= ApplicationInfo.FLAG_INSTALLED;
prevAppList.add(appInfo);
appInfo = createApplicationInfo(PKG_2, OWNER_UID_2);
prevAppList.add(appInfo);
appInfo = createApplicationInfo(PKG_1, PROFILE_UID_1);
prevAppList.add(appInfo);
appInfo = createApplicationInfo(PKG_2, PROFILE_UID_2);
prevAppList.add(appInfo);
mApplicationsState.mApplications = prevAppList;
// Previous Entries:
// owner (PKG_2), profile (pk1, PKG_2)
// PKG_1 is not installed for owner, hence it's removed from entries
addApp(PKG_2, OWNER_UID_2, 0);
addApp(PKG_1, PROFILE_UID_1, PROFILE_USERID);
addApp(PKG_2, PROFILE_UID_2, PROFILE_USERID);
// latest Applications:
// owner (PKG_1 - uninstalled, PKG_2), profile (PKG_1, PKG_2)
// owner's PKG_1 is still listed and is in non-installed state
// owner
final ArrayList<ApplicationInfo> ownerAppList = new ArrayList<>();
appInfo = createApplicationInfo(PKG_1, OWNER_UID_1);
appInfo.flags ^= ApplicationInfo.FLAG_INSTALLED;
ownerAppList.add(appInfo);
appInfo = createApplicationInfo(PKG_2, OWNER_UID_2);
ownerAppList.add(appInfo);
// profile
final ArrayList<ApplicationInfo> profileAppList = new ArrayList<>();
appInfo = createApplicationInfo(PKG_1, PROFILE_UID_1);
profileAppList.add(appInfo);
appInfo = createApplicationInfo(PKG_2, PROFILE_UID_2);
profileAppList.add(appInfo);
setupDoResumeIfNeededLocked(ownerAppList, profileAppList);
mApplicationsState.doResumeIfNeededLocked();
verify(mApplicationsState, never()).clearEntries();
}
@Test
public void testDefaultSession_enabledAppIconCache_shouldSkipPreloadIcon() {
when(mApplication.getPackageName()).thenReturn("com.android.settings");
mSession.onResume();
addApp(HOME_PACKAGE_NAME, 1);
addApp(LAUNCHABLE_PACKAGE_NAME, 2);
mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
processAllMessages();
verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture());
List<AppEntry> appEntries = mAppEntriesCaptor.getValue();
for (AppEntry appEntry : appEntries) {
assertThat(appEntry.icon).isNull();
}
}
private void setupDoResumeIfNeededLocked(ArrayList<ApplicationInfo> ownerApps,
ArrayList<ApplicationInfo> profileApps)
throws RemoteException {
if (ownerApps != null) {
when(mApplicationsState.mIpm.getInstalledApplications(anyLong(), eq(0)))
.thenReturn(new ParceledListSlice<>(ownerApps));
}
if (profileApps != null) {
when(mApplicationsState.mIpm.getInstalledApplications(anyLong(), eq(PROFILE_USERID)))
.thenReturn(new ParceledListSlice<>(profileApps));
}
final InterestingConfigChanges configChanges = mock(InterestingConfigChanges.class);
when(configChanges.applyNewConfig(any(Resources.class))).thenReturn(false);
mApplicationsState.setInterestingConfigChanges(configChanges);
}
@Test
public void shouldShowInPersonalTab_forCurrentUser_returnsTrue() {
UserManager um = RuntimeEnvironment.application.getSystemService(UserManager.class);
ApplicationInfo appInfo = createApplicationInfo(PKG_1);
AppEntry primaryUserApp = createAppEntry(appInfo, 1);
assertThat(primaryUserApp.shouldShowInPersonalTab(um, appInfo.uid)).isTrue();
}
@Test
public void shouldShowInPersonalTab_userProfilePreU_returnsFalse() {
UserManager um = RuntimeEnvironment.application.getSystemService(UserManager.class);
ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT",
Build.VERSION_CODES.TIRAMISU);
// Create an app (and subsequent AppEntry) in a non-primary user profile.
ApplicationInfo appInfo1 = createApplicationInfo(PKG_1, PROFILE_UID_1);
AppEntry nonPrimaryUserApp = createAppEntry(appInfo1, 1);
assertThat(nonPrimaryUserApp.shouldShowInPersonalTab(um, appInfo1.uid)).isFalse();
}
@Test
public void shouldShowInPersonalTab_currentUserIsParent_returnsAsPerUserPropertyOfProfile1() {
// Mark system user as parent for both profile users.
UserManager um = RuntimeEnvironment.application.getSystemService(UserManager.class);
ShadowUserManager shadowUserManager = Shadow.extract(um);
shadowUserManager.addProfile(USER_SYSTEM, PROFILE_USERID,
CLONE_USER, 0);
shadowUserManager.addProfile(USER_SYSTEM, PROFILE_USERID2,
RANDOM_USER, 0);
shadowUserManager.setupUserProperty(PROFILE_USERID,
/*showInSettings*/ UserProperties.SHOW_IN_SETTINGS_WITH_PARENT);
shadowUserManager.setupUserProperty(PROFILE_USERID2,
/*showInSettings*/ UserProperties.SHOW_IN_SETTINGS_NO);
ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT",
Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
// Treat PROFILE_USERID as a clone user profile and create an app PKG_1 in it.
ApplicationInfo appInfo1 = createApplicationInfo(PKG_1, PROFILE_UID_1);
// Treat PROFILE_USERID2 as a random non-primary profile and create an app PKG_3 in it.
ApplicationInfo appInfo2 = createApplicationInfo(PKG_3, PROFILE_UID_3);
AppEntry nonPrimaryUserApp1 = createAppEntry(appInfo1, 1);
AppEntry nonPrimaryUserApp2 = createAppEntry(appInfo2, 2);
assertThat(nonPrimaryUserApp1.shouldShowInPersonalTab(um, appInfo1.uid)).isTrue();
assertThat(nonPrimaryUserApp2.shouldShowInPersonalTab(um, appInfo2.uid)).isFalse();
}
@Test
public void getEntry_hasCache_shouldReturnCacheEntry() {
mApplicationsState.mEntriesMap.put(/* userId= */ 0, new HashMap<>());
addApp(PKG_1, /* id= */ 1);
assertThat(mApplicationsState.getEntry(PKG_1, /* userId= */ 0).info.packageName)
.isEqualTo(PKG_1);
}
@Test
public void getEntry_hasNoCache_shouldReturnEntry() {
mApplicationsState.mEntriesMap.clear();
ApplicationInfo appInfo = createApplicationInfo(PKG_1, /* uid= */ 0);
mApplicationsState.mApplications.add(appInfo);
mApplicationsState.mSystemModules.put(PKG_1, /* value= */ false);
assertThat(mApplicationsState.getEntry(PKG_1, /* userId= */ 0).info.packageName)
.isEqualTo(PKG_1);
}
@Test
public void isHiddenModule_hasApkInApexInfo_shouldSupportHiddenApexPackage() {
mSetFlagsRule.enableFlags(FLAG_PROVIDE_INFO_OF_APK_IN_APEX);
ApplicationsState.sInstance = null;
mApplicationsState = ApplicationsState.getInstance(mApplication, mPackageManagerService);
String normalModulePackage = "test.module.1";
String hiddenModulePackage = "test.hidden.module.2";
String hiddenApexPackage = "test.hidden.module.2.apex";
assertThat(mApplicationsState.isHiddenModule(normalModulePackage)).isFalse();
assertThat(mApplicationsState.isHiddenModule(hiddenModulePackage)).isTrue();
assertThat(mApplicationsState.isHiddenModule(hiddenApexPackage)).isTrue();
}
@Test
public void isHiddenModule_noApkInApexInfo_onlySupportHiddenModule() {
mSetFlagsRule.disableFlags(FLAG_PROVIDE_INFO_OF_APK_IN_APEX);
ApplicationsState.sInstance = null;
mApplicationsState = ApplicationsState.getInstance(mApplication, mPackageManagerService);
String normalModulePackage = "test.module.1";
String hiddenModulePackage = "test.hidden.module.2";
String hiddenApexPackage = "test.hidden.module.2.apex";
assertThat(mApplicationsState.isHiddenModule(normalModulePackage)).isFalse();
assertThat(mApplicationsState.isHiddenModule(hiddenModulePackage)).isTrue();
assertThat(mApplicationsState.isHiddenModule(hiddenApexPackage)).isFalse();
}
}

View File

@@ -0,0 +1,92 @@
/*
* 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.
*/
package com.android.settingslib.applications;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
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.PackageItemInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class DefaultAppInfoTest {
@Mock
private PackageItemInfo mPackageItemInfo;
@Mock
private ComponentName mComponentName;
@Mock
private PackageManager mPackageManager;
@Mock
private ApplicationInfo mApplicationInfo;
@Mock
private Drawable mIcon;
private Context mContext;
private DefaultAppInfo mInfo;
@Before
public void setUp() throws PackageManager.NameNotFoundException {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
doReturn(mPackageManager).when(mContext).getPackageManager();
when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(),
anyInt())).thenReturn(mApplicationInfo);
when(mPackageManager.loadUnbadgedItemIcon(mPackageItemInfo, mApplicationInfo)).thenReturn(
mIcon);
}
@Test
public void initInfoWithActivityInfo_shouldLoadInfo() {
mPackageItemInfo.packageName = "test";
mInfo = new DefaultAppInfo(mContext, mPackageManager, 0 /* uid */, mPackageItemInfo);
mInfo.loadLabel();
Drawable icon = mInfo.loadIcon();
assertThat(mInfo.getKey()).isEqualTo(mPackageItemInfo.packageName);
assertThat(icon).isNotNull();
verify(mPackageItemInfo).loadLabel(mPackageManager);
}
@Test
public void initInfoWithComponent_shouldLoadInfo() {
when(mComponentName.getPackageName()).thenReturn("com.android.settings");
mInfo = new DefaultAppInfo(mContext, mPackageManager, 0 /* uid */, mComponentName);
mInfo.getKey();
verify(mComponentName).flattenToString();
}
}

View File

@@ -0,0 +1,201 @@
/*
* 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.settingslib.applications;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.Manifest;
import android.app.AppOpsManager;
import android.app.AppOpsManager.OpEntry;
import android.app.AppOpsManager.PackageOps;
import android.content.Context;
import android.content.PermissionChecker;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.LongSparseArray;
import com.android.settingslib.testutils.shadow.ShadowPermissionChecker;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.time.Clock;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowPermissionChecker.class})
public class RecentAppOpsAccessesTest {
private static final int TEST_UID = 1234;
private static final long NOW = 1_000_000_000; // Approximately 9/8/2001
private static final long ONE_MIN_AGO = NOW - TimeUnit.MINUTES.toMillis(1);
private static final long TWENTY_THREE_HOURS_AGO = NOW - TimeUnit.HOURS.toMillis(23);
private static final long TWO_DAYS_AGO = NOW - TimeUnit.DAYS.toMillis(2);
private static final String[] TEST_PACKAGE_NAMES =
{"package_1MinAgo", "package_14MinAgo", "package_20MinAgo"};
@Mock
private PackageManager mPackageManager;
@Mock
private AppOpsManager mAppOpsManager;
@Mock
private UserManager mUserManager;
@Mock
private Clock mClock;
private Context mContext;
private int mTestUserId;
private RecentAppOpsAccess mRecentAppOpsAccess;
@Before
public void setUp() throws NameNotFoundException {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
when(mPackageManager.getApplicationLabel(isA(ApplicationInfo.class)))
.thenReturn("testApplicationLabel");
when(mPackageManager.getUserBadgedLabel(isA(CharSequence.class), isA(UserHandle.class)))
.thenReturn("testUserBadgedLabel");
when(mPackageManager.getPermissionFlags(any(), any(), any()))
.thenReturn(PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED
| PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED);
for (String testPackageName : TEST_PACKAGE_NAMES) {
ShadowPermissionChecker.setResult(
testPackageName,
Manifest.permission.ACCESS_COARSE_LOCATION,
PermissionChecker.PERMISSION_GRANTED);
ShadowPermissionChecker.setResult(
testPackageName,
Manifest.permission.ACCESS_FINE_LOCATION,
PermissionChecker.PERMISSION_GRANTED);
}
mTestUserId = UserHandle.getUserId(TEST_UID);
when(mUserManager.getUserProfiles())
.thenReturn(Collections.singletonList(new UserHandle(mTestUserId)));
long[] testRequestTime = {ONE_MIN_AGO, TWENTY_THREE_HOURS_AGO, TWO_DAYS_AGO};
List<PackageOps> appOps = createTestPackageOpsList(TEST_PACKAGE_NAMES, testRequestTime);
when(mAppOpsManager.getPackagesForOps(RecentAppOpsAccess.LOCATION_OPS)).thenReturn(
appOps);
mockTestApplicationInfos(mTestUserId, TEST_PACKAGE_NAMES);
when(mClock.millis()).thenReturn(NOW);
mRecentAppOpsAccess = new RecentAppOpsAccess(mContext, mClock,
RecentAppOpsAccess.LOCATION_OPS);
}
@Test
public void testGetAppList_shouldFilterRecentAccesses() {
List<RecentAppOpsAccess.Access> requests = mRecentAppOpsAccess.getAppList(false);
// Only two of the apps have requested location within 15 min.
assertThat(requests).hasSize(2);
// Make sure apps are ordered by recency
assertThat(requests.get(0).packageName).isEqualTo(TEST_PACKAGE_NAMES[0]);
assertThat(requests.get(0).accessFinishTime).isEqualTo(ONE_MIN_AGO);
assertThat(requests.get(1).packageName).isEqualTo(TEST_PACKAGE_NAMES[1]);
assertThat(requests.get(1).accessFinishTime).isEqualTo(TWENTY_THREE_HOURS_AGO);
}
@Test
public void testGetAppList_shouldNotShowAndroidOS() throws NameNotFoundException {
// Add android OS to the list of apps.
PackageOps androidSystemPackageOps =
createPackageOps(
RecentAppOpsAccess.ANDROID_SYSTEM_PACKAGE_NAME,
Process.SYSTEM_UID,
AppOpsManager.OP_FINE_LOCATION,
ONE_MIN_AGO);
long[] testRequestTime =
{ONE_MIN_AGO, TWENTY_THREE_HOURS_AGO, TWO_DAYS_AGO, ONE_MIN_AGO};
List<PackageOps> appOps = createTestPackageOpsList(TEST_PACKAGE_NAMES, testRequestTime);
appOps.add(androidSystemPackageOps);
when(mAppOpsManager.getPackagesForOps(RecentAppOpsAccess.LOCATION_OPS)).thenReturn(
appOps);
mockTestApplicationInfos(
Process.SYSTEM_UID, RecentAppOpsAccess.ANDROID_SYSTEM_PACKAGE_NAME);
List<RecentAppOpsAccess.Access> requests = mRecentAppOpsAccess.getAppList(true);
// Android OS shouldn't show up in the list of apps.
assertThat(requests).hasSize(2);
// Make sure apps are ordered by recency
assertThat(requests.get(0).packageName).isEqualTo(TEST_PACKAGE_NAMES[0]);
assertThat(requests.get(0).accessFinishTime).isEqualTo(ONE_MIN_AGO);
assertThat(requests.get(1).packageName).isEqualTo(TEST_PACKAGE_NAMES[1]);
assertThat(requests.get(1).accessFinishTime).isEqualTo(TWENTY_THREE_HOURS_AGO);
}
private void mockTestApplicationInfos(int userId, String... packageNameList)
throws NameNotFoundException {
for (String packageName : packageNameList) {
ApplicationInfo appInfo = new ApplicationInfo();
appInfo.packageName = packageName;
when(mPackageManager.getApplicationInfoAsUser(
packageName, PackageManager.GET_META_DATA, userId)).thenReturn(appInfo);
}
}
private List<PackageOps> createTestPackageOpsList(String[] packageNameList, long[] time) {
List<PackageOps> packageOpsList = new ArrayList<>();
for (int i = 0; i < packageNameList.length; i++) {
PackageOps packageOps = createPackageOps(
packageNameList[i],
TEST_UID,
AppOpsManager.OP_FINE_LOCATION,
time[i]);
packageOpsList.add(packageOps);
}
return packageOpsList;
}
private PackageOps createPackageOps(String packageName, int uid, int op, long time) {
return new PackageOps(
packageName,
uid,
Collections.singletonList(createOpEntryWithTime(op, time)));
}
private OpEntry createOpEntryWithTime(int op, long time) {
// Slot for background access timestamp.
final LongSparseArray<AppOpsManager.NoteOpEvent> accessEvents = new LongSparseArray<>();
accessEvents.put(AppOpsManager.makeKey(AppOpsManager.UID_STATE_BACKGROUND,
AppOpsManager.OP_FLAG_SELF), new AppOpsManager.NoteOpEvent(time, -1, null));
return new OpEntry(op, AppOpsManager.MODE_ALLOWED, Collections.singletonMap(null,
new AppOpsManager.AttributedOpEntry(op, false, accessEvents, null)));
}
}

View File

@@ -0,0 +1,196 @@
/*
* 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.
*/
package com.android.settingslib.applications;
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.anyList;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.provider.Settings;
import androidx.test.core.app.ApplicationProvider;
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.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class ServiceListingTest {
private static final String TEST_SETTING = "testSetting";
private static final String TEST_INTENT = "com.example.intent";
private ServiceListing mServiceListing;
private Context mContext;
private PackageManager mPm;
@Before
public void setUp() {
mPm = mock(PackageManager.class);
mContext = spy(ApplicationProvider.getApplicationContext());
when(mContext.getPackageManager()).thenReturn(mPm);
mServiceListing = new ServiceListing.Builder(mContext)
.setTag("testTag")
.setSetting(TEST_SETTING)
.setNoun("testNoun")
.setIntentAction(TEST_INTENT)
.setPermission("testPermission")
.build();
}
@Test
public void testValidator() {
ServiceInfo s1 = new ServiceInfo();
s1.permission = "testPermission";
s1.packageName = "pkg";
ServiceInfo s2 = new ServiceInfo();
s2.permission = "testPermission";
s2.packageName = "pkg2";
ResolveInfo r1 = new ResolveInfo();
r1.serviceInfo = s1;
ResolveInfo r2 = new ResolveInfo();
r2.serviceInfo = s2;
when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(
ImmutableList.of(r1, r2));
mServiceListing = new ServiceListing.Builder(mContext)
.setTag("testTag")
.setSetting(TEST_SETTING)
.setNoun("testNoun")
.setIntentAction(TEST_INTENT)
.setValidator(info -> {
if (info.packageName.equals("pkg")) {
return true;
}
return false;
})
.setPermission("testPermission")
.build();
ServiceListing.Callback callback = mock(ServiceListing.Callback.class);
mServiceListing.addCallback(callback);
mServiceListing.reload();
verify(mPm).queryIntentServicesAsUser(any(), anyInt(), anyInt());
ArgumentCaptor<List<ServiceInfo>> captor = ArgumentCaptor.forClass(List.class);
verify(callback, times(1)).onServicesReloaded(captor.capture());
assertThat(captor.getValue().size()).isEqualTo(1);
assertThat(captor.getValue().get(0)).isEqualTo(s1);
}
@Test
public void testNoValidator() {
ServiceInfo s1 = new ServiceInfo();
s1.permission = "testPermission";
s1.packageName = "pkg";
ServiceInfo s2 = new ServiceInfo();
s2.permission = "testPermission";
s2.packageName = "pkg2";
ResolveInfo r1 = new ResolveInfo();
r1.serviceInfo = s1;
ResolveInfo r2 = new ResolveInfo();
r2.serviceInfo = s2;
when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(
ImmutableList.of(r1, r2));
mServiceListing = new ServiceListing.Builder(mContext)
.setTag("testTag")
.setSetting(TEST_SETTING)
.setNoun("testNoun")
.setIntentAction(TEST_INTENT)
.setPermission("testPermission")
.build();
ServiceListing.Callback callback = mock(ServiceListing.Callback.class);
mServiceListing.addCallback(callback);
mServiceListing.reload();
verify(mPm).queryIntentServicesAsUser(any(), anyInt(), anyInt());
ArgumentCaptor<List<ServiceInfo>> captor = ArgumentCaptor.forClass(List.class);
verify(callback, times(1)).onServicesReloaded(captor.capture());
assertThat(captor.getValue().size()).isEqualTo(2);
}
@Test
public void testCallback() {
ServiceListing.Callback callback = mock(ServiceListing.Callback.class);
mServiceListing.addCallback(callback);
mServiceListing.reload();
verify(callback, times(1)).onServicesReloaded(anyList());
mServiceListing.removeCallback(callback);
mServiceListing.reload();
verify(callback, times(1)).onServicesReloaded(anyList());
}
@Test
public void testSaveLoad() {
ComponentName testComponent1 = new ComponentName("testPackage1", "testClass1");
ComponentName testComponent2 = new ComponentName("testPackage2", "testClass2");
Settings.Secure.putString(RuntimeEnvironment.application.getContentResolver(),
TEST_SETTING,
testComponent1.flattenToString() + ":" + testComponent2.flattenToString());
mServiceListing.reload();
assertThat(mServiceListing.isEnabled(testComponent1)).isTrue();
assertThat(mServiceListing.isEnabled(testComponent2)).isTrue();
assertThat(Settings.Secure.getString(RuntimeEnvironment.application.getContentResolver(),
TEST_SETTING)).contains(testComponent1.flattenToString());
assertThat(Settings.Secure.getString(RuntimeEnvironment.application.getContentResolver(),
TEST_SETTING)).contains(testComponent2.flattenToString());
mServiceListing.setEnabled(testComponent1, false);
assertThat(mServiceListing.isEnabled(testComponent1)).isFalse();
assertThat(mServiceListing.isEnabled(testComponent2)).isTrue();
assertThat(Settings.Secure.getString(RuntimeEnvironment.application.getContentResolver(),
TEST_SETTING)).doesNotContain(testComponent1.flattenToString());
assertThat(Settings.Secure.getString(RuntimeEnvironment.application.getContentResolver(),
TEST_SETTING)).contains(testComponent2.flattenToString());
mServiceListing.setEnabled(testComponent1, true);
assertThat(mServiceListing.isEnabled(testComponent1)).isTrue();
assertThat(mServiceListing.isEnabled(testComponent2)).isTrue();
assertThat(Settings.Secure.getString(RuntimeEnvironment.application.getContentResolver(),
TEST_SETTING)).contains(testComponent1.flattenToString());
assertThat(Settings.Secure.getString(RuntimeEnvironment.application.getContentResolver(),
TEST_SETTING)).contains(testComponent2.flattenToString());
}
}

View File

@@ -0,0 +1,234 @@
/*
* 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.
*/
package com.android.settingslib.bluetooth;
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.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothCodecConfig;
import android.bluetooth.BluetoothCodecStatus;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.res.Resources;
import com.android.settingslib.R;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import java.util.Arrays;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class A2dpProfileTest {
@Mock
private Context mContext;
@Mock
private CachedBluetoothDeviceManager mDeviceManager;
@Mock
private LocalBluetoothProfileManager mProfileManager;
@Mock
private BluetoothDevice mDevice;
@Mock
private BluetoothA2dp mBluetoothA2dp;
@Mock
private BluetoothAdapter mBluetoothAdapter;
private BluetoothProfile.ServiceListener mServiceListener;
private A2dpProfile mProfile;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mProfile = new A2dpProfile(mContext, mDeviceManager, mProfileManager);
mServiceListener = mShadowBluetoothAdapter.getServiceListener();
mServiceListener.onServiceConnected(BluetoothProfile.A2DP, mBluetoothA2dp);
when(mBluetoothAdapter.getActiveDevices(eq(BluetoothProfile.A2DP)))
.thenReturn(Arrays.asList(mDevice));
}
@Test
public void onServiceConnected_isProfileReady() {
assertThat(mProfile.isProfileReady()).isTrue();
verify(mProfileManager).callServiceConnectedListeners();
}
@Test
public void onServiceDisconnected_profileNotReady() {
mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP);
assertThat(mProfile.isProfileReady()).isFalse();
verify(mProfileManager).callServiceDisconnectedListeners();
}
@Test
public void supportsHighQualityAudio() {
when(mBluetoothA2dp.isOptionalCodecsSupported(mDevice)).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED);
assertThat(mProfile.supportsHighQualityAudio(mDevice)).isTrue();
when(mBluetoothA2dp.isOptionalCodecsSupported(mDevice)).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED);
assertThat(mProfile.supportsHighQualityAudio(mDevice)).isFalse();
when(mBluetoothA2dp.isOptionalCodecsSupported(mDevice)).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN);
assertThat(mProfile.supportsHighQualityAudio(mDevice)).isFalse();
}
@Test
public void isHighQualityAudioEnabled() {
when(mBluetoothA2dp.isOptionalCodecsEnabled(mDevice)).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED);
assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isTrue();
when(mBluetoothA2dp.isOptionalCodecsEnabled(mDevice)).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED);
assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isFalse();
// If we don't have a stored pref for whether optional codecs should be enabled or not,
// then isHighQualityAudioEnabled() should return true or false based on whether optional
// codecs are supported. If the device is connected then we should ask it directly, but if
// the device isn't connected then rely on the stored pref about such support.
when(mBluetoothA2dp.isOptionalCodecsEnabled(mDevice)).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN);
when(mBluetoothA2dp.getConnectionState(any())).thenReturn(
BluetoothProfile.STATE_DISCONNECTED);
when(mBluetoothA2dp.isOptionalCodecsSupported(mDevice)).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED);
assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isFalse();
when(mBluetoothA2dp.isOptionalCodecsSupported(mDevice)).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED);
assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isTrue();
when(mBluetoothA2dp.getConnectionState(any())).thenReturn(
BluetoothProfile.STATE_CONNECTED);
BluetoothCodecStatus status = mock(BluetoothCodecStatus.class);
when(mBluetoothA2dp.getCodecStatus(mDevice)).thenReturn(status);
BluetoothCodecConfig config = mock(BluetoothCodecConfig.class);
when(status.getCodecConfig()).thenReturn(config);
when(config.isMandatoryCodec()).thenReturn(false);
assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isTrue();
when(config.isMandatoryCodec()).thenReturn(true);
assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isFalse();
}
// Strings to use in fake resource lookups.
private static String KNOWN_CODEC_LABEL = "Use high quality audio: %1$s";
private static String UNKNOWN_CODEC_LABEL = "Use high quality audio";
private static String[] CODEC_NAMES =
new String[]{"Default", "SBC", "AAC", "aptX", "aptX HD", "LDAC"};
/**
* Helper for setting up several tests of getHighQualityAudioOptionLabel
*/
private void setupLabelTest() {
// SettingsLib doesn't have string resource lookup working for robotests, so fake our own
// string loading.
when(mContext.getString(eq(R.string.bluetooth_profile_a2dp_high_quality),
any(String.class))).thenAnswer((invocation) -> {
return String.format(KNOWN_CODEC_LABEL, invocation.getArguments()[1]);
});
when(mContext.getString(eq(R.string.bluetooth_profile_a2dp_high_quality_unknown_codec)))
.thenReturn(UNKNOWN_CODEC_LABEL);
final Resources res = mock(Resources.class);
when(mContext.getResources()).thenReturn(res);
when(res.getStringArray(eq(R.array.bluetooth_a2dp_codec_titles)))
.thenReturn(CODEC_NAMES);
// Most tests want to simulate optional codecs being supported by the device, so do that
// by default here.
when(mBluetoothA2dp.isOptionalCodecsSupported(any())).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED);
}
@Test
public void getLableCodecsNotSupported() {
setupLabelTest();
when(mBluetoothA2dp.isOptionalCodecsSupported(any())).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED);
assertThat(mProfile.getHighQualityAudioOptionLabel(mDevice)).isEqualTo(UNKNOWN_CODEC_LABEL);
}
@Test
public void getLabelDeviceDisconnected() {
setupLabelTest();
when(mBluetoothA2dp.getConnectionState(any())).thenReturn(
BluetoothProfile.STATE_DISCONNECTED);
assertThat(mProfile.getHighQualityAudioOptionLabel(mDevice)).isEqualTo(UNKNOWN_CODEC_LABEL);
}
@Test
public void getLabelDeviceConnectedButNotHighQualityCodec() {
setupLabelTest();
when(mBluetoothA2dp.getConnectionState(any())).thenReturn(
BluetoothProfile.STATE_CONNECTED);
BluetoothCodecStatus status = mock(BluetoothCodecStatus.class);
BluetoothCodecConfig config = mock(BluetoothCodecConfig.class);
List<BluetoothCodecConfig> configs = Arrays.asList(config);
when(mBluetoothA2dp.getCodecStatus(mDevice)).thenReturn(status);
when(status.getCodecsSelectableCapabilities()).thenReturn(configs);
when(config.isMandatoryCodec()).thenReturn(true);
assertThat(mProfile.getHighQualityAudioOptionLabel(mDevice)).isEqualTo(UNKNOWN_CODEC_LABEL);
}
@Test
public void getLabelDeviceConnectedWithHighQualityCodec() {
setupLabelTest();
when(mBluetoothA2dp.getConnectionState(any())).thenReturn(
BluetoothProfile.STATE_CONNECTED);
BluetoothCodecStatus status = mock(BluetoothCodecStatus.class);
BluetoothCodecConfig config = mock(BluetoothCodecConfig.class);
List<BluetoothCodecConfig> configs = Arrays.asList(config);
when(mBluetoothA2dp.getCodecStatus(mDevice)).thenReturn(status);
when(status.getCodecsSelectableCapabilities()).thenReturn(configs);
when(config.isMandatoryCodec()).thenReturn(false);
when(config.getCodecType()).thenReturn(4);
assertThat(mProfile.getHighQualityAudioOptionLabel(mDevice)).isEqualTo(
String.format(KNOWN_CODEC_LABEL, "LDAC"));
}
@Test
public void setActiveDevice_returnTrue() {
assertThat(mProfile.setActiveDevice(null)).isTrue();
assertThat(mProfile.setActiveDevice(mDevice)).isTrue();
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.
*/
package com.android.settingslib.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothA2dpSink;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class A2dpSinkProfileTest {
@Mock
private CachedBluetoothDeviceManager mDeviceManager;
@Mock
private LocalBluetoothProfileManager mProfileManager;
@Mock
private BluetoothA2dpSink mService;
@Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private A2dpSinkProfile mProfile;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mProfile = new A2dpSinkProfile(RuntimeEnvironment.application,
mDeviceManager, mProfileManager);
mServiceListener = mShadowBluetoothAdapter.getServiceListener();
mServiceListener.onServiceConnected(BluetoothProfile.A2DP_SINK, mService);
}
@Test
public void getConnectionStatus_shouldReturnConnectionState() {
when(mService.getConnectionState(mBluetoothDevice)).
thenReturn(BluetoothProfile.STATE_CONNECTED);
assertThat(mProfile.getConnectionStatus(mBluetoothDevice)).
isEqualTo(BluetoothProfile.STATE_CONNECTED);
}
}

View File

@@ -0,0 +1,492 @@
/*
* Copyright 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.
*/
package com.android.settingslib.bluetooth;
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.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.UserHandle;
import android.telephony.TelephonyManager;
import com.android.settingslib.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class BluetoothEventManagerTest {
private static final String DEVICE_NAME = "test_device_name";
@Mock
private LocalBluetoothAdapter mLocalAdapter;
@Mock
private CachedBluetoothDeviceManager mCachedDeviceManager;
@Mock
private BluetoothCallback mBluetoothCallback;
@Mock
private CachedBluetoothDevice mCachedBluetoothDevice;
@Mock
private BluetoothDevice mBluetoothDevice;
@Mock
private HeadsetProfile mHfpProfile;
@Mock
private A2dpProfile mA2dpProfile;
@Mock
private HearingAidProfile mHearingAidProfile;
@Mock
private LeAudioProfile mLeAudioProfile;
@Mock
private BluetoothDevice mDevice1;
@Mock
private BluetoothDevice mDevice2;
@Mock
private BluetoothDevice mDevice3;
@Mock
private LocalBluetoothProfileManager mLocalProfileManager;
@Mock
private BluetoothUtils.ErrorListener mErrorListener;
private Context mContext;
private Intent mIntent;
private BluetoothEventManager mBluetoothEventManager;
private CachedBluetoothDevice mCachedDevice1;
private CachedBluetoothDevice mCachedDevice2;
private CachedBluetoothDevice mCachedDevice3;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mBluetoothEventManager = new BluetoothEventManager(mLocalAdapter,
mCachedDeviceManager, mContext, /* handler= */ null, /* userHandle= */ null);
when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice);
when(mHfpProfile.isProfileReady()).thenReturn(true);
when(mA2dpProfile.isProfileReady()).thenReturn(true);
when(mHearingAidProfile.isProfileReady()).thenReturn(true);
when(mLeAudioProfile.isProfileReady()).thenReturn(true);
mCachedDevice1 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1);
mCachedDevice2 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2);
mCachedDevice3 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice3);
BluetoothUtils.setErrorListener(mErrorListener);
}
@Test
public void ifUserHandleIsNull_registerReceiverIsCalled() {
Context mockContext = mock(Context.class);
BluetoothEventManager eventManager =
new BluetoothEventManager(mLocalAdapter, mCachedDeviceManager, mockContext,
/* handler= */ null, /* userHandle= */ null);
verify(mockContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class),
eq(null), eq(null), eq(Context.RECEIVER_EXPORTED));
}
@Test
public void ifUserHandleSpecified_registerReceiverAsUserIsCalled() {
Context mockContext = mock(Context.class);
BluetoothEventManager eventManager =
new BluetoothEventManager(mLocalAdapter, mCachedDeviceManager, mockContext,
/* handler= */ null, UserHandle.ALL);
verify(mockContext).registerReceiverAsUser(any(BroadcastReceiver.class), eq(UserHandle.ALL),
any(IntentFilter.class), eq(null), eq(null), eq(Context.RECEIVER_EXPORTED));
}
/**
* Intent ACTION_AUDIO_STATE_CHANGED should dispatch to callback.
*/
@Test
public void intentWithExtraState_audioStateChangedShouldDispatchToRegisterCallback() {
mBluetoothEventManager.registerCallback(mBluetoothCallback);
mIntent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
mContext.sendBroadcast(mIntent);
verify(mBluetoothCallback).onAudioModeChanged();
}
/**
* Intent ACTION_PHONE_STATE_CHANGED should dispatch to callback.
*/
@Test
public void intentWithExtraState_phoneStateChangedShouldDispatchToRegisterCallback() {
mBluetoothEventManager.registerCallback(mBluetoothCallback);
mIntent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
mContext.sendBroadcast(mIntent);
verify(mBluetoothCallback).onAudioModeChanged();
}
/**
* dispatchProfileConnectionStateChanged should dispatch to onProfileConnectionStateChanged
* callback.
*/
@Test
public void dispatchProfileConnectionStateChanged_registerCallback_shouldDispatchCallback() {
mBluetoothEventManager.registerCallback(mBluetoothCallback);
mBluetoothEventManager.dispatchProfileConnectionStateChanged(mCachedBluetoothDevice,
BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
verify(mBluetoothCallback).onProfileConnectionStateChanged(mCachedBluetoothDevice,
BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
}
@Test
public void dispatchAclConnectionStateChanged_aclDisconnected_shouldDispatchCallback() {
mBluetoothEventManager.registerCallback(mBluetoothCallback);
mIntent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
mContext.sendBroadcast(mIntent);
verify(mBluetoothCallback).onAclConnectionStateChanged(mCachedBluetoothDevice,
BluetoothAdapter.STATE_DISCONNECTED);
}
@Test
public void dispatchAclConnectionStateChanged_aclConnected_shouldDispatchCallback() {
mBluetoothEventManager.registerCallback(mBluetoothCallback);
mIntent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
mContext.sendBroadcast(mIntent);
verify(mBluetoothCallback).onAclConnectionStateChanged(mCachedBluetoothDevice,
BluetoothAdapter.STATE_CONNECTED);
}
@Test
public void dispatchAclConnectionStateChanged_aclDisconnected_shouldNotCallbackSubDevice() {
when(mCachedDeviceManager.isSubDevice(mBluetoothDevice)).thenReturn(true);
mBluetoothEventManager.registerCallback(mBluetoothCallback);
mIntent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
mContext.sendBroadcast(mIntent);
verify(mBluetoothCallback, never()).onAclConnectionStateChanged(mCachedBluetoothDevice,
BluetoothAdapter.STATE_DISCONNECTED);
}
@Test
public void dispatchAclConnectionStateChanged_aclConnected_shouldNotCallbackSubDevice() {
when(mCachedDeviceManager.isSubDevice(mBluetoothDevice)).thenReturn(true);
mBluetoothEventManager.registerCallback(mBluetoothCallback);
mIntent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
mContext.sendBroadcast(mIntent);
verify(mBluetoothCallback, never()).onAclConnectionStateChanged(mCachedBluetoothDevice,
BluetoothAdapter.STATE_CONNECTED);
}
@Test
public void dispatchAclConnectionStateChanged_findDeviceReturnNull_shouldNotDispatchCallback() {
when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(null);
mBluetoothEventManager.registerCallback(mBluetoothCallback);
mIntent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
mContext.sendBroadcast(mIntent);
verify(mBluetoothCallback, never()).onAclConnectionStateChanged(mCachedBluetoothDevice,
BluetoothAdapter.STATE_CONNECTED);
}
/**
* Test to verify onActiveDeviceChanged().
*/
@Test
public void dispatchActiveDeviceChanged_connectedDevices_activeDeviceChanged() {
final List<CachedBluetoothDevice> cachedDevices = new ArrayList<>();
cachedDevices.add(mCachedDevice1);
cachedDevices.add(mCachedDevice2);
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices);
// Connect both devices for A2DP and HFP
mCachedDevice1.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice2.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice1.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice2.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
// Verify that both devices are connected and none is Active
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
// The first device is active for A2DP, the second device is active for HFP
mBluetoothEventManager.dispatchActiveDeviceChanged(mCachedDevice1, BluetoothProfile.A2DP);
mBluetoothEventManager
.dispatchActiveDeviceChanged(mCachedDevice2, BluetoothProfile.HEADSET);
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
// The first device is active for A2DP and HFP
mBluetoothEventManager
.dispatchActiveDeviceChanged(mCachedDevice1, BluetoothProfile.HEADSET);
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
// The second device is active for A2DP and HFP
mBluetoothEventManager.dispatchActiveDeviceChanged(mCachedDevice2, BluetoothProfile.A2DP);
mBluetoothEventManager
.dispatchActiveDeviceChanged(mCachedDevice2, BluetoothProfile.HEADSET);
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
// No active device for A2DP
mBluetoothEventManager.dispatchActiveDeviceChanged(null, BluetoothProfile.A2DP);
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
// No active device for HFP
mBluetoothEventManager.dispatchActiveDeviceChanged(null, BluetoothProfile.HEADSET);
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
}
@Test
public void dispatchActiveDeviceChanged_connectedMemberDevices_activeDeviceChanged() {
final List<CachedBluetoothDevice> cachedDevices = new ArrayList<>();
cachedDevices.add(mCachedDevice1);
cachedDevices.add(mCachedDevice2);
int group1 = 1;
when(mDevice3.getAddress()).thenReturn("testAddress3");
mCachedDevice1.setGroupId(group1);
mCachedDevice3.setGroupId(group1);
mCachedDevice1.addMemberDevice(mCachedDevice3);
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mDevice3.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices);
// Connect device1 and device3 for LE and device2 for A2DP and HFP
mCachedDevice1.onProfileStateChanged(mLeAudioProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice3.onProfileStateChanged(mLeAudioProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice2.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice2.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
// Verify that both devices are connected and none is Active
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.LE_AUDIO)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(mCachedDevice3.isActiveDevice(BluetoothProfile.LE_AUDIO)).isFalse();
// The member device is active.
mBluetoothEventManager.dispatchActiveDeviceChanged(mCachedDevice3,
BluetoothProfile.LE_AUDIO);
// The main device is active since the member is active.
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.LE_AUDIO)).isTrue();
}
/**
* Test to verify onActiveDeviceChanged() with A2DP and Hearing Aid.
*/
@Test
public void dispatchActiveDeviceChanged_withA2dpAndHearingAid() {
final List<CachedBluetoothDevice> cachedDevices = new ArrayList<>();
cachedDevices.add(mCachedDevice1);
cachedDevices.add(mCachedDevice2);
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices);
// Connect device1 for A2DP and HFP and device2 for Hearing Aid
mCachedDevice1.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice1.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice2.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
// Verify that both devices are connected and none is Active
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
// The first device is active for A2DP and HFP
mBluetoothEventManager.dispatchActiveDeviceChanged(mCachedDevice1, BluetoothProfile.A2DP);
mBluetoothEventManager
.dispatchActiveDeviceChanged(mCachedDevice1, BluetoothProfile.HEADSET);
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
// The second device is active for Hearing Aid and the first device is not active
mBluetoothEventManager.dispatchActiveDeviceChanged(null, BluetoothProfile.A2DP);
mBluetoothEventManager.dispatchActiveDeviceChanged(null, BluetoothProfile.HEADSET);
mBluetoothEventManager
.dispatchActiveDeviceChanged(mCachedDevice2, BluetoothProfile.HEARING_AID);
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isTrue();
// No active device for Hearing Aid
mBluetoothEventManager.dispatchActiveDeviceChanged(null, BluetoothProfile.HEARING_AID);
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
}
@Test
public void dispatchActiveDeviceChanged_callExpectedOnActiveDeviceChanged() {
mBluetoothEventManager.registerCallback(mBluetoothCallback);
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
Collections.singletonList(mCachedDevice1));
mBluetoothEventManager.dispatchActiveDeviceChanged(mCachedDevice1,
BluetoothProfile.HEARING_AID);
verify(mCachedDeviceManager).onActiveDeviceChanged(mCachedDevice1);
verify(mBluetoothCallback).onActiveDeviceChanged(mCachedDevice1,
BluetoothProfile.HEARING_AID);
}
@Test
public void dispatchActiveDeviceChanged_activeFromSubDevice_mainCachedDeviceActive() {
CachedBluetoothDevice subDevice = new CachedBluetoothDevice(mContext, mLocalProfileManager,
mDevice3);
mCachedDevice1.setSubDevice(subDevice);
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
Collections.singletonList(mCachedDevice1));
mCachedDevice1.onProfileStateChanged(mHearingAidProfile,
BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
mBluetoothEventManager.dispatchActiveDeviceChanged(subDevice, BluetoothProfile.HEARING_AID);
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isTrue();
}
@Test
public void showUnbondMessage_reasonAuthTimeout_showCorrectedErrorCode() {
mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
mIntent.putExtra(BluetoothDevice.EXTRA_UNBOND_REASON,
BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT);
when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1);
when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME);
mContext.sendBroadcast(mIntent);
verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME),
eq(R.string.bluetooth_pairing_error_message));
}
@Test
public void showUnbondMessage_reasonRemoteDeviceDown_showCorrectedErrorCode() {
mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
mIntent.putExtra(BluetoothDevice.EXTRA_UNBOND_REASON,
BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN);
when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1);
when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME);
mContext.sendBroadcast(mIntent);
verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME),
eq(R.string.bluetooth_pairing_device_down_error_message));
}
@Test
public void showUnbondMessage_reasonAuthRejected_showCorrectedErrorCode() {
mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
mIntent.putExtra(BluetoothDevice.EXTRA_UNBOND_REASON,
BluetoothDevice.UNBOND_REASON_AUTH_REJECTED);
when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1);
when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME);
mContext.sendBroadcast(mIntent);
verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME),
eq(R.string.bluetooth_pairing_rejected_error_message));
}
@Test
public void showUnbondMessage_reasonAuthFailed_showCorrectedErrorCode() {
mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
mIntent.putExtra(BluetoothDevice.EXTRA_UNBOND_REASON,
BluetoothDevice.UNBOND_REASON_AUTH_FAILED);
when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1);
when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME);
mContext.sendBroadcast(mIntent);
verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME),
eq(R.string.bluetooth_pairing_pin_error_message));
}
}

View File

@@ -0,0 +1,433 @@
/*
* 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.
*/
package com.android.settingslib.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.net.Uri;
import android.util.Pair;
import com.android.settingslib.widget.AdaptiveIcon;
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;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class BluetoothUtilsTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private CachedBluetoothDevice mCachedBluetoothDevice;
@Mock
private BluetoothDevice mBluetoothDevice;
@Mock
private AudioManager mAudioManager;
@Mock
private PackageManager mPackageManager;
private Context mContext;
private static final String STRING_METADATA = "string_metadata";
private static final String BOOL_METADATA = "true";
private static final String INT_METADATA = "25";
private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25;
private static final String KEY_HEARABLE_CONTROL_SLICE = "HEARABLE_CONTROL_SLICE_WITH_WIDTH";
private static final String CONTROL_METADATA =
"<HEARABLE_CONTROL_SLICE_WITH_WIDTH>" + STRING_METADATA
+ "</HEARABLE_CONTROL_SLICE_WITH_WIDTH>";
private static final String FAKE_EXCLUSIVE_MANAGER_NAME = "com.fake.name";
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
}
@Test
public void getBtClassDrawableWithDescription_typePhone_returnPhoneDrawable() {
when(mCachedBluetoothDevice.getBtClass().getMajorDeviceClass()).thenReturn(
BluetoothClass.Device.Major.PHONE);
final Pair<Drawable, String> pair = BluetoothUtils.getBtClassDrawableWithDescription(
mContext, mCachedBluetoothDevice);
verify(mContext).getDrawable(com.android.internal.R.drawable.ic_phone);
}
@Test
public void getBtClassDrawableWithDescription_typeComputer_returnComputerDrawable() {
when(mCachedBluetoothDevice.getBtClass().getMajorDeviceClass()).thenReturn(
BluetoothClass.Device.Major.COMPUTER);
final Pair<Drawable, String> pair = BluetoothUtils.getBtClassDrawableWithDescription(
mContext, mCachedBluetoothDevice);
verify(mContext).getDrawable(com.android.internal.R.drawable.ic_bt_laptop);
}
@Test
public void getBtClassDrawableWithDescription_typeHearingAid_returnHearingAidDrawable() {
when(mCachedBluetoothDevice.isHearingAidDevice()).thenReturn(true);
BluetoothUtils.getBtClassDrawableWithDescription(mContext, mCachedBluetoothDevice);
verify(mContext).getDrawable(com.android.internal.R.drawable.ic_bt_hearing_aid);
}
@Test
public void getBtRainbowDrawableWithDescription_normalHeadset_returnAdaptiveIcon() {
when(mBluetoothDevice.getMetadata(
BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn("false".getBytes());
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
when(mCachedBluetoothDevice.getAddress()).thenReturn("1f:aa:bb");
assertThat(BluetoothUtils.getBtRainbowDrawableWithDescription(
RuntimeEnvironment.application,
mCachedBluetoothDevice).first).isInstanceOf(AdaptiveIcon.class);
}
@Test
public void getStringMetaData_hasMetaData_getCorrectMetaData() {
when(mBluetoothDevice.getMetadata(
BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON)).thenReturn(
STRING_METADATA.getBytes());
assertThat(BluetoothUtils.getStringMetaData(mBluetoothDevice,
BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON)).isEqualTo(STRING_METADATA);
}
@Test
public void getIntMetaData_hasMetaData_getCorrectMetaData() {
when(mBluetoothDevice.getMetadata(
BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn(
INT_METADATA.getBytes());
assertThat(BluetoothUtils.getIntMetaData(mBluetoothDevice,
BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY))
.isEqualTo(Integer.parseInt(INT_METADATA));
}
@Test
public void getIntMetaData_invalidMetaData_getErrorCode() {
when(mBluetoothDevice.getMetadata(
BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn(null);
assertThat(BluetoothUtils.getIntMetaData(mBluetoothDevice,
BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON))
.isEqualTo(BluetoothUtils.META_INT_ERROR);
}
@Test
public void getBooleanMetaData_hasMetaData_getCorrectMetaData() {
when(mBluetoothDevice.getMetadata(
BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
BOOL_METADATA.getBytes());
assertThat(BluetoothUtils.getBooleanMetaData(mBluetoothDevice,
BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).isEqualTo(true);
}
@Test
public void getUriMetaData_hasMetaData_getCorrectMetaData() {
when(mBluetoothDevice.getMetadata(
BluetoothDevice.METADATA_MAIN_ICON)).thenReturn(
STRING_METADATA.getBytes());
assertThat(BluetoothUtils.getUriMetaData(mBluetoothDevice,
BluetoothDevice.METADATA_MAIN_ICON)).isEqualTo(Uri.parse(STRING_METADATA));
}
@Test
public void getUriMetaData_nullMetaData_getNullUri() {
when(mBluetoothDevice.getMetadata(
BluetoothDevice.METADATA_MAIN_ICON)).thenReturn(null);
assertThat(BluetoothUtils.getUriMetaData(mBluetoothDevice,
BluetoothDevice.METADATA_MAIN_ICON)).isNull();
}
@Test
public void getControlUriMetaData_hasMetaData_returnsCorrectMetaData() {
when(mBluetoothDevice.getMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS)).thenReturn(
CONTROL_METADATA.getBytes());
assertThat(BluetoothUtils.getControlUriMetaData(mBluetoothDevice)).isEqualTo(
STRING_METADATA);
}
@Test
public void isAdvancedDetailsHeader_untetheredHeadset_returnTrue() {
when(mBluetoothDevice.getMetadata(
BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
BOOL_METADATA.getBytes());
assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isEqualTo(true);
}
@Test
public void isAdvancedDetailsHeader_deviceTypeUntetheredHeadset_returnTrue() {
when(mBluetoothDevice.getMetadata(
BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET.getBytes());
assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isEqualTo(true);
}
@Test
public void isAdvancedDetailsHeader_deviceTypeWatch_returnTrue() {
when(mBluetoothDevice.getMetadata(
BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
BluetoothDevice.DEVICE_TYPE_WATCH.getBytes());
assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isEqualTo(true);
}
@Test
public void isAdvancedDetailsHeader_deviceTypeStylus_returnTrue() {
when(mBluetoothDevice.getMetadata(
BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
BluetoothDevice.DEVICE_TYPE_STYLUS.getBytes());
assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isEqualTo(true);
}
@Test
public void isAdvancedDetailsHeader_deviceTypeDefault_returnTrue() {
when(mBluetoothDevice.getMetadata(
BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
BluetoothDevice.DEVICE_TYPE_DEFAULT.getBytes());
assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isEqualTo(true);
}
@Test
public void isAdvancedDetailsHeader_noMetadata_returnFalse() {
assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isEqualTo(false);
}
@Test
public void isAdvancedUntetheredDevice_untetheredHeadset_returnTrue() {
when(mBluetoothDevice.getMetadata(
BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
BOOL_METADATA.getBytes());
assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isEqualTo(true);
}
@Test
public void isAdvancedUntetheredDevice_deviceTypeUntetheredHeadset_returnTrue() {
when(mBluetoothDevice.getMetadata(
BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET.getBytes());
assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isEqualTo(true);
}
@Test
public void isAdvancedUntetheredDevice_deviceTypeWatch_returnFalse() {
when(mBluetoothDevice.getMetadata(
BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
BluetoothDevice.DEVICE_TYPE_WATCH.getBytes());
assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isEqualTo(false);
}
@Test
public void isAdvancedUntetheredDevice_deviceTypeDefault_returnFalse() {
when(mBluetoothDevice.getMetadata(
BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
BluetoothDevice.DEVICE_TYPE_DEFAULT.getBytes());
assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isEqualTo(false);
}
@Test
public void isAdvancedUntetheredDevice_noMetadata_returnFalse() {
assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isEqualTo(false);
}
@Test
public void isAvailableMediaBluetoothDevice_isConnectedLeAudioDevice_returnTrue() {
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mBluetoothDevice.isConnected()).thenReturn(true);
assertThat(BluetoothUtils.isAvailableMediaBluetoothDevice(mCachedBluetoothDevice,
mAudioManager)).isEqualTo(true);
}
@Test
public void isAvailableMediaBluetoothDevice_isHeadset_isConnectedA2dpDevice_returnFalse() {
when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_RINGTONE);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mBluetoothDevice.isConnected()).thenReturn(true);
assertThat(BluetoothUtils.isAvailableMediaBluetoothDevice(mCachedBluetoothDevice,
mAudioManager)).isEqualTo(false);
}
@Test
public void isAvailableMediaBluetoothDevice_isA2dp_isConnectedA2dpDevice_returnTrue() {
when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mBluetoothDevice.isConnected()).thenReturn(true);
assertThat(BluetoothUtils.isAvailableMediaBluetoothDevice(mCachedBluetoothDevice,
mAudioManager)).isEqualTo(true);
}
@Test
public void isAvailableMediaBluetoothDevice_isHeadset_isConnectedHfpDevice_returnTrue() {
when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_RINGTONE);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mBluetoothDevice.isConnected()).thenReturn(true);
assertThat(BluetoothUtils.isAvailableMediaBluetoothDevice(mCachedBluetoothDevice,
mAudioManager)).isEqualTo(true);
}
@Test
public void isConnectedBluetoothDevice_isConnectedLeAudioDevice_returnFalse() {
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mBluetoothDevice.isConnected()).thenReturn(true);
assertThat(BluetoothUtils.isConnectedBluetoothDevice(mCachedBluetoothDevice,
mAudioManager)).isEqualTo(false);
}
@Test
public void isConnectedBluetoothDevice_isHeadset_isConnectedA2dpDevice_returnTrue() {
when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_RINGTONE);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mBluetoothDevice.isConnected()).thenReturn(true);
assertThat(BluetoothUtils.isConnectedBluetoothDevice(mCachedBluetoothDevice,
mAudioManager)).isEqualTo(true);
}
@Test
public void isConnectedBluetoothDevice_isA2dp_isConnectedA2dpDevice_returnFalse() {
when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mBluetoothDevice.isConnected()).thenReturn(true);
assertThat(BluetoothUtils.isConnectedBluetoothDevice(mCachedBluetoothDevice,
mAudioManager)).isEqualTo(false);
}
@Test
public void isConnectedBluetoothDevice_isHeadset_isConnectedHfpDevice_returnFalse() {
when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_RINGTONE);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mBluetoothDevice.isConnected()).thenReturn(true);
assertThat(BluetoothUtils.isConnectedBluetoothDevice(mCachedBluetoothDevice,
mAudioManager)).isEqualTo(false);
}
@Test
public void isConnectedBluetoothDevice_isNotConnected_returnFalse() {
when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_RINGTONE);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mBluetoothDevice.isConnected()).thenReturn(false);
assertThat(BluetoothUtils.isConnectedBluetoothDevice(mCachedBluetoothDevice,
mAudioManager)).isEqualTo(false);
}
@Test
public void isExclusivelyManagedBluetoothDevice_isNotExclusivelyManaged_returnFalse() {
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
null);
assertThat(BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext,
mBluetoothDevice)).isEqualTo(false);
}
@Test
public void isExclusivelyManagedBluetoothDevice_isNotInAllowList_returnFalse() {
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
FAKE_EXCLUSIVE_MANAGER_NAME.getBytes());
assertThat(BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext,
mBluetoothDevice)).isEqualTo(false);
}
@Test
public void isExclusivelyManagedBluetoothDevice_packageNotInstalled_returnFalse()
throws Exception {
final String exclusiveManagerName =
BluetoothUtils.getExclusiveManagers().stream().findAny().orElse(
FAKE_EXCLUSIVE_MANAGER_NAME);
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
exclusiveManagerName.getBytes());
when(mContext.getPackageManager()).thenReturn(mPackageManager);
doThrow(new PackageManager.NameNotFoundException()).when(mPackageManager).getPackageInfo(
exclusiveManagerName, 0);
assertThat(BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext,
mBluetoothDevice)).isEqualTo(false);
}
@Test
public void isExclusivelyManagedBluetoothDevice_isExclusivelyManaged_returnTrue()
throws Exception {
final String exclusiveManagerName =
BluetoothUtils.getExclusiveManagers().stream().findAny().orElse(
FAKE_EXCLUSIVE_MANAGER_NAME);
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
exclusiveManagerName.getBytes());
when(mContext.getPackageManager()).thenReturn(mPackageManager);
doReturn(new PackageInfo()).when(mPackageManager).getPackageInfo(exclusiveManagerName, 0);
assertThat(BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext,
mBluetoothDevice)).isEqualTo(true);
}
}

View File

@@ -0,0 +1,625 @@
/*
* 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.
*/
package com.android.settingslib.bluetooth;
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.doNothing;
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.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothUuid;
import android.content.Context;
import android.os.Parcel;
import android.os.ParcelUuid;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.Collection;
import java.util.Map;
@RunWith(RobolectricTestRunner.class)
public class CachedBluetoothDeviceManagerTest {
private final static String DEVICE_NAME_1 = "TestName_1";
private final static String DEVICE_NAME_2 = "TestName_2";
private final static String DEVICE_NAME_3 = "TestName_3";
private final static String DEVICE_ALIAS_1 = "TestAlias_1";
private final static String DEVICE_ALIAS_2 = "TestAlias_2";
private final static String DEVICE_ALIAS_3 = "TestAlias_3";
private final static String DEVICE_ADDRESS_1 = "AA:BB:CC:DD:EE:11";
private final static String DEVICE_ADDRESS_2 = "AA:BB:CC:DD:EE:22";
private final static String DEVICE_ADDRESS_3 = "AA:BB:CC:DD:EE:33";
private final static long HISYNCID1 = 10;
private final static long HISYNCID2 = 11;
private final static Map<Integer, ParcelUuid> CAP_GROUP1 =
Map.of(1, BluetoothUuid.CAP);
private final static Map<Integer, ParcelUuid> CAP_GROUP2 =
Map.of(2, BluetoothUuid.CAP);
private final BluetoothClass DEVICE_CLASS_1 =
createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
private final BluetoothClass DEVICE_CLASS_2 =
createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE);
@Mock
private LocalBluetoothProfileManager mLocalProfileManager;
@Mock
private LocalBluetoothManager mLocalBluetoothManager;
@Mock
private BluetoothEventManager mBluetoothEventManager;
@Mock
private HeadsetProfile mHfpProfile;
@Mock
private A2dpProfile mA2dpProfile;
@Mock
private PanProfile mPanProfile;
@Mock
private HearingAidProfile mHearingAidProfile;
@Mock
private CsipSetCoordinatorProfile mCsipSetCoordinatorProfile;
@Mock
private BluetoothDevice mDevice1;
@Mock
private BluetoothDevice mDevice2;
@Mock
private BluetoothDevice mDevice3;
private CachedBluetoothDevice mCachedDevice1;
private CachedBluetoothDevice mCachedDevice2;
private CachedBluetoothDevice mCachedDevice3;
private CachedBluetoothDeviceManager mCachedDeviceManager;
private HearingAidDeviceManager mHearingAidDeviceManager;
private Context mContext;
private BluetoothClass createBtClass(int deviceClass) {
Parcel p = Parcel.obtain();
p.writeInt(deviceClass);
p.setDataPosition(0); // reset position of parcel before passing to constructor
BluetoothClass bluetoothClass = BluetoothClass.CREATOR.createFromParcel(p);
p.recycle();
return bluetoothClass;
}
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
when(mDevice1.getAddress()).thenReturn(DEVICE_ADDRESS_1);
when(mDevice2.getAddress()).thenReturn(DEVICE_ADDRESS_2);
when(mDevice3.getAddress()).thenReturn(DEVICE_ADDRESS_3);
when(mDevice1.getName()).thenReturn(DEVICE_NAME_1);
when(mDevice2.getName()).thenReturn(DEVICE_NAME_2);
when(mDevice3.getName()).thenReturn(DEVICE_NAME_3);
when(mDevice1.getAlias()).thenReturn(DEVICE_ALIAS_1);
when(mDevice2.getAlias()).thenReturn(DEVICE_ALIAS_2);
when(mDevice3.getAlias()).thenReturn(DEVICE_ALIAS_3);
when(mDevice1.getBluetoothClass()).thenReturn(DEVICE_CLASS_1);
when(mDevice2.getBluetoothClass()).thenReturn(DEVICE_CLASS_2);
when(mDevice3.getBluetoothClass()).thenReturn(DEVICE_CLASS_2);
when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalProfileManager);
when(mHfpProfile.isProfileReady()).thenReturn(true);
when(mA2dpProfile.isProfileReady()).thenReturn(true);
when(mPanProfile.isProfileReady()).thenReturn(true);
when(mHearingAidProfile.isProfileReady()).thenReturn(true);
when(mCsipSetCoordinatorProfile.isProfileReady())
.thenReturn(true);
doAnswer((invocation) -> mHearingAidProfile).
when(mLocalProfileManager).getHearingAidProfile();
doAnswer((invocation) -> mCsipSetCoordinatorProfile)
.when(mLocalProfileManager).getCsipSetCoordinatorProfile();
mCachedDeviceManager = new CachedBluetoothDeviceManager(mContext, mLocalBluetoothManager);
mCachedDevice1 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1));
mCachedDevice2 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2));
mCachedDevice3 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice3));
}
/**
* Test to verify addDevice().
*/
@Test
public void addDevice_validCachedDevices_devicesAdded() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
assertThat(cachedDevice2).isNotNull();
Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
assertThat(devices).contains(cachedDevice1);
assertThat(devices).contains(cachedDevice2);
assertThat(mCachedDeviceManager.findDevice(mDevice1)).isEqualTo(cachedDevice1);
assertThat(mCachedDeviceManager.findDevice(mDevice2)).isEqualTo(cachedDevice2);
}
/**
* Test to verify getSubDevice(), new device has the same HiSyncId.
*/
@Test
public void addDevice_sameHiSyncId_validSubDevice() {
doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice2);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
assertThat(cachedDevice1.getSubDevice()).isEqualTo(cachedDevice2);
}
/**
* Test to verify getSubDevice(), new device has the different HiSyncId.
*/
@Test
public void addDevice_differentHiSyncId_validSubDevice() {
doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
doAnswer((invocation) -> HISYNCID2).when(mHearingAidProfile).getHiSyncId(mDevice2);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
assertThat(cachedDevice1.getSubDevice()).isNull();
}
/**
* Test to verify addDevice(), new device has the same HiSyncId.
*/
@Test
public void addDevice_sameHiSyncId_validCachedDevices_mainDevicesAdded_subDevicesNotAdded() {
doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice2);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
assertThat(devices).contains(cachedDevice1);
assertThat(devices).doesNotContain(cachedDevice2);
}
/**
* Test to verify addDevice(), new device has the different HiSyncId.
*/
@Test
public void addDevice_differentHiSyncId_validCachedDevices_bothAdded() {
doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
doAnswer((invocation) -> HISYNCID2).when(mHearingAidProfile).getHiSyncId(mDevice2);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
assertThat(devices).contains(cachedDevice1);
assertThat(devices).contains(cachedDevice2);
}
/**
* Test to verify addDevice(), the duplicated device should not added.
*/
@Test
public void addDevice_addDuplicatedDevice_duplicateDeviceShouldNotAdded() {
final CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
final CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
assertThat(cachedDevice2).isNotNull();
final CachedBluetoothDevice cachedDevice3 = mCachedDeviceManager.addDevice(mDevice2);
assertThat(cachedDevice3).isNotNull();
final CachedBluetoothDevice cachedDevice4 = mCachedDeviceManager.addDevice(mDevice2);
assertThat(cachedDevice4).isNotNull();
final Collection<CachedBluetoothDevice> devices =
mCachedDeviceManager.getCachedDevicesCopy();
assertThat(devices.size()).isEqualTo(2);
}
/**
* Test to verify findDevice(), new device has the same HiSyncId.
*/
@Test
public void findDevice_sameHiSyncId_foundBothDevice() {
doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice2);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
assertThat(mCachedDeviceManager.findDevice(mDevice1)).isEqualTo(cachedDevice1);
assertThat(mCachedDeviceManager.findDevice(mDevice2)).isEqualTo(cachedDevice2);
}
/**
* Test to verify getName().
*/
@Test
public void getName_validCachedDevice_nameFound() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
assertThat(mCachedDeviceManager.getName(mDevice1)).isEqualTo(DEVICE_ALIAS_1);
}
/**
* Test to verify onDeviceNameUpdated().
*/
@Test
public void onDeviceNameUpdated_validName_nameUpdated() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
assertThat(cachedDevice1.getName()).isEqualTo(DEVICE_ALIAS_1);
final String newAliasName = "NewAliasName";
when(mDevice1.getAlias()).thenReturn(newAliasName);
mCachedDeviceManager.onDeviceNameUpdated(mDevice1);
assertThat(cachedDevice1.getName()).isEqualTo(newAliasName);
}
/**
* Test to verify clearNonBondedDevices().
*/
@Test
public void clearNonBondedDevices_bondedAndNonBondedDevices_nonBondedDevicesCleared() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
assertThat(cachedDevice2).isNotNull();
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
mCachedDeviceManager.clearNonBondedDevices();
Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
assertThat(devices).contains(cachedDevice1);
assertThat(devices).contains(cachedDevice2);
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
mCachedDeviceManager.clearNonBondedDevices();
devices = mCachedDeviceManager.getCachedDevicesCopy();
assertThat(devices).contains(cachedDevice1);
assertThat(devices).doesNotContain(cachedDevice2);
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
mCachedDeviceManager.clearNonBondedDevices();
devices = mCachedDeviceManager.getCachedDevicesCopy();
assertThat(devices).doesNotContain(cachedDevice1);
assertThat(devices).doesNotContain(cachedDevice2);
}
/**
* Test to verify clearNonBondedDevices() for hearing aids sub device.
*/
@Test
public void clearNonBondedDevices_nonBondedSubDevice() {
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
cachedDevice1.setSubDevice(cachedDevice2);
assertThat(cachedDevice1.getSubDevice()).isEqualTo(cachedDevice2);
mCachedDeviceManager.clearNonBondedDevices();
assertThat(cachedDevice1.getSubDevice()).isNull();
}
/**
* Test to verify OnDeviceUnpaired() for main hearing Aid device unpair.
*/
@Test
public void onDeviceUnpaired_unpairHearingAidMainDevice() {
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
cachedDevice1.setHearingAidInfo(
new HearingAidInfo.Builder().setHiSyncId(HISYNCID1).build());
cachedDevice2.setHearingAidInfo(
new HearingAidInfo.Builder().setHiSyncId(HISYNCID1).build());
cachedDevice1.setSubDevice(cachedDevice2);
// Call onDeviceUnpaired for the one in mCachedDevices.
mCachedDeviceManager.onDeviceUnpaired(cachedDevice2);
verify(mDevice1).removeBond();
}
/**
* Test to verify OnDeviceUnpaired() for sub hearing Aid device unpair.
*/
@Test
public void onDeviceUnpaired_unpairSubDevice() {
when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
cachedDevice1.setSubDevice(cachedDevice2);
// Call onDeviceUnpaired for the one in mCachedDevices.
mCachedDeviceManager.onDeviceUnpaired(cachedDevice1);
verify(mDevice2).removeBond();
}
/**
* Test to verify getSubDeviceSummary() for disconnected sub device
*/
@Test
public void getSubDeviceSummary_SubDeviceDisconnected() {
when(mCachedDevice2.isConnected()).thenReturn(false);
mCachedDevice1.setSubDevice(mCachedDevice2);
mCachedDeviceManager.getSubDeviceSummary(mCachedDevice1);
verify(mCachedDevice2, never()).getConnectionSummary();
}
/**
* Test to verify getSubDeviceSummary() for connected sub device
*/
@Test
public void getSubDeviceSummary_SubDeviceConnected() {
when(mCachedDevice2.isConnected()).thenReturn(true);
mCachedDevice1.setSubDevice(mCachedDevice2);
mCachedDeviceManager.getSubDeviceSummary(mCachedDevice1);
verify(mCachedDevice2).getConnectionSummary();
}
/**
* Test to verify isSubDevice_validSubDevice().
*/
@Test
public void isSubDevice_validSubDevice() {
doReturn(HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
mCachedDeviceManager.addDevice(mDevice1);
// Both device are not sub device in default value.
assertThat(mCachedDeviceManager.isSubDevice(mDevice1)).isFalse();
assertThat(mCachedDeviceManager.isSubDevice(mDevice2)).isFalse();
// Add Device-2 as sub device of Device-1 with same HiSyncId.
doReturn(HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice2);
mCachedDeviceManager.addDevice(mDevice2);
// Verify Device-2 is sub device, but Device-1 is not.
assertThat(mCachedDeviceManager.isSubDevice(mDevice2)).isTrue();
assertThat(mCachedDeviceManager.isSubDevice(mDevice1)).isFalse();
}
/**
* Test to verify updateHearingAidsDevices().
*/
@Test
public void updateHearingAidDevices_directToHearingAidDeviceManager() {
mHearingAidDeviceManager = spy(new HearingAidDeviceManager(mContext, mLocalBluetoothManager,
mCachedDeviceManager.mCachedDevices));
mCachedDeviceManager.mHearingAidDeviceManager = mHearingAidDeviceManager;
mCachedDeviceManager.updateHearingAidsDevices();
verify(mHearingAidDeviceManager).updateHearingAidsDevices();
}
/**
* Test to verify onDeviceDisappeared().
*/
@Test
public void onDeviceDisappeared_deviceBondedUnbonded_unbondedDeviceDisappeared() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
assertThat(mCachedDeviceManager.onDeviceDisappeared(cachedDevice1)).isFalse();
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
assertThat(mCachedDeviceManager.onDeviceDisappeared(cachedDevice1)).isTrue();
}
/**
* Test to verify getMemberDevice(), new device has the same group id.
*/
@Test
public void addDevice_sameGroupId_validMemberDevice() {
doAnswer((invocation) -> CAP_GROUP1).when(mCsipSetCoordinatorProfile)
.getGroupUuidMapByDevice(mDevice1);
doAnswer((invocation) -> CAP_GROUP1).when(mCsipSetCoordinatorProfile)
.getGroupUuidMapByDevice(mDevice2);
doAnswer((invocation) -> CAP_GROUP1).when(mCsipSetCoordinatorProfile)
.getGroupUuidMapByDevice(mDevice3);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
CachedBluetoothDevice cachedDevice3 = mCachedDeviceManager.addDevice(mDevice3);
assertThat(cachedDevice1.getMemberDevice()).contains(cachedDevice2);
assertThat(cachedDevice1.getMemberDevice()).contains(cachedDevice3);
}
/**
* Test to verify getMemberDevice(), new device has the different group id.
*/
@Test
public void addDevice_differentGroupId_validMemberDevice() {
doAnswer((invocation) -> CAP_GROUP1).when(mCsipSetCoordinatorProfile)
.getGroupUuidMapByDevice(mDevice1);
doAnswer((invocation) -> CAP_GROUP2).when(mCsipSetCoordinatorProfile)
.getGroupUuidMapByDevice(mDevice2);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
assertThat(cachedDevice1.getMemberDevice()).isEmpty();
}
/**
* Test to verify addDevice(), new device has the same group id.
*/
@Test
public void addDevice_sameGroupId_validCachedDevices_mainDevicesAdded_memberDevicesNotAdded() {
doAnswer((invocation) -> CAP_GROUP1).when(mCsipSetCoordinatorProfile)
.getGroupUuidMapByDevice(mDevice1);
doAnswer((invocation) -> CAP_GROUP1).when(mCsipSetCoordinatorProfile)
.getGroupUuidMapByDevice(mDevice2);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
assertThat(devices).contains(cachedDevice1);
assertThat(devices).doesNotContain(cachedDevice2);
}
/**
* Test to verify addDevice(), new device has the different group id.
*/
@Test
public void addDevice_differentGroupId_validCachedDevices_bothAdded() {
doAnswer((invocation) -> CAP_GROUP1).when(mCsipSetCoordinatorProfile)
.getGroupUuidMapByDevice(mDevice1);
doAnswer((invocation) -> CAP_GROUP2).when(mCsipSetCoordinatorProfile)
.getGroupUuidMapByDevice(mDevice2);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
assertThat(devices).contains(cachedDevice1);
assertThat(devices).contains(cachedDevice2);
}
/**
* Test to verify clearNonBondedDevices() for csip set member device.
*/
@Test
public void clearNonBondedDevices_nonBondedMemberDevice() {
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
when(mDevice3.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
CachedBluetoothDevice cachedDevice3 = mCachedDeviceManager.addDevice(mDevice3);
cachedDevice1.addMemberDevice(cachedDevice2);
cachedDevice1.addMemberDevice(cachedDevice3);
assertThat(cachedDevice1.getMemberDevice()).contains(cachedDevice2);
assertThat(cachedDevice1.getMemberDevice()).contains(cachedDevice3);
mCachedDeviceManager.clearNonBondedDevices();
assertThat(cachedDevice1.getMemberDevice().contains(cachedDevice2)).isFalse();
assertThat(cachedDevice1.getMemberDevice().contains(cachedDevice3)).isTrue();
}
/**
* Test to verify OnDeviceUnpaired() for csip device unpair.
*/
@Test
public void onDeviceUnpaired_unpairCsipMainDevice() {
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
cachedDevice1.setGroupId(1);
cachedDevice2.setGroupId(1);
cachedDevice1.addMemberDevice(cachedDevice2);
// Call onDeviceUnpaired for the one in mCachedDevices.
mCachedDeviceManager.onDeviceUnpaired(cachedDevice1);
verify(mDevice2).removeBond();
assertThat(cachedDevice1.getGroupId()).isEqualTo(
BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
assertThat(cachedDevice2.getGroupId()).isEqualTo(
BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
}
/**
* Test to verify OnDeviceUnpaired() for csip device unpair.
*/
@Test
public void onDeviceUnpaired_unpairCsipSubDevice() {
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
cachedDevice1.setGroupId(1);
cachedDevice2.setGroupId(1);
cachedDevice1.addMemberDevice(cachedDevice2);
// Call onDeviceUnpaired for the one in mCachedDevices.
mCachedDeviceManager.onDeviceUnpaired(cachedDevice2);
verify(mDevice1).removeBond();
assertThat(cachedDevice2.getGroupId()).isEqualTo(
BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
}
/**
* Test to verify isSubDevice_validSubDevice().
*/
@Test
public void isSubDevice_validMemberDevice() {
doReturn(CAP_GROUP1).when(mCsipSetCoordinatorProfile).getGroupUuidMapByDevice(mDevice1);
mCachedDeviceManager.addDevice(mDevice1);
// Both device are not sub device in default value.
assertThat(mCachedDeviceManager.isSubDevice(mDevice1)).isFalse();
assertThat(mCachedDeviceManager.isSubDevice(mDevice2)).isFalse();
// Add Device-2 as device with Device-1 with the same group id, and add Device-3 with
// the different group id.
doReturn(CAP_GROUP1).when(mCsipSetCoordinatorProfile).getGroupUuidMapByDevice(mDevice2);
doReturn(CAP_GROUP2).when(mCsipSetCoordinatorProfile).getGroupUuidMapByDevice(mDevice3);
mCachedDeviceManager.addDevice(mDevice2);
mCachedDeviceManager.addDevice(mDevice3);
// Verify Device-2 is sub device, but Device-1, and Device-3 is not.
assertThat(mCachedDeviceManager.isSubDevice(mDevice1)).isFalse();
assertThat(mCachedDeviceManager.isSubDevice(mDevice2)).isTrue();
assertThat(mCachedDeviceManager.isSubDevice(mDevice3)).isFalse();
}
@Test
public void pairDeviceByCsip_device2AndCapGroup1_device2StartsPairing() {
doReturn(CAP_GROUP1).when(mCsipSetCoordinatorProfile).getGroupUuidMapByDevice(mDevice1);
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mDevice1.getPhonebookAccessPermission()).thenReturn(BluetoothDevice.ACCESS_ALLOWED);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
assertThat(cachedDevice2).isNotNull();
int groupId = CAP_GROUP1.keySet().stream().findFirst().orElse(
BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
assertThat(groupId).isNotEqualTo(BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
mCachedDeviceManager.pairDeviceByCsip(mDevice2, groupId);
verify(mDevice2).setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
verify(mDevice2).createBond(BluetoothDevice.TRANSPORT_LE);
}
@Test
public void onActiveDeviceChanged_validHiSyncId_callExpectedFunction() {
mHearingAidDeviceManager = spy(new HearingAidDeviceManager(mContext, mLocalBluetoothManager,
mCachedDeviceManager.mCachedDevices));
doNothing().when(mHearingAidDeviceManager).onActiveDeviceChanged(any());
mCachedDeviceManager.mHearingAidDeviceManager = mHearingAidDeviceManager;
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
cachedDevice1.setHearingAidInfo(
new HearingAidInfo.Builder().setHiSyncId(HISYNCID1).build());
mCachedDeviceManager.onActiveDeviceChanged(cachedDevice1);
verify(mHearingAidDeviceManager).onActiveDeviceChanged(cachedDevice1);
}
}

View File

@@ -0,0 +1,355 @@
/*
* 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.settingslib.bluetooth;
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.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.os.Parcel;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class CsipDeviceManagerTest {
private final static String DEVICE_NAME_1 = "TestName_1";
private final static String DEVICE_NAME_2 = "TestName_2";
private final static String DEVICE_NAME_3 = "TestName_3";
private final static String DEVICE_ALIAS_1 = "TestAlias_1";
private final static String DEVICE_ALIAS_2 = "TestAlias_2";
private final static String DEVICE_ALIAS_3 = "TestAlias_3";
private final static String DEVICE_ADDRESS_1 = "AA:BB:CC:DD:EE:11";
private final static String DEVICE_ADDRESS_2 = "AA:BB:CC:DD:EE:22";
private final static String DEVICE_ADDRESS_3 = "AA:BB:CC:DD:EE:33";
private final static int GROUP1 = 1;
private final BluetoothClass DEVICE_CLASS_1 =
createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
private final BluetoothClass DEVICE_CLASS_2 =
createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE);
@Mock
private LocalBluetoothManager mLocalBluetoothManager;
@Mock
private LocalBluetoothProfileManager mLocalProfileManager;
@Mock
private BluetoothEventManager mBluetoothEventManager;
@Mock
private BluetoothDevice mDevice1;
@Mock
private BluetoothDevice mDevice2;
@Mock
private BluetoothDevice mDevice3;
@Mock
private HeadsetProfile mHfpProfile;
@Mock
private A2dpProfile mA2dpProfile;
@Mock
private LeAudioProfile mLeAudioProfile;
private CachedBluetoothDevice mCachedDevice1;
private CachedBluetoothDevice mCachedDevice2;
private CachedBluetoothDevice mCachedDevice3;
private CachedBluetoothDeviceManager mCachedDeviceManager;
private CsipDeviceManager mCsipDeviceManager;
private Context mContext;
private List<CachedBluetoothDevice> mCachedDevices = new ArrayList<CachedBluetoothDevice>();
private BluetoothClass createBtClass(int deviceClass) {
Parcel p = Parcel.obtain();
p.writeInt(deviceClass);
p.setDataPosition(0); // reset position of parcel before passing to constructor
BluetoothClass bluetoothClass = BluetoothClass.CREATOR.createFromParcel(p);
p.recycle();
return bluetoothClass;
}
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
when(mDevice1.getAddress()).thenReturn(DEVICE_ADDRESS_1);
when(mDevice2.getAddress()).thenReturn(DEVICE_ADDRESS_2);
when(mDevice3.getAddress()).thenReturn(DEVICE_ADDRESS_3);
when(mDevice1.getName()).thenReturn(DEVICE_NAME_1);
when(mDevice2.getName()).thenReturn(DEVICE_NAME_2);
when(mDevice3.getName()).thenReturn(DEVICE_NAME_3);
when(mDevice1.getAlias()).thenReturn(DEVICE_ALIAS_1);
when(mDevice2.getAlias()).thenReturn(DEVICE_ALIAS_2);
when(mDevice3.getAlias()).thenReturn(DEVICE_ALIAS_3);
when(mDevice1.getBluetoothClass()).thenReturn(DEVICE_CLASS_1);
when(mDevice2.getBluetoothClass()).thenReturn(DEVICE_CLASS_2);
when(mDevice3.getBluetoothClass()).thenReturn(DEVICE_CLASS_2);
when(mDevice1.isConnected()).thenReturn(true);
when(mDevice2.isConnected()).thenReturn(true);
when(mDevice3.isConnected()).thenReturn(true);
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mDevice3.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalProfileManager);
when(mLocalProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
when(mLocalProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
when(mLocalProfileManager.getHeadsetProfile()).thenReturn(mHfpProfile);
when(mLeAudioProfile.getConnectedGroupLeadDevice(anyInt())).thenReturn(null);
mCachedDeviceManager = new CachedBluetoothDeviceManager(mContext, mLocalBluetoothManager);
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
mCachedDevices = mCachedDeviceManager.mCachedDevices;
mCsipDeviceManager = mCachedDeviceManager.mCsipDeviceManager;
// Setup the default for testing
mCachedDevice1 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1));
mCachedDevice2 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2));
mCachedDevice3 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice3));
mCachedDevice1.setGroupId(GROUP1);
mCachedDevice2.setGroupId(GROUP1);
mCachedDevice1.addMemberDevice(mCachedDevice2);
mCachedDevices.add(mCachedDevice1);
mCachedDevices.add(mCachedDevice3);
List<LocalBluetoothProfile> profiles = new ArrayList<LocalBluetoothProfile>();
profiles.add(mHfpProfile);
profiles.add(mA2dpProfile);
profiles.add(mLeAudioProfile);
when(mCachedDevice1.getConnectableProfiles()).thenReturn(profiles);
when(mCachedDevice1.isConnected()).thenReturn(true);
profiles.clear();
profiles.add(mLeAudioProfile);
when(mCachedDevice2.getConnectableProfiles()).thenReturn(profiles);
when(mCachedDevice2.isConnected()).thenReturn(true);
profiles.clear();
profiles.add(mHfpProfile);
profiles.add(mA2dpProfile);
when(mCachedDevice3.getConnectableProfiles()).thenReturn(profiles);
when(mCachedDevice3.isConnected()).thenReturn(true);
}
@Test
public void onProfileConnectionStateChangedIfProcessed_profileIsConnecting_returnOff() {
assertThat(mCsipDeviceManager.onProfileConnectionStateChangedIfProcessed(mCachedDevice1,
BluetoothProfile.STATE_CONNECTING)).isFalse();
}
@Test
public void onProfileConnectionStateChangedIfProcessed_profileIsDisconnecting_returnOff() {
assertThat(mCsipDeviceManager.onProfileConnectionStateChangedIfProcessed(mCachedDevice1,
BluetoothProfile.STATE_DISCONNECTING)).isFalse();
}
@Test
public void updateRelationshipOfGroupDevices_invalidGroupId_returnOff() {
assertThat(mCsipDeviceManager.updateRelationshipOfGroupDevices(
BluetoothCsipSetCoordinator.GROUP_ID_INVALID)).isFalse();
}
@Test
public void getGroupDevicesFromAllOfDevicesList_invalidGroupId_returnEmpty() {
assertThat(mCsipDeviceManager.getGroupDevicesFromAllOfDevicesList(
BluetoothCsipSetCoordinator.GROUP_ID_INVALID).isEmpty()).isTrue();
}
@Test
public void getGroupDevicesFromAllOfDevicesList_validGroupId_returnGroupDevices() {
List<CachedBluetoothDevice> expectedList = new ArrayList<>();
expectedList.add(mCachedDevice1);
expectedList.add(mCachedDevice2);
assertThat(mCsipDeviceManager.getGroupDevicesFromAllOfDevicesList(GROUP1))
.isEqualTo(expectedList);
}
@Test
public void getPreferredMainDevice_dualModeDevice_returnDualModeDevice() {
CachedBluetoothDevice expectedDevice = mCachedDevice1;
assertThat(
mCsipDeviceManager.getPreferredMainDevice(GROUP1,
mCsipDeviceManager.getGroupDevicesFromAllOfDevicesList(GROUP1)))
.isEqualTo(expectedDevice);
}
@Test
public void getPreferredMainDevice_noConnectedDualModeDevice_returnLeadDevice() {
when(mCachedDevice1.isConnected()).thenReturn(false);
when(mDevice1.isConnected()).thenReturn(false);
when(mLeAudioProfile.getConnectedGroupLeadDevice(anyInt())).thenReturn(mDevice2);
CachedBluetoothDevice expectedDevice = mCachedDevice2;
assertThat(
mCsipDeviceManager.getPreferredMainDevice(GROUP1,
mCsipDeviceManager.getGroupDevicesFromAllOfDevicesList(GROUP1)))
.isEqualTo(expectedDevice);
}
@Test
public void getPreferredMainDevice_noConnectedDualModeDeviceNoLeadDevice_returnConnectedOne() {
when(mCachedDevice1.isConnected()).thenReturn(false);
when(mCachedDevice2.isConnected()).thenReturn(true);
when(mDevice1.isConnected()).thenReturn(false);
when(mDevice2.isConnected()).thenReturn(true);
CachedBluetoothDevice expectedDevice = mCachedDevice2;
assertThat(
mCsipDeviceManager.getPreferredMainDevice(GROUP1,
mCsipDeviceManager.getGroupDevicesFromAllOfDevicesList(GROUP1)))
.isEqualTo(expectedDevice);
}
@Test
public void getPreferredMainDevice_noConnectedDevice_returnDualModeDevice() {
when(mCachedDevice1.isConnected()).thenReturn(false);
when(mCachedDevice2.isConnected()).thenReturn(false);
when(mDevice1.isConnected()).thenReturn(false);
when(mDevice2.isConnected()).thenReturn(false);
CachedBluetoothDevice expectedDevice = mCachedDevice1;
assertThat(
mCsipDeviceManager.getPreferredMainDevice(GROUP1,
mCsipDeviceManager.getGroupDevicesFromAllOfDevicesList(GROUP1)))
.isEqualTo(expectedDevice);
}
@Test
public void getPreferredMainDevice_noConnectedDeviceNoDualMode_returnFirstOneDevice() {
when(mCachedDevice1.isConnected()).thenReturn(false);
when(mCachedDevice2.isConnected()).thenReturn(false);
when(mDevice1.isConnected()).thenReturn(false);
when(mDevice2.isConnected()).thenReturn(false);
List<LocalBluetoothProfile> profiles = new ArrayList<LocalBluetoothProfile>();
profiles.add(mLeAudioProfile);
when(mCachedDevice1.getConnectableProfiles()).thenReturn(profiles);
CachedBluetoothDevice expectedDevice = mCachedDevice1;
assertThat(
mCsipDeviceManager.getPreferredMainDevice(GROUP1,
mCsipDeviceManager.getGroupDevicesFromAllOfDevicesList(GROUP1)))
.isEqualTo(expectedDevice);
}
@Test
public void addMemberDevicesIntoMainDevice_noPreferredDevice_returnFalseAndNoChangeList() {
CachedBluetoothDevice preferredDevice = null;
List<CachedBluetoothDevice> expectedList = new ArrayList<>();
for (CachedBluetoothDevice item : mCachedDevices) {
expectedList.add(item);
}
assertThat(mCsipDeviceManager.addMemberDevicesIntoMainDevice(GROUP1, preferredDevice))
.isFalse();
for (CachedBluetoothDevice expectedItem : expectedList) {
assertThat(mCachedDevices.contains(expectedItem)).isTrue();
}
}
@Test
public void
addMemberDevicesIntoMainDevice_preferredDeviceIsMainAndNoOtherInList_noChangeList() {
// Condition: The preferredDevice is main and no other main device in top list
// Expected Result: return false and the list is no changed
CachedBluetoothDevice preferredDevice = mCachedDevice1;
List<CachedBluetoothDevice> expectedList = new ArrayList<>();
for (CachedBluetoothDevice item : mCachedDevices) {
expectedList.add(item);
}
assertThat(mCsipDeviceManager.addMemberDevicesIntoMainDevice(GROUP1, preferredDevice))
.isFalse();
for (CachedBluetoothDevice expectedItem : expectedList) {
assertThat(mCachedDevices.contains(expectedItem)).isTrue();
}
}
@Test
public void addMemberDevicesIntoMainDevice_preferredDeviceIsMainAndTwoMain_returnTrue() {
// Condition: The preferredDevice is main and there is another main device in top list
// Expected Result: return true and there is the preferredDevice in top list
CachedBluetoothDevice preferredDevice = mCachedDevice1;
mCachedDevice1.getMemberDevice().clear();
mCachedDevices.clear();
mCachedDevices.add(preferredDevice);
mCachedDevices.add(mCachedDevice2);
mCachedDevices.add(mCachedDevice3);
assertThat(mCsipDeviceManager.addMemberDevicesIntoMainDevice(GROUP1, preferredDevice))
.isTrue();
assertThat(mCachedDevices.contains(preferredDevice)).isTrue();
assertThat(mCachedDevices.contains(mCachedDevice2)).isFalse();
assertThat(mCachedDevices.contains(mCachedDevice3)).isTrue();
assertThat(preferredDevice.getMemberDevice()).contains(mCachedDevice2);
}
@Test
public void addMemberDevicesIntoMainDevice_preferredDeviceIsMember_returnTrue() {
// Condition: The preferredDevice is member
// Expected Result: return true and there is the preferredDevice in top list
CachedBluetoothDevice preferredDevice = mCachedDevice2;
BluetoothDevice expectedMainBluetoothDevice = preferredDevice.getDevice();
assertThat(mCsipDeviceManager.addMemberDevicesIntoMainDevice(GROUP1, preferredDevice))
.isTrue();
// expected main is mCachedDevice1 which is the main of preferredDevice, since system
// switch the relationship between preferredDevice and the main of preferredDevice
assertThat(mCachedDevices.contains(mCachedDevice1)).isTrue();
assertThat(mCachedDevices.contains(mCachedDevice2)).isFalse();
assertThat(mCachedDevices.contains(mCachedDevice3)).isTrue();
assertThat(mCachedDevice1.getMemberDevice()).contains(mCachedDevice2);
assertThat(mCachedDevice1.getDevice()).isEqualTo(expectedMainBluetoothDevice);
}
@Test
public void addMemberDevicesIntoMainDevice_preferredDeviceIsMemberAndTwoMain_returnTrue() {
// Condition: The preferredDevice is member and there are two main device in top list
// Expected Result: return true and there is the preferredDevice in top list
CachedBluetoothDevice preferredDevice = mCachedDevice2;
BluetoothDevice expectedMainBluetoothDevice = preferredDevice.getDevice();
mCachedDevice3.setGroupId(GROUP1);
assertThat(mCsipDeviceManager.addMemberDevicesIntoMainDevice(GROUP1, preferredDevice))
.isTrue();
// expected main is mCachedDevice1 which is the main of preferredDevice, since system
// switch the relationship between preferredDevice and the main of preferredDevice
assertThat(mCachedDevices.contains(mCachedDevice1)).isTrue();
assertThat(mCachedDevices.contains(mCachedDevice2)).isFalse();
assertThat(mCachedDevices.contains(mCachedDevice3)).isFalse();
assertThat(mCachedDevice1.getMemberDevice()).contains(mCachedDevice2);
assertThat(mCachedDevice1.getMemberDevice()).contains(mCachedDevice3);
assertThat(mCachedDevice1.getMemberDevice()).contains(mCachedDevice3);
assertThat(mCachedDevice1.getDevice()).isEqualTo(expectedMainBluetoothDevice);
}
}

View File

@@ -0,0 +1,489 @@
/*
* 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.settingslib.bluetooth;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHapClient;
import android.bluetooth.BluetoothHapPresetInfo;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.test.core.app.ApplicationProvider;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
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 org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class HapClientProfileTest {
private static final int TEST_GROUP_ID = 1;
private static final int TEST_PRESET_INDEX = 1;
private static final String TEST_DEVICE_NAME = "test_device";
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
@Mock
private CachedBluetoothDeviceManager mDeviceManager;
@Mock
private LocalBluetoothProfileManager mProfileManager;
@Mock
private BluetoothDevice mBluetoothDevice;
@Mock
private BluetoothHapClient mService;
@Mock
private BluetoothHapPresetInfo mPresetInfo;
private final Context mContext = ApplicationProvider.getApplicationContext();
private BluetoothProfile.ServiceListener mServiceListener;
private HapClientProfile mProfile;
@Before
public void setUp() {
mProfile = new HapClientProfile(mContext, mDeviceManager, mProfileManager);
final BluetoothManager bluetoothManager = mContext.getSystemService(BluetoothManager.class);
final ShadowBluetoothAdapter shadowBluetoothAdapter =
Shadow.extract(bluetoothManager.getAdapter());
mServiceListener = shadowBluetoothAdapter.getServiceListener();
}
@Test
public void onServiceConnected_isProfileReady() {
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
assertThat(mProfile.isProfileReady()).isTrue();
verify(mProfileManager).callServiceConnectedListeners();
}
@Test
public void onServiceDisconnected_isProfileNotReady() {
mServiceListener.onServiceDisconnected(BluetoothProfile.HAP_CLIENT);
assertThat(mProfile.isProfileReady()).isFalse();
verify(mProfileManager).callServiceDisconnectedListeners();
}
@Test
public void getConnectionStatus_returnCorrectConnectionState() {
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
when(mService.getConnectionState(mBluetoothDevice))
.thenReturn(BluetoothProfile.STATE_CONNECTED);
assertThat(mProfile.getConnectionStatus(mBluetoothDevice))
.isEqualTo(BluetoothProfile.STATE_CONNECTED);
}
@Test
public void isEnabled_connectionPolicyAllowed_returnTrue() {
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
when(mService.getConnectionPolicy(mBluetoothDevice)).thenReturn(CONNECTION_POLICY_ALLOWED);
assertThat(mProfile.isEnabled(mBluetoothDevice)).isTrue();
}
@Test
public void isEnabled_connectionPolicyForbidden_returnFalse() {
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
when(mService.getConnectionPolicy(mBluetoothDevice))
.thenReturn(CONNECTION_POLICY_FORBIDDEN);
assertThat(mProfile.isEnabled(mBluetoothDevice)).isFalse();
}
@Test
public void getConnectionPolicy_returnCorrectConnectionPolicy() {
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
when(mService.getConnectionPolicy(mBluetoothDevice)).thenReturn(CONNECTION_POLICY_ALLOWED);
assertThat(mProfile.getConnectionPolicy(mBluetoothDevice))
.isEqualTo(CONNECTION_POLICY_ALLOWED);
}
@Test
public void setEnabled_connectionPolicyAllowed_setConnectionPolicyAllowed_returnFalse() {
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
when(mService.getConnectionPolicy(mBluetoothDevice)).thenReturn(CONNECTION_POLICY_ALLOWED);
when(mService.setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED))
.thenReturn(true);
assertThat(mProfile.setEnabled(mBluetoothDevice, true)).isFalse();
}
@Test
public void setEnabled_connectionPolicyForbidden_setConnectionPolicyAllowed_returnTrue() {
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
when(mService.getConnectionPolicy(mBluetoothDevice))
.thenReturn(CONNECTION_POLICY_FORBIDDEN);
when(mService.setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED))
.thenReturn(true);
assertThat(mProfile.setEnabled(mBluetoothDevice, true)).isTrue();
}
@Test
public void setEnabled_connectionPolicyAllowed_setConnectionPolicyForbidden_returnTrue() {
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
when(mService.getConnectionPolicy(mBluetoothDevice)).thenReturn(CONNECTION_POLICY_ALLOWED);
when(mService.setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN))
.thenReturn(true);
assertThat(mProfile.setEnabled(mBluetoothDevice, false)).isTrue();
}
@Test
public void setEnabled_connectionPolicyForbidden_setConnectionPolicyForbidden_returnTrue() {
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
when(mService.getConnectionPolicy(mBluetoothDevice))
.thenReturn(CONNECTION_POLICY_FORBIDDEN);
when(mService.setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN))
.thenReturn(true);
assertThat(mProfile.setEnabled(mBluetoothDevice, false)).isTrue();
}
@Test
public void getConnectedDevices_returnCorrectList() {
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
int[] connectedStates = new int[] {
BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_CONNECTING,
BluetoothProfile.STATE_DISCONNECTING};
List<BluetoothDevice> connectedList = Arrays.asList(
mBluetoothDevice,
mBluetoothDevice,
mBluetoothDevice);
when(mService.getDevicesMatchingConnectionStates(connectedStates))
.thenReturn(connectedList);
assertThat(mProfile.getConnectedDevices().size()).isEqualTo(connectedList.size());
}
@Test
public void getConnectableDevices_returnCorrectList() {
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
int[] connectableStates = new int[] {
BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_CONNECTING,
BluetoothProfile.STATE_DISCONNECTING};
List<BluetoothDevice> connectableList = Arrays.asList(
mBluetoothDevice,
mBluetoothDevice,
mBluetoothDevice,
mBluetoothDevice);
when(mService.getDevicesMatchingConnectionStates(connectableStates))
.thenReturn(connectableList);
assertThat(mProfile.getConnectableDevices().size()).isEqualTo(connectableList.size());
}
/**
* Verify registerCallback() call is correctly delegated to {@link BluetoothHapClient} service.
*/
@Test
public void registerCallback_verifyIsCalled() {
final Executor executor = (command -> new Thread(command).start());
final BluetoothHapClient.Callback callback = new BluetoothHapClient.Callback() {
@Override
public void onPresetSelected(@NonNull BluetoothDevice device, int presetIndex,
int reason) {
}
@Override
public void onPresetSelectionFailed(@NonNull BluetoothDevice device, int reason) {
}
@Override
public void onPresetSelectionForGroupFailed(int hapGroupId, int reason) {
}
@Override
public void onPresetInfoChanged(@NonNull BluetoothDevice device,
@NonNull List<BluetoothHapPresetInfo> presetInfoList, int reason) {
}
@Override
public void onSetPresetNameFailed(@NonNull BluetoothDevice device, int reason) {
}
@Override
public void onSetPresetNameForGroupFailed(int hapGroupId, int reason) {
}
};
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
mProfile.registerCallback(executor, callback);
verify(mService).registerCallback(executor, callback);
}
/**
* Verify unregisterCallback() call is correctly delegated to {@link BluetoothHapClient}
* service.
*/
@Test
public void unregisterCallback_verifyIsCalled() {
final BluetoothHapClient.Callback callback = new BluetoothHapClient.Callback() {
@Override
public void onPresetSelected(@NonNull BluetoothDevice device, int presetIndex,
int reason) {
}
@Override
public void onPresetSelectionFailed(@NonNull BluetoothDevice device, int reason) {
}
@Override
public void onPresetSelectionForGroupFailed(int hapGroupId, int reason) {
}
@Override
public void onPresetInfoChanged(@NonNull BluetoothDevice device,
@NonNull List<BluetoothHapPresetInfo> presetInfoList, int reason) {
}
@Override
public void onSetPresetNameFailed(@NonNull BluetoothDevice device, int reason) {
}
@Override
public void onSetPresetNameForGroupFailed(int hapGroupId, int reason) {
}
};
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
mProfile.unregisterCallback(callback);
verify(mService).unregisterCallback(callback);
}
/**
* Verify getHapGroup() call is correctly delegated to {@link BluetoothHapClient} service
* and return correct value.
*/
@Test
public void getHapGroup_verifyIsCalledAndReturnCorrectValue() {
when(mService.getHapGroup(mBluetoothDevice)).thenReturn(TEST_GROUP_ID);
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
final int groupId = mProfile.getHapGroup(mBluetoothDevice);
verify(mService).getHapGroup(mBluetoothDevice);
assertThat(groupId).isEqualTo(TEST_GROUP_ID);
}
/**
* Verify getActivePresetIndex() call is correctly delegated to {@link BluetoothHapClient}
* service and return correct index.
*/
@Test
public void getActivePresetIndex_verifyIsCalledAndReturnCorrectValue() {
when(mService.getActivePresetIndex(mBluetoothDevice)).thenReturn(TEST_PRESET_INDEX);
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
final int activeIndex = mProfile.getActivePresetIndex(mBluetoothDevice);
verify(mService).getActivePresetIndex(mBluetoothDevice);
assertThat(activeIndex).isEqualTo(TEST_PRESET_INDEX);
}
/**
* Verify getActivePresetInfo() call is correctly delegated to {@link BluetoothHapClient}
* service and return correct object.
*/
@Test
public void getActivePresetInfo_verifyIsCalledAndReturnCorrectObject() {
when(mService.getActivePresetInfo(mBluetoothDevice)).thenReturn(mPresetInfo);
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
final BluetoothHapPresetInfo activeInfo = mProfile.getActivePresetInfo(mBluetoothDevice);
verify(mService).getActivePresetInfo(mBluetoothDevice);
assertThat(activeInfo).isEqualTo(mPresetInfo);
}
/**
* Verify selectPreset() call is correctly delegated to {@link BluetoothHapClient} service.
*/
@Test
public void selectPreset_verifyIsCalled() {
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
mProfile.selectPreset(mBluetoothDevice, TEST_PRESET_INDEX);
verify(mService).selectPreset(mBluetoothDevice, TEST_PRESET_INDEX);
}
/**
* Verify selectPresetForGroup() call is correctly delegated to {@link BluetoothHapClient}
* service.
*/
@Test
public void selectPresetForGroup_verifyIsCalled() {
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
mProfile.selectPresetForGroup(TEST_GROUP_ID, TEST_PRESET_INDEX);
verify(mService).selectPresetForGroup(TEST_GROUP_ID, TEST_PRESET_INDEX);
}
/**
* Verify switchToNextPreset() call is correctly delegated to {@link BluetoothHapClient}
* service.
*/
@Test
public void switchToNextPreset_verifyIsCalled() {
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
mProfile.switchToNextPreset(mBluetoothDevice);
verify(mService).switchToNextPreset(mBluetoothDevice);
}
/**
* Verify switchToNextPresetForGroup() call is correctly delegated to {@link BluetoothHapClient}
* service.
*/
@Test
public void switchToNextPresetForGroup_verifyIsCalled() {
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
mProfile.switchToNextPresetForGroup(TEST_GROUP_ID);
verify(mService).switchToNextPresetForGroup(TEST_GROUP_ID);
}
/**
* Verify switchToPreviousPreset() call is correctly delegated to {@link BluetoothHapClient}
* service.
*/
@Test
public void switchToPreviousPreset_verifyIsCalled() {
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
mProfile.switchToPreviousPreset(mBluetoothDevice);
verify(mService).switchToPreviousPreset(mBluetoothDevice);
}
/**
* Verify switchToPreviousPresetForGroup() call is correctly delegated to
* {@link BluetoothHapClient} service.
*/
@Test
public void switchToPreviousPresetForGroup_verifyIsCalled() {
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
mProfile.switchToPreviousPresetForGroup(TEST_GROUP_ID);
verify(mService).switchToPreviousPresetForGroup(TEST_GROUP_ID);
}
/**
* Verify getPresetInfo() call is correctly delegated to {@link BluetoothHapClient} service and
* return correct object.
*/
@Test
public void getPresetInfo_verifyIsCalledAndReturnCorrectObject() {
when(mService.getPresetInfo(mBluetoothDevice, TEST_PRESET_INDEX)).thenReturn(mPresetInfo);
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
final BluetoothHapPresetInfo info = mProfile.getPresetInfo(mBluetoothDevice,
TEST_PRESET_INDEX);
verify(mService).getPresetInfo(mBluetoothDevice, TEST_PRESET_INDEX);
assertThat(info).isEqualTo(mPresetInfo);
}
/**
* Verify getAllPresetInfo() call is correctly delegated to {@link BluetoothHapClient} service
* and return correct list.
*/
@Test
public void getAllPresetInfo_verifyIsCalledAndReturnCorrectList() {
final List<BluetoothHapPresetInfo> testList = Arrays.asList(mPresetInfo, mPresetInfo);
when(mService.getAllPresetInfo(mBluetoothDevice)).thenReturn(testList);
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
final List<BluetoothHapPresetInfo> infoList = mProfile.getAllPresetInfo(mBluetoothDevice);
verify(mService).getAllPresetInfo(mBluetoothDevice);
assertThat(infoList.size()).isEqualTo(testList.size());
}
/**
* Verify setPresetName() call is correctly delegated to {@link BluetoothHapClient} service.
*/
@Test
public void setPresetName_verifyIsCalled() {
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
mProfile.setPresetName(mBluetoothDevice, TEST_PRESET_INDEX, TEST_DEVICE_NAME);
verify(mService).setPresetName(mBluetoothDevice, TEST_PRESET_INDEX, TEST_DEVICE_NAME);
}
/**
* Verify setPresetNameForGroup() call is correctly delegated to {@link BluetoothHapClient}
* service.
*/
@Test
public void setPresetNameForGroup_verifyIsCalled() {
mServiceListener.onServiceConnected(BluetoothProfile.HAP_CLIENT, mService);
mProfile.setPresetNameForGroup(TEST_GROUP_ID, TEST_PRESET_INDEX, TEST_DEVICE_NAME);
verify(mService).setPresetNameForGroup(TEST_GROUP_ID, TEST_PRESET_INDEX, TEST_DEVICE_NAME);
}
}

View File

@@ -0,0 +1,75 @@
package com.android.settingslib.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class HeadsetProfileTest {
@Mock
private CachedBluetoothDeviceManager mDeviceManager;
@Mock
private LocalBluetoothProfileManager mProfileManager;
@Mock
private BluetoothHeadset mService;
@Mock
private CachedBluetoothDevice mCachedBluetoothDevice;
@Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private HeadsetProfile mProfile;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
Context context = spy(RuntimeEnvironment.application);
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mProfile = new HeadsetProfile(context, mDeviceManager, mProfileManager);
mServiceListener = mShadowBluetoothAdapter.getServiceListener();
mServiceListener.onServiceConnected(BluetoothProfile.HEADSET, mService);
}
@Test
public void testHeadsetProfile_shouldReturnAudioState() {
when(mService.getAudioState(mBluetoothDevice)).
thenReturn(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
assertThat(mProfile.getAudioState(mBluetoothDevice)).
isEqualTo(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
when(mService.getAudioState(mBluetoothDevice)).
thenReturn(BluetoothHeadset.STATE_AUDIO_CONNECTED);
assertThat(mProfile.getAudioState(mBluetoothDevice)).
isEqualTo(BluetoothHeadset.STATE_AUDIO_CONNECTED);
}
@Test
public void setActiveDevice_returnTrue() {
assertThat(mProfile.setActiveDevice(null)).isTrue();
assertThat(mProfile.setActiveDevice(mBluetoothDevice)).isTrue();
}
}

View File

@@ -0,0 +1,184 @@
/*
* 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.settingslib.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.atLeastOnce;
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.Context;
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.audiopolicy.AudioProductStrategy;
import androidx.test.core.app.ApplicationProvider;
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 org.robolectric.RobolectricTestRunner;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/** Tests for {@link HearingAidAudioRoutingHelper}. */
@RunWith(RobolectricTestRunner.class)
public class HearingAidAudioRoutingHelperTest {
@Rule
public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Spy
private final Context mContext = ApplicationProvider.getApplicationContext();
private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1";
private static final String NOT_EXPECT_DEVICE_ADDRESS = "11:B2:B2:B2:B2:B2";
@Mock
private AudioProductStrategy mAudioStrategy;
@Spy
private AudioManager mAudioManager = mContext.getSystemService(AudioManager.class);
@Mock
private AudioDeviceInfo mAudioDeviceInfo;
@Mock
private CachedBluetoothDevice mCachedBluetoothDevice;
@Mock
private CachedBluetoothDevice mSubCachedBluetoothDevice;
private AudioDeviceAttributes mHearingDeviceAttribute;
private HearingAidAudioRoutingHelper mHelper;
@Before
public void setUp() {
doReturn(mAudioManager).when(mContext).getSystemService(AudioManager.class);
when(mAudioDeviceInfo.getType()).thenReturn(AudioDeviceInfo.TYPE_HEARING_AID);
when(mAudioDeviceInfo.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
when(mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)).thenReturn(
new AudioDeviceInfo[]{mAudioDeviceInfo});
doReturn(Collections.emptyList()).when(mAudioManager).getPreferredDevicesForStrategy(
any(AudioProductStrategy.class));
when(mAudioStrategy.getAudioAttributesForLegacyStreamType(
AudioManager.STREAM_MUSIC))
.thenReturn((new AudioAttributes.Builder()).build());
mHearingDeviceAttribute = new AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT,
AudioDeviceInfo.TYPE_HEARING_AID,
TEST_DEVICE_ADDRESS);
mHelper = spy(new HearingAidAudioRoutingHelper(mContext));
doReturn(List.of(mAudioStrategy)).when(mHelper).getAudioProductStrategies();
}
@Test
public void setPreferredDeviceRoutingStrategies_hadValueThenValueAuto_callRemoveStrategy() {
when(mAudioManager.getPreferredDeviceForStrategy(mAudioStrategy)).thenReturn(
mHearingDeviceAttribute);
mHelper.setPreferredDeviceRoutingStrategies(List.of(mAudioStrategy),
mHearingDeviceAttribute,
HearingAidAudioRoutingConstants.RoutingValue.AUTO);
verify(mAudioManager, atLeastOnce()).removePreferredDeviceForStrategy(mAudioStrategy);
}
@Test
public void setPreferredDeviceRoutingStrategies_NoValueThenValueAuto_notCallRemoveStrategy() {
when(mAudioManager.getPreferredDeviceForStrategy(mAudioStrategy)).thenReturn(null);
mHelper.setPreferredDeviceRoutingStrategies(List.of(mAudioStrategy),
mHearingDeviceAttribute,
HearingAidAudioRoutingConstants.RoutingValue.AUTO);
verify(mAudioManager, never()).removePreferredDeviceForStrategy(mAudioStrategy);
}
@Test
public void setPreferredDeviceRoutingStrategies_valueHearingDevice_callSetStrategy() {
mHelper.setPreferredDeviceRoutingStrategies(List.of(mAudioStrategy),
mHearingDeviceAttribute,
HearingAidAudioRoutingConstants.RoutingValue.HEARING_DEVICE);
verify(mAudioManager, atLeastOnce()).setPreferredDeviceForStrategy(mAudioStrategy,
mHearingDeviceAttribute);
}
@Test
public void setPreferredDeviceRoutingStrategies_valueDeviceSpeaker_callSetStrategy() {
final AudioDeviceAttributes speakerDevice = new AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, "");
mHelper.setPreferredDeviceRoutingStrategies(List.of(mAudioStrategy),
mHearingDeviceAttribute,
HearingAidAudioRoutingConstants.RoutingValue.DEVICE_SPEAKER);
verify(mAudioManager, atLeastOnce()).setPreferredDeviceForStrategy(mAudioStrategy,
speakerDevice);
}
@Test
public void getMatchedHearingDeviceAttributes_mainHearingDevice_equalAddress() {
when(mCachedBluetoothDevice.isHearingAidDevice()).thenReturn(true);
when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
final String targetAddress = mHelper.getMatchedHearingDeviceAttributes(
mCachedBluetoothDevice).getAddress();
assertThat(targetAddress).isEqualTo(mHearingDeviceAttribute.getAddress());
}
@Test
public void getMatchedHearingDeviceAttributes_subHearingDevice_equalAddress() {
when(mCachedBluetoothDevice.isHearingAidDevice()).thenReturn(true);
when(mCachedBluetoothDevice.getAddress()).thenReturn(NOT_EXPECT_DEVICE_ADDRESS);
when(mCachedBluetoothDevice.getSubDevice()).thenReturn(mSubCachedBluetoothDevice);
when(mSubCachedBluetoothDevice.isHearingAidDevice()).thenReturn(true);
when(mSubCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
final String targetAddress = mHelper.getMatchedHearingDeviceAttributes(
mCachedBluetoothDevice).getAddress();
assertThat(targetAddress).isEqualTo(mHearingDeviceAttribute.getAddress());
}
@Test
public void getMatchedHearingDeviceAttributes_memberHearingDevice_equalAddress() {
when(mSubCachedBluetoothDevice.isHearingAidDevice()).thenReturn(true);
when(mSubCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
final Set<CachedBluetoothDevice> memberDevices = new HashSet<CachedBluetoothDevice>();
memberDevices.add(mSubCachedBluetoothDevice);
when(mCachedBluetoothDevice.isHearingAidDevice()).thenReturn(true);
when(mCachedBluetoothDevice.getAddress()).thenReturn(NOT_EXPECT_DEVICE_ADDRESS);
when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(memberDevices);
final String targetAddress = mHelper.getMatchedHearingDeviceAttributes(
mCachedBluetoothDevice).getAddress();
assertThat(targetAddress).isEqualTo(mHearingDeviceAttribute.getAddress());
}
}

View File

@@ -0,0 +1,732 @@
/*
* 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.
*/
package com.android.settingslib.bluetooth;
import static android.bluetooth.BluetoothHearingAid.HI_SYNC_ID_INVALID;
import static android.bluetooth.BluetoothLeAudio.AUDIO_LOCATION_FRONT_LEFT;
import static android.bluetooth.BluetoothLeAudio.AUDIO_LOCATION_INVALID;
import static com.android.settingslib.bluetooth.HapClientProfile.HearingAidType.TYPE_BINAURAL;
import static com.android.settingslib.bluetooth.HapClientProfile.HearingAidType.TYPE_INVALID;
import static com.android.settingslib.bluetooth.HearingAidProfile.DeviceMode.MODE_BINAURAL;
import static com.android.settingslib.bluetooth.HearingAidProfile.DeviceSide.SIDE_RIGHT;
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.anyList;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.atLeastOnce;
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.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.le.ScanFilter;
import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.audiopolicy.AudioProductStrategy;
import android.os.Parcel;
import android.util.FeatureFlagUtils;
import androidx.test.core.app.ApplicationProvider;
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 org.robolectric.RobolectricTestRunner;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class HearingAidDeviceManagerTest {
@Rule
public MockitoRule mMockitoRule = MockitoJUnit.rule();
private final static long HISYNCID1 = 10;
private final static long HISYNCID2 = 11;
private final static String DEVICE_NAME_1 = "TestName_1";
private final static String DEVICE_NAME_2 = "TestName_2";
private final static String DEVICE_ALIAS_1 = "TestAlias_1";
private final static String DEVICE_ALIAS_2 = "TestAlias_2";
private final static String DEVICE_ADDRESS_1 = "AA:BB:CC:DD:EE:11";
private final static String DEVICE_ADDRESS_2 = "AA:BB:CC:DD:EE:22";
private final BluetoothClass DEVICE_CLASS =
createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE);
private final Context mContext = ApplicationProvider.getApplicationContext();
private CachedBluetoothDevice mCachedDevice1;
private CachedBluetoothDevice mCachedDevice2;
private CachedBluetoothDeviceManager mCachedDeviceManager;
private HearingAidDeviceManager mHearingAidDeviceManager;
private AudioDeviceAttributes mHearingDeviceAttribute;
@Spy
private HearingAidAudioRoutingHelper mHelper = new HearingAidAudioRoutingHelper(mContext);
@Mock
private LocalBluetoothProfileManager mLocalProfileManager;
@Mock
private LocalBluetoothManager mLocalBluetoothManager;
@Mock
private BluetoothEventManager mBluetoothEventManager;
@Mock
private HearingAidProfile mHearingAidProfile;
@Mock
private LeAudioProfile mLeAudioProfile;
@Mock
private HapClientProfile mHapClientProfile;
@Mock
private AudioProductStrategy mAudioStrategy;
@Mock
private BluetoothDevice mDevice1;
@Mock
private BluetoothDevice mDevice2;
private BluetoothClass createBtClass(int deviceClass) {
Parcel p = Parcel.obtain();
p.writeInt(deviceClass);
p.setDataPosition(0); // reset position of parcel before passing to constructor
BluetoothClass bluetoothClass = BluetoothClass.CREATOR.createFromParcel(p);
p.recycle();
return bluetoothClass;
}
@Before
public void setUp() {
FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_AUDIO_ROUTING, true);
when(mDevice1.getAddress()).thenReturn(DEVICE_ADDRESS_1);
when(mDevice2.getAddress()).thenReturn(DEVICE_ADDRESS_2);
when(mDevice1.getName()).thenReturn(DEVICE_NAME_1);
when(mDevice2.getName()).thenReturn(DEVICE_NAME_2);
when(mDevice1.getAlias()).thenReturn(DEVICE_ALIAS_1);
when(mDevice2.getAlias()).thenReturn(DEVICE_ALIAS_2);
when(mDevice1.getBluetoothClass()).thenReturn(DEVICE_CLASS);
when(mDevice2.getBluetoothClass()).thenReturn(DEVICE_CLASS);
when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalProfileManager);
when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
when(mLocalProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
when(mLocalProfileManager.getHapClientProfile()).thenReturn(mHapClientProfile);
when(mAudioStrategy.getAudioAttributesForLegacyStreamType(
AudioManager.STREAM_MUSIC))
.thenReturn((new AudioAttributes.Builder()).build());
doReturn(List.of(mAudioStrategy)).when(mHelper).getSupportedStrategies(any(int[].class));
mHearingDeviceAttribute = new AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT,
AudioDeviceInfo.TYPE_HEARING_AID,
DEVICE_ADDRESS_1);
mCachedDeviceManager = new CachedBluetoothDeviceManager(mContext, mLocalBluetoothManager);
mHearingAidDeviceManager = spy(new HearingAidDeviceManager(mContext, mLocalBluetoothManager,
mCachedDeviceManager.mCachedDevices, mHelper));
mCachedDevice1 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1));
mCachedDevice2 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2));
}
/**
* Test initHearingAidDeviceIfNeeded
*
* Conditions:
* 1) ASHA hearing aid
* 2) Valid HiSyncId
* Result:
* Set hearing aid info to the device.
*/
@Test
public void initHearingAidDeviceIfNeeded_asha_validHiSyncId_setHearingAidInfo() {
when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHearingAidProfile));
when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(HISYNCID1);
when(mHearingAidProfile.getDeviceMode(mDevice1)).thenReturn(MODE_BINAURAL);
when(mHearingAidProfile.getDeviceSide(mDevice1)).thenReturn(SIDE_RIGHT);
assertThat(mCachedDevice1.getHiSyncId()).isNotEqualTo(HISYNCID1);
mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1, null);
assertThat(mCachedDevice1.getHiSyncId()).isEqualTo(HISYNCID1);
assertThat(mCachedDevice1.getDeviceSide()).isEqualTo(HearingAidInfo.DeviceSide.SIDE_RIGHT);
assertThat(mCachedDevice1.getDeviceMode()).isEqualTo(
HearingAidInfo.DeviceMode.MODE_BINAURAL);
}
/**
* Test initHearingAidDeviceIfNeeded
*
* Conditions:
* 1) ASHA hearing aid
* 2) Invalid HiSyncId
* Result:
* Do not set hearing aid info to the device.
*/
@Test
public void initHearingAidDeviceIfNeeded_asha_invalidHiSyncId_notToSetHearingAidInfo() {
when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHearingAidProfile));
when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(HI_SYNC_ID_INVALID);
mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1, null);
verify(mCachedDevice1, never()).setHearingAidInfo(any(HearingAidInfo.class));
}
/**
* Test initHearingAidDeviceIfNeeded
*
* Conditions:
* 1) ASHA hearing aid
* 2) Invalid HiSyncId
* 3) ASHA uuid scan filter
* Result:
* Set an empty hearing aid info to the device.
*/
@Test
public void initHearingAidDeviceIfNeeded_asha_scanFilterNotNull_setEmptyHearingAidInfo() {
when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHearingAidProfile));
when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(HI_SYNC_ID_INVALID);
final ScanFilter scanFilter = new ScanFilter.Builder()
.setServiceData(BluetoothUuid.HEARING_AID, new byte[]{0}).build();
mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1, List.of(scanFilter));
verify(mCachedDevice1).setHearingAidInfo(new HearingAidInfo.Builder().build());
}
/**
* Test initHearingAidDeviceIfNeeded
*
* Conditions:
* 1) Asha hearing aid
* 2) Invalid HiSyncId
* 3) Random scan filter
* Result:
* Do not set hearing aid info to the device.
*/
@Test
public void initHearingAidDeviceIfNeeded_asha_randomScanFilter_notToSetHearingAidInfo() {
when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHearingAidProfile));
when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(HI_SYNC_ID_INVALID);
final ScanFilter scanFilter = new ScanFilter.Builder().build();
mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1, List.of(scanFilter));
verify(mCachedDevice1, never()).setHearingAidInfo(any(HearingAidInfo.class));
}
/**
* Test initHearingAidDeviceIfNeeded
*
* Conditions:
* 1) LeAudio hearing aid
* 2) Valid audio location and device type
* Result:
* Set hearing aid info to the device.
*/
@Test
public void initHearingAidDeviceIfNeeded_leAudio_validInfo_setHearingAidInfo() {
when(mCachedDevice1.getProfiles()).thenReturn(List.of(mLeAudioProfile, mHapClientProfile));
when(mLeAudioProfile.getAudioLocation(mDevice1)).thenReturn(AUDIO_LOCATION_FRONT_LEFT);
when(mHapClientProfile.getHearingAidType(mDevice1)).thenReturn(TYPE_BINAURAL);
mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1, null);
verify(mCachedDevice1).setHearingAidInfo(any(HearingAidInfo.class));
assertThat(mCachedDevice1.getDeviceSide()).isEqualTo(HearingAidInfo.DeviceSide.SIDE_LEFT);
assertThat(mCachedDevice1.getDeviceMode()).isEqualTo(
HearingAidInfo.DeviceMode.MODE_BINAURAL);
}
/**
* Test initHearingAidDeviceIfNeeded
*
* Conditions:
* 1) LeAudio hearing aid
* 2) Invalid audio location and device type
* Result:
* Do not set hearing aid info to the device.
*/
@Test
public void initHearingAidDeviceIfNeeded_leAudio_invalidInfo_notToSetHearingAidInfo() {
when(mCachedDevice1.getProfiles()).thenReturn(List.of(mLeAudioProfile, mHapClientProfile));
when(mLeAudioProfile.getAudioLocation(mDevice1)).thenReturn(AUDIO_LOCATION_INVALID);
when(mHapClientProfile.getHearingAidType(mDevice1)).thenReturn(TYPE_INVALID);
mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1, null);
verify(mCachedDevice1, never()).setHearingAidInfo(any(HearingAidInfo.class));
}
/**
* Test setSubDeviceIfNeeded, a device with same HiSyncId will be set as sub device
*/
@Test
public void setSubDeviceIfNeeded_sameHiSyncId_setSubDevice() {
mCachedDevice1.setHearingAidInfo(getLeftAshaHearingAidInfo(HISYNCID1));
mCachedDevice2.setHearingAidInfo(getRightAshaHearingAidInfo(HISYNCID1));
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mHearingAidDeviceManager.setSubDeviceIfNeeded(mCachedDevice2);
assertThat(mCachedDevice1.getSubDevice()).isEqualTo(mCachedDevice2);
}
/**
* Test setSubDeviceIfNeeded, a device with different HiSyncId will not be set as sub device
*/
@Test
public void setSubDeviceIfNeeded_differentHiSyncId_notSetSubDevice() {
mCachedDevice1.setHearingAidInfo(getLeftAshaHearingAidInfo(HISYNCID1));
mCachedDevice2.setHearingAidInfo(getRightAshaHearingAidInfo(HISYNCID2));
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mHearingAidDeviceManager.setSubDeviceIfNeeded(mCachedDevice2);
assertThat(mCachedDevice1.getSubDevice()).isNull();
}
/**
* Test updateHearingAidsDevices
*
* Conditions:
* 1) Two ASHA hearing aids with the same HiSyncId
* 2) First paired devices is connected
* 3) Second paired device is disconnected
* Result:
* First paired device would be set as main device and second paired device will be set
* as sub device and removed from CachedDevices list.
*/
@Test
public void updateHearingAidsDevices_asha_firstPairedDevicesConnected_verifySubDevice() {
when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHearingAidProfile));
when(mCachedDevice2.getProfiles()).thenReturn(List.of(mHearingAidProfile));
when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(HISYNCID1);
when(mHearingAidProfile.getHiSyncId(mDevice2)).thenReturn(HISYNCID1);
when(mCachedDevice1.isConnected()).thenReturn(true);
when(mCachedDevice2.isConnected()).thenReturn(false);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice2);
assertThat(mCachedDeviceManager.mCachedDevices.contains(mCachedDevice2)).isTrue();
assertThat(mCachedDevice1.getSubDevice()).isNull();
mHearingAidDeviceManager.updateHearingAidsDevices();
assertThat(mCachedDeviceManager.mCachedDevices.contains(mCachedDevice2)).isFalse();
assertThat(mCachedDevice1.getSubDevice()).isEqualTo(mCachedDevice2);
}
/**
* Test updateHearingAidsDevices
*
* Conditions:
* 1) Two ASHA hearing aids with the same HiSyncId
* 2) First paired devices is disconnected
* 3) Second paired device is connected
* Result:
* Second paired device would be set as main device and first paired device will be set
* as sub device and removed from CachedDevices list.
*/
@Test
public void updateHearingAidsDevices_asha_secondPairedDeviceConnected_verifySubDevice() {
when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHearingAidProfile));
when(mCachedDevice2.getProfiles()).thenReturn(List.of(mHearingAidProfile));
when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(HISYNCID1);
when(mHearingAidProfile.getHiSyncId(mDevice2)).thenReturn(HISYNCID1);
when(mCachedDevice1.isConnected()).thenReturn(false);
when(mCachedDevice2.isConnected()).thenReturn(true);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice2);
assertThat(mCachedDeviceManager.mCachedDevices.contains(mCachedDevice1)).isTrue();
assertThat(mCachedDevice2.getSubDevice()).isNull();
mHearingAidDeviceManager.updateHearingAidsDevices();
assertThat(mCachedDeviceManager.mCachedDevices.contains(mCachedDevice1)).isFalse();
assertThat(mCachedDevice2.getSubDevice()).isEqualTo(mCachedDevice1);
}
/**
* Test updateHearingAidsDevices
*
* Conditions:
* 1) Two ASHA hearing aids with the same HiSyncId
* 2) First paired devices is connected
* 3) Second paired device is connected
* Result:
* First paired device would be set as main device and second paired device will be set
* as sub device and removed from CachedDevices list.
*/
@Test
public void updateHearingAidsDevices_asha_bothConnected_verifySubDevice() {
when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHearingAidProfile));
when(mCachedDevice2.getProfiles()).thenReturn(List.of(mHearingAidProfile));
when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(HISYNCID1);
when(mHearingAidProfile.getHiSyncId(mDevice2)).thenReturn(HISYNCID1);
when(mCachedDevice1.isConnected()).thenReturn(true);
when(mCachedDevice2.isConnected()).thenReturn(true);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice2);
assertThat(mCachedDeviceManager.mCachedDevices.contains(mCachedDevice2)).isTrue();
assertThat(mCachedDevice1.getSubDevice()).isNull();
mHearingAidDeviceManager.updateHearingAidsDevices();
assertThat(mCachedDeviceManager.mCachedDevices.contains(mCachedDevice2)).isFalse();
assertThat(mCachedDevice1.getSubDevice()).isEqualTo(mCachedDevice2);
}
/**
* Test updateHearingAidsDevices
*
* Conditions:
* 1) Two ASHA hearing aids with the same HiSyncId
* Result:
* Dispatch device removed callback
*/
@Test
public void updateHearingAidsDevices_asha_dispatchDeviceRemovedCallback() {
when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHearingAidProfile));
when(mCachedDevice2.getProfiles()).thenReturn(List.of(mHearingAidProfile));
when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(HISYNCID1);
when(mHearingAidProfile.getHiSyncId(mDevice2)).thenReturn(HISYNCID1);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice2);
mHearingAidDeviceManager.updateHearingAidsDevices();
verify(mBluetoothEventManager).dispatchDeviceRemoved(mCachedDevice1);
}
/**
* Test updateHearingAidsDevices
*
* Conditions:
* 1) Two ASHA hearing aids with invalid HiSyncId
* Result:
* Do nothing
*/
@Test
public void updateHearingAidsDevices_asha_invalidHiSyncId_doNothing() {
when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHearingAidProfile));
when(mCachedDevice2.getProfiles()).thenReturn(List.of(mHearingAidProfile));
when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(HI_SYNC_ID_INVALID);
when(mHearingAidProfile.getHiSyncId(mDevice2)).thenReturn(HI_SYNC_ID_INVALID);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice2);
mHearingAidDeviceManager.updateHearingAidsDevices();
verify(mHearingAidDeviceManager, never()).onHiSyncIdChanged(anyLong());
}
/**
* Test updateHearingAidsDevices
*
* Conditions:
* 1) ASHA hearing aids
* 2) Valid HiSync Id
* Result:
* Set hearing aid info to the device.
*/
@Test
public void updateHearingAidsDevices_asha_validHiSyncId_setHearingAidInfo() {
when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHearingAidProfile));
when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(HISYNCID1);
when(mHearingAidProfile.getDeviceMode(mDevice1)).thenReturn(MODE_BINAURAL);
when(mHearingAidProfile.getDeviceSide(mDevice1)).thenReturn(SIDE_RIGHT);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mHearingAidDeviceManager.updateHearingAidsDevices();
assertThat(mCachedDevice1.getHiSyncId()).isEqualTo(HISYNCID1);
assertThat(mCachedDevice1.getDeviceMode()).isEqualTo(
HearingAidInfo.DeviceMode.MODE_BINAURAL);
assertThat(mCachedDevice1.getDeviceSide()).isEqualTo(
HearingAidInfo.DeviceSide.SIDE_RIGHT);
verify(mHearingAidDeviceManager).onHiSyncIdChanged(HISYNCID1);
}
/**
* Test updateHearingAidsDevices
*
* Conditions:
* 1) LeAudio hearing aid
* 2) Valid audio location and device type
* Result:
* Set hearing aid info to the device.
*/
@Test
public void updateHearingAidsDevices_leAudio_validInfo_setHearingAidInfo() {
when(mCachedDevice1.getProfiles()).thenReturn(List.of(mLeAudioProfile, mHapClientProfile));
when(mLeAudioProfile.getAudioLocation(mDevice1)).thenReturn(AUDIO_LOCATION_FRONT_LEFT);
when(mHapClientProfile.getHearingAidType(mDevice1)).thenReturn(TYPE_BINAURAL);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mHearingAidDeviceManager.updateHearingAidsDevices();
verify(mCachedDevice1).setHearingAidInfo(any(HearingAidInfo.class));
assertThat(mCachedDevice1.getDeviceSide()).isEqualTo(HearingAidInfo.DeviceSide.SIDE_LEFT);
assertThat(mCachedDevice1.getDeviceMode()).isEqualTo(
HearingAidInfo.DeviceMode.MODE_BINAURAL);
}
/**
* Test updateHearingAidsDevices
*
* Conditions:
* 1) LeAudio hearing aid
* 2) Invalid audio location and device type
* Result:
* Do not set hearing aid info to the device.
*/
@Test
public void updateHearingAidsDevices_leAudio_invalidInfo_notToSetHearingAidInfo() {
when(mCachedDevice1.getProfiles()).thenReturn(List.of(mLeAudioProfile, mHapClientProfile));
when(mLeAudioProfile.getAudioLocation(mDevice1)).thenReturn(AUDIO_LOCATION_INVALID);
when(mHapClientProfile.getHearingAidType(mDevice1)).thenReturn(TYPE_INVALID);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mHearingAidDeviceManager.updateHearingAidsDevices();
verify(mCachedDevice1, never()).setHearingAidInfo(any(HearingAidInfo.class));
}
/**
* Test onProfileConnectionStateChangedIfProcessed.
* When first hearing aid device is connected, to process it same as other generic devices.
* No need to process it.
*/
@Test
public void onProfileConnectionStateChanged_connected_singleDevice_returnFalse() {
when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(HISYNCID1);
assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
mCachedDevice1, BluetoothProfile.STATE_CONNECTED)).isFalse();
}
/**
* Test onProfileConnectionStateChangedIfProcessed.
* When a new hearing aid device is connected, to set it as sub device by onHiSyncIdChanged().
* And, to verify new device is not in CachedDevices list.
*/
@Test
public void onProfileConnectionStateChanged_connected_newDevice_verifySubDevice() {
when(mCachedDevice1.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice2.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice1.isConnected()).thenReturn(true);
when(mCachedDevice2.isConnected()).thenReturn(true);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice2);
assertThat(mCachedDeviceManager.mCachedDevices.contains(mCachedDevice2)).isTrue();
assertThat(mCachedDevice1.getSubDevice()).isNull();
mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(mCachedDevice1,
BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDeviceManager.mCachedDevices.contains(mCachedDevice2)).isFalse();
assertThat(mCachedDevice1.getSubDevice()).isEqualTo(mCachedDevice2);
verify(mHearingAidDeviceManager).onHiSyncIdChanged(anyLong());
}
/**
* Test onProfileConnectionStateChangedIfProcessed.
* When sub device is disconnected, do nothing and return False for main device connected event
*/
@Test
public void
onProfileConnectionStateChanged_connected_mainDevice_subDeviceDisconnected_returnFalse() {
when(mCachedDevice1.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice2.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice2.isConnected()).thenReturn(false);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDevice1.setSubDevice(mCachedDevice2);
assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
mCachedDevice1, BluetoothProfile.STATE_CONNECTED)).isFalse();
verify(mHearingAidDeviceManager).onHiSyncIdChanged(anyLong());
}
/**
* Test onProfileConnectionStateChangedIfProcessed.
* When main device is connected, do main device refresh() for sub device connected event
*/
@Test
public void
onProfileConnectionStateChanged_connected_subDevice_mainDeviceConnected_verifyRefresh() {
when(mCachedDevice1.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice2.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice1.isConnected()).thenReturn(true);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDevice1.setSubDevice(mCachedDevice2);
assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
mCachedDevice2, BluetoothProfile.STATE_CONNECTED)).isTrue();
verify(mHearingAidDeviceManager).onHiSyncIdChanged(anyLong());
verify(mCachedDevice1).refresh();
}
/**
* Test onProfileConnectionStateChangedIfProcessed.
* When main device is disconnected, to verify switch() result for sub device connected
* event
*/
@Test
public void onProfileConnectionStateChanged_connected_subDevice_mainDeviceDisconnected_switch()
{
when(mCachedDevice1.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice2.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice1.isConnected()).thenReturn(false);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDevice1.setSubDevice(mCachedDevice2);
assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice1);
assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice2);
assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
mCachedDevice2, BluetoothProfile.STATE_CONNECTED)).isTrue();
assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice2);
assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice1);
verify(mHearingAidDeviceManager).onHiSyncIdChanged(anyLong());
verify(mCachedDevice1).refresh();
}
/**
* Test onProfileConnectionStateChangedIfProcessed.
* When sub device is connected, to verify switch() result for main device disconnected
* event
*/
@Test
public void onProfileConnectionStateChanged_disconnected_mainDevice_subDeviceConnected_switch()
{
mCachedDevice1.setHearingAidInfo(getLeftAshaHearingAidInfo(HISYNCID1));
mCachedDevice2.setHearingAidInfo(getRightAshaHearingAidInfo(HISYNCID1));
when(mCachedDevice2.isConnected()).thenReturn(true);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDevice1.setSubDevice(mCachedDevice2);
assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice1);
assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice2);
assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
mCachedDevice1, BluetoothProfile.STATE_DISCONNECTED)).isTrue();
assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice2);
assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice1);
assertThat(mCachedDevice1.getDeviceSide()).isEqualTo(HearingAidInfo.DeviceSide.SIDE_RIGHT);
assertThat(mCachedDevice2.getDeviceSide()).isEqualTo(HearingAidInfo.DeviceSide.SIDE_LEFT);
verify(mCachedDevice1).refresh();
}
/**
* Test onProfileConnectionStateChangedIfProcessed.
* When sub device is disconnected, do nothing and return False for main device disconnected
* event
*/
@Test
public void
onProfileConnectionStateChanged_disconnected_mainDevice_subDeviceDisconnected_returnFalse() {
when(mCachedDevice1.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice2.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice2.isConnected()).thenReturn(false);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDevice1.setSubDevice(mCachedDevice2);
assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
mCachedDevice1, BluetoothProfile.STATE_DISCONNECTED)).isFalse();
}
/**
* Test onProfileConnectionStateChangedIfProcessed.
* Refresh main device UI for sub device disconnected event
*/
@Test
public void onProfileConnectionStateChanged_disconnected_subDevice_verifyRefresh() {
when(mCachedDevice1.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice2.getHiSyncId()).thenReturn(HISYNCID1);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDevice1.setSubDevice(mCachedDevice2);
assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
mCachedDevice2, BluetoothProfile.STATE_DISCONNECTED)).isTrue();
verify(mCachedDevice1).refresh();
}
@Test
public void onActiveDeviceChanged_connected_callSetStrategies() {
when(mHelper.getMatchedHearingDeviceAttributes(mCachedDevice1)).thenReturn(
mHearingDeviceAttribute);
when(mCachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).thenReturn(true);
doReturn(true).when(mHelper).setPreferredDeviceRoutingStrategies(anyList(),
eq(mHearingDeviceAttribute), anyInt());
mHearingAidDeviceManager.onActiveDeviceChanged(mCachedDevice1);
verify(mHelper, atLeastOnce()).setPreferredDeviceRoutingStrategies(
eq(List.of(mAudioStrategy)), any(AudioDeviceAttributes.class), anyInt());
}
@Test
public void onActiveDeviceChanged_disconnected_callSetStrategiesWithAutoValue() {
when(mHelper.getMatchedHearingDeviceAttributes(mCachedDevice1)).thenReturn(
mHearingDeviceAttribute);
when(mCachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).thenReturn(false);
doReturn(true).when(mHelper).setPreferredDeviceRoutingStrategies(anyList(), any(),
anyInt());
mHearingAidDeviceManager.onActiveDeviceChanged(mCachedDevice1);
verify(mHelper, atLeastOnce()).setPreferredDeviceRoutingStrategies(
eq(List.of(mAudioStrategy)), /* hearingDevice= */ isNull(),
eq(HearingAidAudioRoutingConstants.RoutingValue.AUTO));
}
@Test
public void findMainDevice() {
when(mCachedDevice1.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice2.getHiSyncId()).thenReturn(HISYNCID1);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDevice1.setSubDevice(mCachedDevice2);
assertThat(mHearingAidDeviceManager.findMainDevice(mCachedDevice2)).
isEqualTo(mCachedDevice1);
}
private HearingAidInfo getLeftAshaHearingAidInfo(long hiSyncId) {
return new HearingAidInfo.Builder()
.setAshaDeviceSide(HearingAidInfo.DeviceSide.SIDE_LEFT)
.setHiSyncId(hiSyncId)
.build();
}
private HearingAidInfo getRightAshaHearingAidInfo(long hiSyncId) {
return new HearingAidInfo.Builder()
.setAshaDeviceSide(HearingAidInfo.DeviceSide.SIDE_RIGHT)
.setHiSyncId(hiSyncId)
.build();
}
}

View File

@@ -0,0 +1,93 @@
/*
* 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.settingslib.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class HearingAidProfileTest {
@Mock
private CachedBluetoothDeviceManager mDeviceManager;
@Mock
private LocalBluetoothProfileManager mProfileManager;
@Mock
private BluetoothHearingAid mService;
@Mock
private CachedBluetoothDevice mCachedBluetoothDevice;
@Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private HearingAidProfile mProfile;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
Context context = spy(RuntimeEnvironment.application);
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mProfile = new HearingAidProfile(context, mDeviceManager, mProfileManager);
mServiceListener = mShadowBluetoothAdapter.getServiceListener();
}
@Test
public void setActiveDevice_returnTrue() {
assertThat(mProfile.setActiveDevice(null)).isTrue();
assertThat(mProfile.setActiveDevice(mBluetoothDevice)).isTrue();
}
@Test
public void onServiceConnected_isProfileReady() {
mServiceListener.onServiceConnected(BluetoothProfile.HEARING_AID, mService);
assertThat(mProfile.isProfileReady()).isTrue();
verify(mProfileManager).callServiceConnectedListeners();
}
@Test
public void onServiceDisconnected_profileNotReady() {
mServiceListener.onServiceDisconnected(BluetoothProfile.HEARING_AID);
assertThat(mProfile.isProfileReady()).isFalse();
verify(mProfileManager).callServiceDisconnectedListeners();
}
}

View File

@@ -0,0 +1,196 @@
/*
* 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.settingslib.bluetooth;
import static com.android.settingslib.bluetooth.HearingAidStatsLogUtils.CONNECTED_HISTORY_EXPIRED_DAY;
import static com.android.settingslib.bluetooth.HearingAidStatsLogUtils.PAIRED_HISTORY_EXPIRED_DAY;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import com.android.internal.util.FrameworkStatsLog;
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 org.robolectric.RobolectricTestRunner;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
@RunWith(RobolectricTestRunner.class)
public class HearingAidStatsLogUtilsTest {
private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1";
private static final int TEST_HISTORY_TYPE =
HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_AIDS_CONNECTED;
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
private final Context mContext = ApplicationProvider.getApplicationContext();
@Mock
private CachedBluetoothDevice mCachedBluetoothDevice;
@Test
public void setBondEntryForDevice_addsEntryToDeviceAddressToBondEntryMap() {
when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
HearingAidStatsLogUtils.setBondEntryForDevice(
FrameworkStatsLog.HEARING_AID_INFO_REPORTED__BOND_ENTRY__BLUETOOTH,
mCachedBluetoothDevice);
final HashMap<String, Integer> map =
HearingAidStatsLogUtils.getDeviceAddressToBondEntryMap();
assertThat(map.containsKey(TEST_DEVICE_ADDRESS)).isTrue();
assertThat(map.get(TEST_DEVICE_ADDRESS)).isEqualTo(
FrameworkStatsLog.HEARING_AID_INFO_REPORTED__BOND_ENTRY__BLUETOOTH);
}
@Test
public void logHearingAidInfo_removesEntryFromDeviceAddressToBondEntryMap() {
when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
HearingAidStatsLogUtils.setBondEntryForDevice(
FrameworkStatsLog.HEARING_AID_INFO_REPORTED__BOND_ENTRY__BLUETOOTH,
mCachedBluetoothDevice);
HearingAidStatsLogUtils.logHearingAidInfo(mCachedBluetoothDevice);
final HashMap<String, Integer> map =
HearingAidStatsLogUtils.getDeviceAddressToBondEntryMap();
assertThat(map.containsKey(TEST_DEVICE_ADDRESS)).isFalse();
}
@Test
public void addCurrentTimeToHistory_addNewData() {
final long todayStartOfDay = convertToStartOfDayTime(System.currentTimeMillis());
final long lastData = todayStartOfDay - TimeUnit.DAYS.toMillis(6);
HearingAidStatsLogUtils.addToHistory(mContext, TEST_HISTORY_TYPE, lastData);
HearingAidStatsLogUtils.addCurrentTimeToHistory(mContext, TEST_HISTORY_TYPE);
LinkedList<Long> history = HearingAidStatsLogUtils.getHistory(mContext, TEST_HISTORY_TYPE);
assertThat(history).isNotNull();
assertThat(history.size()).isEqualTo(2);
}
@Test
public void addCurrentTimeToHistory_skipSameDateData() {
final long todayStartOfDay = convertToStartOfDayTime(System.currentTimeMillis());
HearingAidStatsLogUtils.addToHistory(mContext, TEST_HISTORY_TYPE, todayStartOfDay);
HearingAidStatsLogUtils.addCurrentTimeToHistory(mContext, TEST_HISTORY_TYPE);
LinkedList<Long> history = HearingAidStatsLogUtils.getHistory(mContext, TEST_HISTORY_TYPE);
assertThat(history).isNotNull();
assertThat(history.size()).isEqualTo(1);
assertThat(history.getFirst()).isEqualTo(todayStartOfDay);
}
@Test
public void addCurrentTimeToHistory_cleanUpExpiredData() {
final long todayStartOfDay = convertToStartOfDayTime(System.currentTimeMillis());
final long expiredData = todayStartOfDay - TimeUnit.DAYS.toMillis(6) - 1;
HearingAidStatsLogUtils.addToHistory(mContext, TEST_HISTORY_TYPE, expiredData);
HearingAidStatsLogUtils.addCurrentTimeToHistory(mContext, TEST_HISTORY_TYPE);
LinkedList<Long> history = HearingAidStatsLogUtils.getHistory(mContext, TEST_HISTORY_TYPE);
assertThat(history).isNotNull();
assertThat(history.size()).isEqualTo(1);
assertThat(history.getFirst()).isNotEqualTo(expiredData);
}
@Test
public void getUserCategory_hearingAidsUser() {
prepareHearingAidsUserHistory();
assertThat(HearingAidStatsLogUtils.getUserCategory(mContext)).isEqualTo(
HearingAidStatsLogUtils.CATEGORY_HEARING_AIDS);
}
@Test
public void getUserCategory_newHearingAidsUser() {
prepareHearingAidsUserHistory();
prepareNewUserHistory();
assertThat(HearingAidStatsLogUtils.getUserCategory(mContext)).isEqualTo(
HearingAidStatsLogUtils.CATEGORY_NEW_HEARING_AIDS);
}
@Test
public void getUserCategory_hearingDevicesUser() {
prepareHearingDevicesUserHistory();
assertThat(HearingAidStatsLogUtils.getUserCategory(mContext)).isEqualTo(
HearingAidStatsLogUtils.CATEGORY_HEARING_DEVICES);
}
@Test
public void getUserCategory_newHearingDevicesUser() {
prepareHearingDevicesUserHistory();
prepareNewUserHistory();
assertThat(HearingAidStatsLogUtils.getUserCategory(mContext)).isEqualTo(
HearingAidStatsLogUtils.CATEGORY_NEW_HEARING_DEVICES);
}
private long convertToStartOfDayTime(long timestamp) {
ZoneId zoneId = ZoneId.systemDefault();
LocalDate date = Instant.ofEpochMilli(timestamp).atZone(zoneId).toLocalDate();
return date.atStartOfDay(zoneId).toInstant().toEpochMilli();
}
private void prepareHearingAidsUserHistory() {
final long todayStartOfDay = convertToStartOfDayTime(System.currentTimeMillis());
for (int i = CONNECTED_HISTORY_EXPIRED_DAY - 1; i >= 0; i--) {
final long data = todayStartOfDay - TimeUnit.DAYS.toMillis(i);
HearingAidStatsLogUtils.addToHistory(mContext,
HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_AIDS_CONNECTED, data);
}
}
private void prepareHearingDevicesUserHistory() {
final long todayStartOfDay = convertToStartOfDayTime(System.currentTimeMillis());
for (int i = CONNECTED_HISTORY_EXPIRED_DAY - 1; i >= 0; i--) {
final long data = todayStartOfDay - TimeUnit.DAYS.toMillis(i);
HearingAidStatsLogUtils.addToHistory(mContext,
HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_DEVICES_CONNECTED, data);
}
}
private void prepareNewUserHistory() {
final long todayStartOfDay = convertToStartOfDayTime(System.currentTimeMillis());
final long data = todayStartOfDay - TimeUnit.DAYS.toMillis(PAIRED_HISTORY_EXPIRED_DAY - 1);
HearingAidStatsLogUtils.addToHistory(mContext,
HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_AIDS_PAIRED, data);
HearingAidStatsLogUtils.addToHistory(mContext,
HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_DEVICES_PAIRED, data);
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.
*/
package com.android.settingslib.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadsetClient;
import android.bluetooth.BluetoothProfile;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class HfpClientProfileTest {
@Mock
private CachedBluetoothDeviceManager mDeviceManager;
@Mock
private LocalBluetoothProfileManager mProfileManager;
@Mock
private BluetoothHeadsetClient mService;
@Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private HfpClientProfile mProfile;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mProfile = new HfpClientProfile(RuntimeEnvironment.application,
mDeviceManager, mProfileManager);
mServiceListener = mShadowBluetoothAdapter.getServiceListener();
mServiceListener.onServiceConnected(BluetoothProfile.HEADSET_CLIENT, mService);
}
@Test
public void getConnectionStatus_shouldReturnConnectionState() {
when(mService.getConnectionState(mBluetoothDevice)).
thenReturn(BluetoothProfile.STATE_CONNECTED);
assertThat(mProfile.getConnectionStatus(mBluetoothDevice)).
isEqualTo(BluetoothProfile.STATE_CONNECTED);
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.
*/
package com.android.settingslib.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHidDevice;
import android.bluetooth.BluetoothProfile;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class HidDeviceProfileTest {
@Mock
private CachedBluetoothDeviceManager mDeviceManager;
@Mock
private LocalBluetoothProfileManager mProfileManager;
@Mock
private BluetoothHidDevice mService;
@Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private HidDeviceProfile mProfile;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mProfile = new HidDeviceProfile(RuntimeEnvironment.application,
mDeviceManager, mProfileManager);
mServiceListener = mShadowBluetoothAdapter.getServiceListener();
mServiceListener.onServiceConnected(BluetoothProfile.HID_DEVICE, mService);
}
@Test
public void getConnectionStatus_shouldReturnConnectionState() {
when(mService.getConnectionState(mBluetoothDevice)).
thenReturn(BluetoothProfile.STATE_CONNECTED);
assertThat(mProfile.getConnectionStatus(mBluetoothDevice)).
isEqualTo(BluetoothProfile.STATE_CONNECTED);
}
}

View File

@@ -0,0 +1,283 @@
/*
* 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.
*/
package com.android.settingslib.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
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.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothPan;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
import android.content.Context;
import android.content.Intent;
import android.os.ParcelUuid;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class LocalBluetoothProfileManagerTest {
private final static long HISYNCID = 10;
@Mock
private CachedBluetoothDeviceManager mDeviceManager;
@Mock
private BluetoothEventManager mEventManager;
@Mock
private BluetoothDevice mDevice;
@Mock
private CachedBluetoothDevice mCachedBluetoothDevice;
private Context mContext;
private Intent mIntent;
private LocalBluetoothAdapter mLocalBluetoothAdapter;
private LocalBluetoothProfileManager mProfileManager;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
mLocalBluetoothAdapter = LocalBluetoothAdapter.getInstance();
mEventManager = spy(new BluetoothEventManager(mLocalBluetoothAdapter, mDeviceManager,
mContext, /* handler= */ null, /* userHandle= */ null));
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
when(mDeviceManager.findDevice(mDevice)).thenReturn(mCachedBluetoothDevice);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mDevice);
mProfileManager = new LocalBluetoothProfileManager(mContext, mLocalBluetoothAdapter,
mDeviceManager, mEventManager);
}
/**
* Verify HID and HID Device profiles are not null without running updateUuids()
*/
@Test
public void constructor_initiateHidAndHidDeviceProfile() {
mShadowBluetoothAdapter.setSupportedProfiles(generateList(
new int[] {BluetoothProfile.HID_HOST, BluetoothProfile.HID_DEVICE}));
mProfileManager.updateLocalProfiles();
assertThat(mProfileManager.getHidProfile()).isNotNull();
assertThat(mProfileManager.getHidDeviceProfile()).isNotNull();
}
@Test
public void constructor_doNotUpdateProfiles() {
mProfileManager = spy(new LocalBluetoothProfileManager(mContext, mLocalBluetoothAdapter,
mDeviceManager, mEventManager));
verify(mProfileManager, never()).updateLocalProfiles();
}
/**
* Verify updateLocalProfiles() for a local A2DP source adds A2dpProfile
*/
@Test
public void updateLocalProfiles_addA2dpToLocalProfiles() {
mProfileManager.updateLocalProfiles();
assertThat(mProfileManager.getA2dpProfile()).isNull();
assertThat(mProfileManager.getHeadsetProfile()).isNull();
mShadowBluetoothAdapter.setSupportedProfiles(generateList(
new int[] {BluetoothProfile.A2DP}));
mProfileManager.updateLocalProfiles();
assertThat(mProfileManager.getA2dpProfile()).isNotNull();
assertThat(mProfileManager.getHeadsetProfile()).isNull();
}
/**
* Verify updateProfiles() for a remote HID device updates profiles and removedProfiles
*/
@Test
public void updateProfiles_addHidProfileForRemoteDevice() {
mShadowBluetoothAdapter.setSupportedProfiles(generateList(
new int[] {BluetoothProfile.HID_HOST}));
mProfileManager.updateLocalProfiles();
ParcelUuid[] uuids = new ParcelUuid[]{BluetoothUuid.HID};
ParcelUuid[] localUuids = new ParcelUuid[]{};
List<LocalBluetoothProfile> profiles = new ArrayList<>();
List<LocalBluetoothProfile> removedProfiles = new ArrayList<>();
mProfileManager.updateProfiles(uuids, localUuids, profiles, removedProfiles, false,
mDevice);
assertThat(mProfileManager.getHidProfile()).isNotNull();
assertThat(profiles.contains(mProfileManager.getHidProfile())).isTrue();
assertThat(removedProfiles.contains(mProfileManager.getHidProfile())).isFalse();
}
/**
* Verify BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED with uuid intent will dispatch to
* profile connection state changed callback
*/
@Test
public void stateChangedHandler_receiveA2dpConnectionStateChanged_shouldDispatchCallback() {
mShadowBluetoothAdapter.setSupportedProfiles(generateList(
new int[] {BluetoothProfile.A2DP}));
mProfileManager.updateLocalProfiles();
mIntent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
mIntent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTING);
mIntent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED);
mContext.sendBroadcast(mIntent);
verify(mEventManager).dispatchProfileConnectionStateChanged(
mCachedBluetoothDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
}
/**
* Verify BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED with uuid intent will dispatch to
* profile connection state changed callback
*/
@Test
public void stateChangedHandler_receiveHeadsetConnectionStateChanged_shouldDispatchCallback() {
mShadowBluetoothAdapter.setSupportedProfiles(generateList(
new int[] {BluetoothProfile.HEADSET}));
mProfileManager.updateLocalProfiles();
mIntent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
mIntent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTING);
mIntent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED);
mContext.sendBroadcast(mIntent);
verify(mEventManager).dispatchProfileConnectionStateChanged(mCachedBluetoothDevice,
BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEADSET);
}
/**
* Verify BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED with uuid intent will dispatch to
* CachedBluetoothDeviceManager method
*/
@Test
public void stateChangedHandler_receiveHAPConnectionStateChanged_shouldDispatchDeviceManager() {
mShadowBluetoothAdapter.setSupportedProfiles(generateList(
new int[] {BluetoothProfile.HEARING_AID}));
mProfileManager.updateLocalProfiles();
when(mCachedBluetoothDevice.getHiSyncId()).thenReturn(HISYNCID);
mIntent = new Intent(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
mIntent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTING);
mIntent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED);
mContext.sendBroadcast(mIntent);
verify(mDeviceManager).onProfileConnectionStateChangedIfProcessed(mCachedBluetoothDevice,
BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID);
}
/**
* Verify BluetoothPan.ACTION_CONNECTION_STATE_CHANGED intent with uuid will dispatch to
* profile connection state changed callback
*/
@Test
public void stateChangedHandler_receivePanConnectionStateChanged_shouldNotDispatchCallback() {
mShadowBluetoothAdapter.setSupportedProfiles(generateList(
new int[] {BluetoothProfile.PAN}));
mProfileManager.updateLocalProfiles();
mIntent = new Intent(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
mIntent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTING);
mIntent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED);
mContext.sendBroadcast(mIntent);
verify(mEventManager).dispatchProfileConnectionStateChanged(
any(CachedBluetoothDevice.class), anyInt(), anyInt());
}
/**
* Verify BluetoothPan.ACTION_CONNECTION_STATE_CHANGED intent without uuids will not dispatch to
* handler and refresh CachedBluetoothDevice
*/
@Test
public void stateChangedHandler_receivePanConnectionStateChangedWithoutProfile_shouldNotRefresh
() {
mShadowBluetoothAdapter.setSupportedProfiles(null);
mProfileManager.updateLocalProfiles();
mIntent = new Intent(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
mIntent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTING);
mIntent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED);
mContext.sendBroadcast(mIntent);
verify(mCachedBluetoothDevice, never()).refresh();
}
/**
* Verify BluetoothPan.ACTION_CONNECTION_STATE_CHANGED intent with uuids will dispatch to
* handler and refresh CachedBluetoothDevice
*/
@Test
public void stateChangedHandler_receivePanConnectionStateChangedWithProfile_shouldRefresh() {
mShadowBluetoothAdapter.setSupportedProfiles(generateList(
new int[] {BluetoothProfile.PAN}));
mProfileManager.updateLocalProfiles();
mIntent = new Intent(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
mIntent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTING);
mIntent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED);
mContext.sendBroadcast(mIntent);
verify(mCachedBluetoothDevice).refresh();
}
private List<Integer> generateList(int[] profiles) {
if (profiles == null) {
return null;
}
final List<Integer> profileList = new ArrayList<>(profiles.length);
for (int profile : profiles) {
profileList.add(profile);
}
return profileList;
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.
*/
package com.android.settingslib.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothMapClient;
import android.bluetooth.BluetoothProfile;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class MapClientProfileTest {
@Mock
private CachedBluetoothDeviceManager mDeviceManager;
@Mock
private LocalBluetoothProfileManager mProfileManager;
@Mock
private BluetoothMapClient mService;
@Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private MapClientProfile mProfile;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mProfile = new MapClientProfile(RuntimeEnvironment.application,
mDeviceManager, mProfileManager);
mServiceListener = mShadowBluetoothAdapter.getServiceListener();
mServiceListener.onServiceConnected(BluetoothProfile.MAP_CLIENT, mService);
}
@Test
public void getConnectionStatus_shouldReturnConnectionState() {
when(mService.getConnectionState(mBluetoothDevice)).
thenReturn(BluetoothProfile.STATE_CONNECTED);
assertThat(mProfile.getConnectionStatus(mBluetoothDevice)).
isEqualTo(BluetoothProfile.STATE_CONNECTED);
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.
*/
package com.android.settingslib.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothPbapClient;
import android.bluetooth.BluetoothProfile;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowBluetoothAdapter.class)
public class PbapClientProfileTest {
@Mock
private CachedBluetoothDeviceManager mDeviceManager;
@Mock
private LocalBluetoothProfileManager mProfileManager;
@Mock
private BluetoothPbapClient mService;
@Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private PbapClientProfile mProfile;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mProfile = new PbapClientProfile(RuntimeEnvironment.application,
mDeviceManager, mProfileManager);
mServiceListener = mShadowBluetoothAdapter.getServiceListener();
mServiceListener.onServiceConnected(BluetoothProfile.PBAP_CLIENT, mService);
}
@Test
public void getConnectionStatus_shouldReturnConnectionState() {
when(mService.getConnectionState(mBluetoothDevice)).
thenReturn(BluetoothProfile.STATE_CONNECTED);
assertThat(mProfile.getConnectionStatus(mBluetoothDevice)).
isEqualTo(BluetoothProfile.STATE_CONNECTED);
}
}

View File

@@ -0,0 +1,72 @@
/*
* 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.
*/
package com.android.settingslib.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothSap;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class SapProfileTest {
@Mock
private CachedBluetoothDeviceManager mDeviceManager;
@Mock
private LocalBluetoothProfileManager mProfileManager;
@Mock
private BluetoothSap mService;
@Mock
private BluetoothDevice mBluetoothDevice;
private BluetoothProfile.ServiceListener mServiceListener;
private SapProfile mProfile;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mProfile = new SapProfile(RuntimeEnvironment.application, mDeviceManager, mProfileManager);
mServiceListener = mShadowBluetoothAdapter.getServiceListener();
mServiceListener.onServiceConnected(BluetoothProfile.SAP, mService);
}
@Test
public void getConnectionStatus_shouldReturnConnectionState() {
when(mService.getConnectionState(mBluetoothDevice)).
thenReturn(BluetoothProfile.STATE_CONNECTED);
assertThat(mProfile.getConnectionStatus(mBluetoothDevice)).
isEqualTo(BluetoothProfile.STATE_CONNECTED);
}
}

View File

@@ -0,0 +1,251 @@
/*
* 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.settingslib.bluetooth;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothVolumeControl;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
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 org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class VolumeControlProfileTest {
private static final int TEST_VOLUME_OFFSET = 10;
private static final int TEST_VOLUME_VALUE = 10;
@Rule public final MockitoRule mockito = MockitoJUnit.rule();
@Mock private CachedBluetoothDeviceManager mDeviceManager;
@Mock private LocalBluetoothProfileManager mProfileManager;
@Mock private BluetoothDevice mBluetoothDevice;
@Mock private BluetoothVolumeControl mService;
private final Context mContext = ApplicationProvider.getApplicationContext();
private BluetoothProfile.ServiceListener mServiceListener;
private VolumeControlProfile mProfile;
@Before
public void setUp() {
mProfile = new VolumeControlProfile(mContext, mDeviceManager, mProfileManager);
final BluetoothManager bluetoothManager = mContext.getSystemService(BluetoothManager.class);
final ShadowBluetoothAdapter shadowBluetoothAdapter =
Shadow.extract(bluetoothManager.getAdapter());
mServiceListener = shadowBluetoothAdapter.getServiceListener();
}
@Test
public void onServiceConnected_isProfileReady() {
mServiceListener.onServiceConnected(BluetoothProfile.VOLUME_CONTROL, mService);
assertThat(mProfile.isProfileReady()).isTrue();
verify(mProfileManager).callServiceConnectedListeners();
}
@Test
public void onServiceDisconnected_isProfileNotReady() {
mServiceListener.onServiceDisconnected(BluetoothProfile.VOLUME_CONTROL);
assertThat(mProfile.isProfileReady()).isFalse();
verify(mProfileManager).callServiceDisconnectedListeners();
}
@Test
public void getConnectionStatus_returnCorrectConnectionState() {
mServiceListener.onServiceConnected(BluetoothProfile.VOLUME_CONTROL, mService);
when(mService.getConnectionState(mBluetoothDevice))
.thenReturn(BluetoothProfile.STATE_CONNECTED);
assertThat(mProfile.getConnectionStatus(mBluetoothDevice))
.isEqualTo(BluetoothProfile.STATE_CONNECTED);
}
@Test
public void isEnabled_connectionPolicyAllowed_returnTrue() {
mServiceListener.onServiceConnected(BluetoothProfile.VOLUME_CONTROL, mService);
when(mService.getConnectionPolicy(mBluetoothDevice)).thenReturn(CONNECTION_POLICY_ALLOWED);
assertThat(mProfile.isEnabled(mBluetoothDevice)).isTrue();
}
@Test
public void isEnabled_connectionPolicyForbidden_returnFalse() {
mServiceListener.onServiceConnected(BluetoothProfile.VOLUME_CONTROL, mService);
when(mService.getConnectionPolicy(mBluetoothDevice))
.thenReturn(CONNECTION_POLICY_FORBIDDEN);
assertThat(mProfile.isEnabled(mBluetoothDevice)).isFalse();
}
@Test
public void getConnectionPolicy_returnCorrectConnectionPolicy() {
mServiceListener.onServiceConnected(BluetoothProfile.VOLUME_CONTROL, mService);
when(mService.getConnectionPolicy(mBluetoothDevice)).thenReturn(CONNECTION_POLICY_ALLOWED);
assertThat(mProfile.getConnectionPolicy(mBluetoothDevice))
.isEqualTo(CONNECTION_POLICY_ALLOWED);
}
@Test
public void setEnabled_connectionPolicyAllowed_setConnectionPolicyAllowed_returnFalse() {
mServiceListener.onServiceConnected(BluetoothProfile.VOLUME_CONTROL, mService);
when(mService.getConnectionPolicy(mBluetoothDevice)).thenReturn(CONNECTION_POLICY_ALLOWED);
when(mService.setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED))
.thenReturn(true);
assertThat(mProfile.setEnabled(mBluetoothDevice, true)).isFalse();
}
@Test
public void setEnabled_connectionPolicyForbidden_setConnectionPolicyAllowed_returnTrue() {
mServiceListener.onServiceConnected(BluetoothProfile.VOLUME_CONTROL, mService);
when(mService.getConnectionPolicy(mBluetoothDevice))
.thenReturn(CONNECTION_POLICY_FORBIDDEN);
when(mService.setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED))
.thenReturn(true);
assertThat(mProfile.setEnabled(mBluetoothDevice, true)).isTrue();
}
@Test
public void setEnabled_connectionPolicyAllowed_setConnectionPolicyForbidden_returnTrue() {
mServiceListener.onServiceConnected(BluetoothProfile.VOLUME_CONTROL, mService);
when(mService.getConnectionPolicy(mBluetoothDevice)).thenReturn(CONNECTION_POLICY_ALLOWED);
when(mService.setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN))
.thenReturn(true);
assertThat(mProfile.setEnabled(mBluetoothDevice, false)).isTrue();
}
@Test
public void setEnabled_connectionPolicyForbidden_setConnectionPolicyForbidden_returnTrue() {
mServiceListener.onServiceConnected(BluetoothProfile.VOLUME_CONTROL, mService);
when(mService.getConnectionPolicy(mBluetoothDevice))
.thenReturn(CONNECTION_POLICY_FORBIDDEN);
when(mService.setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN))
.thenReturn(true);
assertThat(mProfile.setEnabled(mBluetoothDevice, false)).isTrue();
}
@Test
public void getConnectedDevices_returnCorrectList() {
mServiceListener.onServiceConnected(BluetoothProfile.VOLUME_CONTROL, mService);
int[] connectedStates =
new int[] {
BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_CONNECTING,
BluetoothProfile.STATE_DISCONNECTING
};
List<BluetoothDevice> connectedList =
Arrays.asList(mBluetoothDevice, mBluetoothDevice, mBluetoothDevice);
when(mService.getDevicesMatchingConnectionStates(connectedStates))
.thenReturn(connectedList);
assertThat(mProfile.getConnectedDevices().size()).isEqualTo(connectedList.size());
}
@Test
public void registerCallback_verifyIsCalled() {
mServiceListener.onServiceConnected(BluetoothProfile.VOLUME_CONTROL, mService);
final Executor executor = (command -> new Thread(command).start());
final BluetoothVolumeControl.Callback callback = (device, volumeOffset) -> {};
mProfile.registerCallback(executor, callback);
verify(mService).registerCallback(executor, callback);
}
@Test
public void unregisterCallback_verifyIsCalled() {
final BluetoothVolumeControl.Callback callback = (device, volumeOffset) -> {};
mServiceListener.onServiceConnected(BluetoothProfile.VOLUME_CONTROL, mService);
mProfile.unregisterCallback(callback);
verify(mService).unregisterCallback(callback);
}
@Test
public void setVolumeOffset_verifyIsCalled() {
mServiceListener.onServiceConnected(BluetoothProfile.VOLUME_CONTROL, mService);
mProfile.setVolumeOffset(mBluetoothDevice, TEST_VOLUME_OFFSET);
verify(mService).setVolumeOffset(mBluetoothDevice, TEST_VOLUME_OFFSET);
}
@Test
public void setDeviceVolume_verifyIsCalled() {
mServiceListener.onServiceConnected(BluetoothProfile.VOLUME_CONTROL, mService);
mProfile.setDeviceVolume(mBluetoothDevice, TEST_VOLUME_VALUE, /* isGroupOp= */ true);
verify(mService)
.setDeviceVolume(mBluetoothDevice, TEST_VOLUME_VALUE, /* isGroupOp= */ true);
}
@Test
public void isVolumeOffsetAvailable_verifyIsCalledAndReturnTrue() {
mServiceListener.onServiceConnected(BluetoothProfile.VOLUME_CONTROL, mService);
when(mService.isVolumeOffsetAvailable(mBluetoothDevice)).thenReturn(true);
final boolean available = mProfile.isVolumeOffsetAvailable(mBluetoothDevice);
verify(mService).isVolumeOffsetAvailable(mBluetoothDevice);
assertThat(available).isTrue();
}
@Test
public void isVolumeOffsetAvailable_verifyIsCalledAndReturnFalse() {
mServiceListener.onServiceConnected(BluetoothProfile.VOLUME_CONTROL, mService);
when(mService.isVolumeOffsetAvailable(mBluetoothDevice)).thenReturn(false);
final boolean available = mProfile.isVolumeOffsetAvailable(mBluetoothDevice);
verify(mService).isVolumeOffsetAvailable(mBluetoothDevice);
assertThat(available).isFalse();
}
}

View File

@@ -0,0 +1,90 @@
/*
* 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.settingslib.collapsingtoolbar.widget;
import static com.google.common.truth.Truth.assertThat;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.Nullable;
import com.android.settingslib.collapsingtoolbar.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
/** Tests for {@link CollapsingCoordinatorLayout}. */
@RunWith(RobolectricTestRunner.class)
public class CollapsingCoordinatorLayoutTest {
private static final String TEXT_HELLO_WORLD = "Hello World!";
private static final String TEST_TITLE = "RENO NAKAMURA";
private TestActivity mActivity;
@Before
public void setUp() {
mActivity = Robolectric.buildActivity(TestActivity.class).create().get();
}
@Test
public void onCreate_childViewsNumberShouldBeTwo() {
CollapsingCoordinatorLayout layout = mActivity.getCollapsingCoordinatorLayout();
assertThat(layout.getChildCount()).isEqualTo(2);
}
@Test
public void onCreate_userAddedChildViewsBeMovedToContentFrame() {
CollapsingCoordinatorLayout layout = mActivity.getCollapsingCoordinatorLayout();
View contentFrameView = layout.findViewById(R.id.content_frame);
TextView textView = contentFrameView.findViewById(com.android.settingslib.robotests.R.id.text_hello_world);
assertThat(textView).isNotNull();
assertThat(textView.getText().toString()).isEqualTo(TEXT_HELLO_WORLD);
}
@Test
public void initSettingsStyleToolBar_assignedTitle() {
CollapsingCoordinatorLayout layout = mActivity.getCollapsingCoordinatorLayout();
layout.initSettingsStyleToolBar(mActivity, TEST_TITLE);
assertThat(layout.getCollapsingToolbarLayout().getTitle().toString()).isEqualTo(TEST_TITLE);
}
public static class TestActivity extends Activity {
private CollapsingCoordinatorLayout mCollapsingCoordinatorLayout;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(android.R.style.Theme_Light_NoTitleBar);
setContentView(com.android.settingslib.robotests.R.layout.collapsing_test_layout);
mCollapsingCoordinatorLayout = findViewById(com.android.settingslib.robotests.R.id.id_collapsing_test);
}
public CollapsingCoordinatorLayout getCollapsingCoordinatorLayout() {
return mCollapsingCoordinatorLayout;
}
}
}

View File

@@ -0,0 +1,93 @@
/*
* 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.settingslib.connectivity;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Handler;
import androidx.test.core.app.ApplicationProvider;
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 org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class ConnectivitySubsystemsRecoveryManagerTest {
@Rule
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Spy
Context mContext = ApplicationProvider.getApplicationContext();
@Spy
Handler mMainHandler = ApplicationProvider.getApplicationContext().getMainThreadHandler();
@Mock
PackageManager mPackageManager;
ConnectivitySubsystemsRecoveryManager mConnectivitySubsystemsRecoveryManager;
@Before
public void setUp() {
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)).thenReturn(true);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true);
}
@Test
public void startTrackingWifiRestart_hasNoWifiFeature_shouldNotCrash() {
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)).thenReturn(false);
mConnectivitySubsystemsRecoveryManager =
new ConnectivitySubsystemsRecoveryManager(mContext, mMainHandler);
mConnectivitySubsystemsRecoveryManager.startTrackingWifiRestart();
}
@Test
public void stopTrackingWifiRestart_hasNoWifiFeature_shouldNotCrash() {
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)).thenReturn(false);
mConnectivitySubsystemsRecoveryManager =
new ConnectivitySubsystemsRecoveryManager(mContext, mMainHandler);
mConnectivitySubsystemsRecoveryManager.stopTrackingWifiRestart();
}
@Test
public void startTrackingTelephonyRestart_hasNoTelephonyFeature_shouldNotCrash() {
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(false);
mConnectivitySubsystemsRecoveryManager =
new ConnectivitySubsystemsRecoveryManager(mContext, mMainHandler);
mConnectivitySubsystemsRecoveryManager.startTrackingTelephonyRestart();
}
@Test
public void stopTrackingTelephonyRestart_hasNoTelephonyFeature_shouldNotCrash() {
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(false);
mConnectivitySubsystemsRecoveryManager =
new ConnectivitySubsystemsRecoveryManager(mContext, mMainHandler);
mConnectivitySubsystemsRecoveryManager.stopTrackingTelephonyRestart();
}
}

View File

@@ -0,0 +1,137 @@
/*
* 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
*/
package com.android.settingslib.core;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.content.Context;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class AbstractPreferenceControllerTest {
private static final String KEY_PREF = "test_pref";
@Mock
private PreferenceScreen mScreen;
private Context mContext;
private Preference mPreference;
private TestPrefController mTestPrefController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mPreference = new Preference(mContext);
mPreference.setKey(KEY_PREF);
when(mScreen.findPreference(KEY_PREF)).thenReturn(mPreference);
mTestPrefController = new TestPrefController(mContext, KEY_PREF);
}
@Test
public void displayPref_ifAvailable() {
mTestPrefController.isAvailable = true;
mTestPrefController.displayPreference(mScreen);
assertThat(mPreference.isVisible()).isTrue();
}
@Test
public void displayPref_noKey_shouldDoNothing() {
mTestPrefController.isAvailable = true;
mTestPrefController.displayPreference(mScreen);
assertThat(mPreference.isVisible()).isTrue();
}
@Test
public void setVisible_prefIsVisible_shouldSetToVisible() {
mTestPrefController.setVisible(mScreen, KEY_PREF, true /* visible */);
assertThat(mPreference.isVisible()).isTrue();
}
@Test
public void setVisible_prefNotVisible_shouldSetToInvisible() {
mTestPrefController.setVisible(mScreen, KEY_PREF, false /* visible */);
assertThat(mPreference.isVisible()).isFalse();
}
@Test
public void doNotDisplayPref_ifNotAvailable() {
mTestPrefController.isAvailable = false;
mTestPrefController.displayPreference(mScreen);
assertThat(mPreference.isVisible()).isFalse();
}
@Test
public void updateState_hasSummary_shouldSetSummary() {
mTestPrefController.updateState(mPreference);
assertThat(mPreference.getSummary()).isEqualTo(TestPrefController.TEST_SUMMARY);
}
private static class TestPrefController extends AbstractPreferenceController {
private static final CharSequence TEST_SUMMARY = "Test";
public boolean isAvailable;
private final String mPrefKey;
TestPrefController(Context context, String key) {
super(context);
mPrefKey = key;
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
return false;
}
@Override
public boolean isAvailable() {
return isAvailable;
}
@Override
public String getPreferenceKey() {
return mPrefKey;
}
@Override
public CharSequence getSummary() {
return TEST_SUMMARY;
}
}
}

View File

@@ -0,0 +1,228 @@
/*
* 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.
*/
package com.android.settingslib.core.instrumentation;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import androidx.preference.Preference;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.LooperMode;
import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class MetricsFeatureProviderTest {
@Mock
private LogWriter mLogWriter;
private Context mContext;
private MetricsFeatureProvider mProvider;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mProvider = new MetricsFeatureProvider();
List<LogWriter> writers = new ArrayList<>();
writers.add(mLogWriter);
ReflectionHelpers.setField(mProvider, "mLoggerWriters", writers);
}
@Test
public void logClickedPreference_preferenceEmpty_shouldNotLog() {
final boolean loggable = mProvider.logClickedPreference(null /* preference */,
MetricsEvent.SETTINGS_GESTURES);
assertThat(loggable).isFalse();
verifyNoMoreInteractions(mLogWriter);
}
@Test
public void logClickedPreference_preferenceHasKey_shouldLog() {
final String key = "abc";
final Preference preference = new Preference(mContext);
preference.setKey(key);
final boolean loggable = mProvider.logClickedPreference(preference,
MetricsEvent.SETTINGS_GESTURES);
assertThat(loggable).isTrue();
verify(mLogWriter).clicked(MetricsEvent.SETTINGS_GESTURES, key);
}
@Test
public void logClickedPreference_preferenceHasIntent_shouldLog() {
final Preference preference = new Preference(mContext);
final Intent intent = new Intent(Intent.ACTION_ASSIST);
preference.setIntent(intent);
final boolean loggable = mProvider.logClickedPreference(preference,
MetricsEvent.SETTINGS_GESTURES);
assertThat(loggable).isTrue();
verify(mLogWriter).clicked(MetricsEvent.SETTINGS_GESTURES, Intent.ACTION_ASSIST);
}
@Test
public void logClickedPreference_preferenceHasFragment_shouldLog() {
final Preference preference = new Preference(mContext);
final String fragment = "com.android.settings.tts.TextToSpeechSettings";
preference.setFragment(fragment);
final boolean loggable = mProvider.logClickedPreference(preference,
MetricsEvent.SETTINGS_GESTURES);
assertThat(loggable).isTrue();
verify(mLogWriter).clicked(MetricsEvent.SETTINGS_GESTURES, fragment);
}
@Test
public void logStartedIntent_intentEmpty_shouldNotLog() {
final boolean loggable = mProvider.logStartedIntent(null /* intent */,
MetricsEvent.SETTINGS_GESTURES);
assertThat(loggable).isFalse();
verifyNoMoreInteractions(mLogWriter);
}
@Test
public void logStartedIntent_intentHasNoComponent_shouldLog() {
final Intent intent = new Intent(Intent.ACTION_ASSIST);
final boolean loggable = mProvider.logStartedIntent(intent, MetricsEvent.SETTINGS_GESTURES);
assertThat(loggable).isTrue();
verify(mLogWriter).clicked(MetricsEvent.SETTINGS_GESTURES, Intent.ACTION_ASSIST);
}
@Test
public void logStartedIntent_intentIsExternal_shouldLog() {
final Intent intent = new Intent().setComponent(new ComponentName("pkg", "cls"));
final boolean loggable = mProvider.logStartedIntent(intent, MetricsEvent.SETTINGS_GESTURES);
assertThat(loggable).isTrue();
verify(mLogWriter).clicked(MetricsEvent.SETTINGS_GESTURES, "pkg/cls");
}
@Test
public void logStartedIntentWithProfile_isPersonalProfile_shouldTagPersonal() {
final Intent intent = new Intent().setComponent(new ComponentName("pkg", "cls"));
final boolean loggable = mProvider.logStartedIntentWithProfile(intent,
MetricsEvent.SETTINGS_GESTURES, false);
assertThat(loggable).isTrue();
verify(mLogWriter).clicked(MetricsEvent.SETTINGS_GESTURES, "pkg/cls/personal");
}
@Test
public void logStartedIntentWithProfile_isWorkProfile_shouldTagWork() {
final Intent intent = new Intent().setComponent(new ComponentName("pkg", "cls"));
final boolean loggable = mProvider.logStartedIntentWithProfile(intent,
MetricsEvent.SETTINGS_GESTURES, true);
assertThat(loggable).isTrue();
verify(mLogWriter).clicked(MetricsEvent.SETTINGS_GESTURES, "pkg/cls/work");
}
@Test
public void getAttribution_noActivity_shouldReturnUnknown() {
assertThat(mProvider.getAttribution(null /* activity */))
.isEqualTo(SettingsEnums.PAGE_UNKNOWN);
}
@Test
@LooperMode(LooperMode.Mode.PAUSED)
public void getAttribution_notSet_shouldReturnUnknown() {
final Activity activity = Robolectric.setupActivity(Activity.class);
assertThat(mProvider.getAttribution(activity))
.isEqualTo(SettingsEnums.PAGE_UNKNOWN);
}
@Test
public void getAttribution_set_shouldReturnAttribution() {
final Intent intent = new Intent()
.putExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, 100);
final Activity activity = Robolectric.buildActivity(Activity.class, intent).create().get();
assertThat(mProvider.getAttribution(activity)).isEqualTo(100);
}
@Test
public void logSettingsTileClick_hasKey_shouldLog() {
final String key = "abc";
final boolean loggable = mProvider.logSettingsTileClick(key,
MetricsEvent.SETTINGS_GESTURES);
assertThat(loggable).isTrue();
verify(mLogWriter).clicked(MetricsEvent.SETTINGS_GESTURES, key);
}
@Test
public void logSettingsTileClick_keyEmpty_shouldNotLog() {
final String key = "";
boolean loggable = mProvider.logSettingsTileClick(key,
MetricsEvent.SETTINGS_GESTURES);
assertThat(loggable).isFalse();
verifyNoMoreInteractions(mLogWriter);
}
@Test
public void logSettingsTileClickWithProfile_isPersonalProfile_shouldTagPersonal() {
final String key = "abc";
final boolean loggable = mProvider.logSettingsTileClickWithProfile(key,
MetricsEvent.SETTINGS_GESTURES, false);
assertThat(loggable).isTrue();
verify(mLogWriter).clicked(MetricsEvent.SETTINGS_GESTURES, "abc/personal");
}
@Test
public void logSettingsTileClickWithProfile_isWorkProfile_shouldTagWork() {
final String key = "abc";
final boolean loggable = mProvider.logSettingsTileClickWithProfile(key,
MetricsEvent.SETTINGS_GESTURES, true);
assertThat(loggable).isTrue();
verify(mLogWriter).clicked(MetricsEvent.SETTINGS_GESTURES, "abc/work");
}
}

View File

@@ -0,0 +1,146 @@
/*
* 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.settingslib.core.instrumentation;
import static com.android.internal.jank.Cuj.CUJ_SETTINGS_TOGGLE;
import static com.android.settingslib.core.instrumentation.SettingsJankMonitor.MONITORED_ANIMATION_DURATION_MS;
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.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.preference.PreferenceGroupAdapter;
import androidx.preference.SwitchPreference;
import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.jank.Cuj.CujType;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.settingslib.testutils.OverpoweredReflectionHelper;
import com.android.settingslib.testutils.shadow.ShadowInteractionJankMonitor;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowInteractionJankMonitor.class, SettingsJankMonitorTest.ShadowBuilder.class})
public class SettingsJankMonitorTest {
private static final String TEST_KEY = "key";
@Rule
public MockitoRule mocks = MockitoJUnit.rule();
@Mock
private View mView;
@Mock
private RecyclerView mRecyclerView;
@Mock
private PreferenceGroupAdapter mPreferenceGroupAdapter;
@Mock
private SwitchPreference mSwitchPreference;
@Mock
private ScheduledExecutorService mScheduledExecutorService;
@Before
public void setUp() {
ShadowInteractionJankMonitor.reset();
when(ShadowInteractionJankMonitor.MOCK_INSTANCE.begin(any())).thenReturn(true);
OverpoweredReflectionHelper
.setStaticField(SettingsJankMonitor.class,
"scheduledExecutorService",
mScheduledExecutorService);
}
@Test
public void detectToggleJank() {
SettingsJankMonitor.detectToggleJank(TEST_KEY, mView);
verifyToggleJankMonitored();
}
@Test
public void detectSwitchPreferenceClickJank() {
int adapterPosition = 7;
when(mRecyclerView.getAdapter()).thenReturn(mPreferenceGroupAdapter);
when(mPreferenceGroupAdapter.getPreferenceAdapterPosition(mSwitchPreference))
.thenReturn(adapterPosition);
when(mRecyclerView.findViewHolderForAdapterPosition(adapterPosition))
.thenReturn(new RecyclerView.ViewHolder(mView) {
});
when(mSwitchPreference.getKey()).thenReturn(TEST_KEY);
SettingsJankMonitor.detectSwitchPreferenceClickJank(mRecyclerView, mSwitchPreference);
verifyToggleJankMonitored();
}
private void verifyToggleJankMonitored() {
verify(ShadowInteractionJankMonitor.MOCK_INSTANCE).begin(ShadowBuilder.sBuilder);
assertThat(ShadowBuilder.sView).isSameInstanceAs(mView);
ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
verify(mScheduledExecutorService).schedule(runnableCaptor.capture(),
eq(MONITORED_ANIMATION_DURATION_MS), eq(TimeUnit.MILLISECONDS));
runnableCaptor.getValue().run();
verify(ShadowInteractionJankMonitor.MOCK_INSTANCE).end(CUJ_SETTINGS_TOGGLE);
}
@Implements(InteractionJankMonitor.Configuration.Builder.class)
static class ShadowBuilder {
private static InteractionJankMonitor.Configuration.Builder sBuilder;
private static View sView;
@Resetter
public static void reset() {
sBuilder = null;
sView = null;
}
@Implementation
public static InteractionJankMonitor.Configuration.Builder withView(
@CujType int cuj, @NonNull View view) {
assertThat(cuj).isEqualTo(CUJ_SETTINGS_TOGGLE);
sView = view;
sBuilder = mock(InteractionJankMonitor.Configuration.Builder.class);
when(sBuilder.setTag(TEST_KEY)).thenReturn(sBuilder);
return sBuilder;
}
}
}

View File

@@ -0,0 +1,175 @@
/*
* 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.
*/
package com.android.settingslib.core.instrumentation;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.SharedPreferences;
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;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class SharedPreferenceLoggerTest {
private static final String TEST_TAG = "tag";
private static final String TEST_KEY = "key";
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@Mock
private MetricsFeatureProvider mMetricsFeature;
private SharedPreferencesLogger mSharedPrefLogger;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
mSharedPrefLogger = new SharedPreferencesLogger(mContext, TEST_TAG, mMetricsFeature);
}
@Test
public void putInt_shouldNotLogInitialPut() {
final SharedPreferences.Editor editor = mSharedPrefLogger.edit();
editor.putInt(TEST_KEY, 1);
editor.putInt(TEST_KEY, 1);
editor.putInt(TEST_KEY, 1);
editor.putInt(TEST_KEY, 2);
editor.putInt(TEST_KEY, 2);
editor.putInt(TEST_KEY, 2);
editor.putInt(TEST_KEY, 2);
verify(mMetricsFeature, times(6)).changed(eq(SettingsEnums.PAGE_UNKNOWN),
eq(TEST_KEY),
anyInt());
}
@Test
public void putBoolean_shouldNotLogInitialPut() {
final SharedPreferences.Editor editor = mSharedPrefLogger.edit();
editor.putBoolean(TEST_KEY, true);
editor.putBoolean(TEST_KEY, true);
editor.putBoolean(TEST_KEY, false);
editor.putBoolean(TEST_KEY, false);
editor.putBoolean(TEST_KEY, false);
verify(mMetricsFeature).changed(SettingsEnums.PAGE_UNKNOWN,
TEST_KEY,
1);
verify(mMetricsFeature, times(3)).changed(SettingsEnums.PAGE_UNKNOWN,
TEST_KEY,
0);
}
@Test
public void putLong_shouldNotLogInitialPut() {
final SharedPreferences.Editor editor = mSharedPrefLogger.edit();
editor.putLong(TEST_KEY, 1);
editor.putLong(TEST_KEY, 1);
editor.putLong(TEST_KEY, 1);
editor.putLong(TEST_KEY, 1);
editor.putLong(TEST_KEY, 2);
verify(mMetricsFeature, times(4)).changed(eq(SettingsEnums.PAGE_UNKNOWN),
eq(TEST_KEY),
anyInt());
}
@Test
public void putLong_biggerThanIntMax_shouldLogIntMax() {
final SharedPreferences.Editor editor = mSharedPrefLogger.edit();
final long veryBigNumber = 500L + Integer.MAX_VALUE;
editor.putLong(TEST_KEY, 1);
editor.putLong(TEST_KEY, veryBigNumber);
verify(mMetricsFeature).changed(SettingsEnums.PAGE_UNKNOWN,
TEST_KEY,
Integer.MAX_VALUE);
}
@Test
public void putLong_smallerThanIntMin_shouldLogIntMin() {
final SharedPreferences.Editor editor = mSharedPrefLogger.edit();
final long veryNegativeNumber = -500L + Integer.MIN_VALUE;
editor.putLong(TEST_KEY, 1);
editor.putLong(TEST_KEY, veryNegativeNumber);
verify(mMetricsFeature).changed(SettingsEnums.PAGE_UNKNOWN,
TEST_KEY, Integer.MIN_VALUE);
}
@Test
public void putFloat_shouldNotLogInitialPut() {
final SharedPreferences.Editor editor = mSharedPrefLogger.edit();
editor.putFloat(TEST_KEY, 1);
editor.putFloat(TEST_KEY, 1);
editor.putFloat(TEST_KEY, 1);
editor.putFloat(TEST_KEY, 1);
editor.putFloat(TEST_KEY, 2);
verify(mMetricsFeature, times(4)).changed(eq(SettingsEnums.PAGE_UNKNOWN),
eq(TEST_KEY),
anyInt());
}
@Test
public void logPackage_shouldUseLogPackageApi() {
mSharedPrefLogger.logPackageName("key", "com.android.settings");
verify(mMetricsFeature).action(SettingsEnums.PAGE_UNKNOWN,
ACTION_SETTINGS_PREFERENCE_CHANGE,
SettingsEnums.PAGE_UNKNOWN,
"key:com.android.settings",
0);
}
@Test
public void putString_shouldNotLogInitialPut() {
mSharedPrefLogger.logValue(TEST_KEY, "1");
mSharedPrefLogger.logValue(TEST_KEY, "2");
mSharedPrefLogger.logValue(TEST_KEY, "62");
mSharedPrefLogger.logValue(TEST_KEY, "0");
verify(mMetricsFeature, times(3)).changed(eq(SettingsEnums.PAGE_UNKNOWN),
eq(TEST_KEY),
anyInt());
}
@Test
public void putString_shouldNotLogAnyNonIntegers() {
mSharedPrefLogger.logValue(TEST_KEY, "string");
mSharedPrefLogger.logValue(TEST_KEY, "not an int");
mSharedPrefLogger.logValue(TEST_KEY, "1.234f");
mSharedPrefLogger.logValue(TEST_KEY, "4.2");
mSharedPrefLogger.logValue(TEST_KEY, "3.0");
verify(mMetricsFeature, times(0)).changed(eq(SettingsEnums.PAGE_UNKNOWN),
eq(TEST_KEY),
anyInt());
}
}

View File

@@ -0,0 +1,149 @@
/*
* 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.
*/
package com.android.settingslib.core.instrumentation;
import static com.android.settingslib.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
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.Context;
import android.content.Intent;
import android.os.Bundle;
import androidx.fragment.app.FragmentActivity;
import com.android.internal.logging.nano.MetricsProto;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.android.controller.ActivityController;
@RunWith(RobolectricTestRunner.class)
public class VisibilityLoggerMixinTest {
@Mock
private MetricsFeatureProvider mMetricsFeature;
private VisibilityLoggerMixin mMixin;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
mMixin = new VisibilityLoggerMixin(TestInstrumentable.TEST_METRIC, mMetricsFeature);
}
@Test
public void shouldLogVisibleOnResume() {
mMixin.onResume();
verify(mMetricsFeature, times(1))
.visible(nullable(Context.class), eq(MetricsProto.MetricsEvent.VIEW_UNKNOWN),
eq(TestInstrumentable.TEST_METRIC), anyInt());
}
@Test
public void shouldLogVisibleWithSource() {
final Intent sourceIntent = new Intent()
.putExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY,
MetricsProto.MetricsEvent.SETTINGS_GESTURES);
final Activity activity = mock(Activity.class);
when(activity.getIntent()).thenReturn(sourceIntent);
mMixin.setSourceMetricsCategory(activity);
mMixin.onResume();
verify(mMetricsFeature, times(1))
.visible(nullable(Context.class), eq(MetricsProto.MetricsEvent.SETTINGS_GESTURES),
eq(TestInstrumentable.TEST_METRIC), anyInt());
}
@Test
public void shouldLogHideOnPause() {
mMixin.onPause();
verify(mMetricsFeature, times(1))
.hidden(nullable(Context.class), eq(TestInstrumentable.TEST_METRIC), anyInt());
}
@Test
public void shouldNotLogIfMetricsFeatureIsNull() {
mMixin = new VisibilityLoggerMixin(TestInstrumentable.TEST_METRIC, null);
mMixin.onResume();
mMixin.onPause();
verify(mMetricsFeature, never())
.hidden(nullable(Context.class), anyInt(), anyInt());
}
@Test
public void shouldNotLogIfMetricsCategoryIsUnknown() {
mMixin = new VisibilityLoggerMixin(METRICS_CATEGORY_UNKNOWN, mMetricsFeature);
mMixin.onResume();
mMixin.onPause();
verify(mMetricsFeature, never())
.hidden(nullable(Context.class), anyInt(), anyInt());
}
@Test
public void activityShouldBecomeVisibleAndHide() {
ActivityController<TestActivity> ac = Robolectric.buildActivity(TestActivity.class);
TestActivity testActivity = ac.get();
MockitoAnnotations.initMocks(testActivity);
ac.create().start().resume();
verify(testActivity.mMetricsFeatureProvider, times(1)).visible(any(), anyInt(), anyInt(),
anyInt());
ac.pause().stop().destroy();
verify(testActivity.mMetricsFeatureProvider, times(1)).hidden(any(), anyInt(), anyInt());
}
public static class TestActivity extends FragmentActivity {
@Mock
MetricsFeatureProvider mMetricsFeatureProvider;
@Override
public void onCreate(Bundle savedInstanceState) {
VisibilityLoggerMixin mixin = new VisibilityLoggerMixin(
TestInstrumentable.TEST_METRIC, mMetricsFeatureProvider);
getLifecycle().addObserver(mixin);
super.onCreate(savedInstanceState);
}
}
private final class TestInstrumentable implements Instrumentable {
private static final int TEST_METRIC = 12345;
@Override
public int getMetricsCategory() {
return TEST_METRIC;
}
}
}

View File

@@ -0,0 +1,100 @@
/*
* 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.settingslib.core.lifecycle;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin.SECURE_OVERLAY_SETTINGS;
import static com.google.common.truth.Truth.assertThat;
import android.os.Bundle;
import android.provider.Settings;
import android.view.WindowManager;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.annotation.LooperMode;
@RunWith(RobolectricTestRunner.class)
@LooperMode(LooperMode.Mode.PAUSED)
public class HideNonSystemOverlayMixinTest {
private ActivityController<TestActivity> mActivityController;
@Before
public void setUp() {
mActivityController = Robolectric.buildActivity(TestActivity.class);
}
@Test
public void startActivity_shouldHideNonSystemOverlay() {
mActivityController.setup();
TestActivity activity = mActivityController.get();
// Activity start: HIDE_NON_SYSTEM_OVERLAY should be set.
final WindowManager.LayoutParams attrs = activity.getWindow().getAttributes();
assertThat(attrs.privateFlags & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS)
.isNotEqualTo(0);
}
@Test
public void stopActivity_shouldUnhideNonSystemOverlay() {
mActivityController.setup().stop();
TestActivity activity = mActivityController.get();
final WindowManager.LayoutParams attrs = activity.getWindow().getAttributes();
assertThat(attrs.privateFlags & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS)
.isEqualTo(0);
}
@Test
public void isEnabled_isAllowedOverlaySettings_returnFalse() {
mActivityController.setup();
final TestActivity activity = mActivityController.get();
Settings.Secure.putInt(activity.getContentResolver(),
SECURE_OVERLAY_SETTINGS, 1);
assertThat(new HideNonSystemOverlayMixin(activity).isEnabled()).isFalse();
}
@Test
public void isEnabled_isNotAllowedOverlaySettings_returnTrue() {
mActivityController.setup();
TestActivity activity = mActivityController.get();
Settings.Secure.putInt(activity.getContentResolver(),
SECURE_OVERLAY_SETTINGS, 0);
assertThat(new HideNonSystemOverlayMixin(activity).isEnabled()).isTrue();
}
public static class TestActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(androidx.appcompat.R.style.Theme_AppCompat);
getLifecycle().addObserver(new HideNonSystemOverlayMixin(this));
}
}
}

View File

@@ -0,0 +1,278 @@
/*
* 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.
*/
package com.android.settingslib.core.lifecycle;
import static androidx.lifecycle.Lifecycle.Event.ON_START;
import static com.google.common.truth.Truth.assertThat;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.LinearLayout;
import androidx.lifecycle.LifecycleOwner;
import com.android.settingslib.core.lifecycle.events.OnAttach;
import com.android.settingslib.core.lifecycle.events.OnCreateOptionsMenu;
import com.android.settingslib.core.lifecycle.events.OnDestroy;
import com.android.settingslib.core.lifecycle.events.OnOptionsItemSelected;
import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnPrepareOptionsMenu;
import com.android.settingslib.core.lifecycle.events.OnResume;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.shadows.androidx.fragment.FragmentController;
@RunWith(RobolectricTestRunner.class)
public class LifecycleTest {
private LifecycleOwner mLifecycleOwner;
private Lifecycle mLifecycle;
public static class TestDialogFragment extends ObservableDialogFragment {
final TestObserver mFragObserver;
private TestDialogFragment() {
mFragObserver = new TestObserver();
mLifecycle.addObserver(mFragObserver);
}
}
public static class TestFragment extends ObservableFragment {
final TestObserver mFragObserver;
public TestFragment() {
mFragObserver = new TestObserver();
getSettingsLifecycle().addObserver(mFragObserver);
}
}
public static class TestActivity extends ObservableActivity {
final TestObserver mActObserver;
public TestActivity() {
mActObserver = new TestObserver();
getSettingsLifecycle().addObserver(mActObserver);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout view = new LinearLayout(this);
view.setId(1);
setContentView(view);
}
}
public static class TestObserver implements LifecycleObserver, OnAttach, OnStart, OnResume,
OnPause, OnStop, OnDestroy, OnCreateOptionsMenu, OnPrepareOptionsMenu,
OnOptionsItemSelected {
boolean mOnAttachObserved;
boolean mOnStartObserved;
boolean mOnResumeObserved;
boolean mOnPauseObserved;
boolean mOnStopObserved;
boolean mOnDestroyObserved;
boolean mOnCreateOptionsMenuObserved;
boolean mOnPrepareOptionsMenuObserved;
boolean mOnOptionsItemSelectedObserved;
@Override
public void onAttach() {
mOnAttachObserved = true;
}
@Override
public void onStart() {
mOnStartObserved = true;
}
@Override
public void onPause() {
mOnPauseObserved = true;
}
@Override
public void onResume() {
mOnResumeObserved = true;
}
@Override
public void onStop() {
mOnStopObserved = true;
}
@Override
public void onDestroy() {
mOnDestroyObserved = true;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
mOnCreateOptionsMenuObserved = true;
}
@Override
public boolean onOptionsItemSelected(MenuItem menuItem) {
mOnOptionsItemSelectedObserved = true;
return true;
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
mOnPrepareOptionsMenuObserved = true;
}
}
@Before
public void setUp() {
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
}
@Test
@Ignore("b/188888268")
public void runThroughActivityLifecycles_shouldObserveEverything() {
ActivityController<TestActivity> ac = Robolectric.buildActivity(TestActivity.class);
TestActivity activity = ac.setup().get();
assertThat(activity.mActObserver.mOnStartObserved).isTrue();
assertThat(activity.mActObserver.mOnResumeObserved).isTrue();
activity.onCreateOptionsMenu(null);
assertThat(activity.mActObserver.mOnCreateOptionsMenuObserved).isTrue();
activity.onPrepareOptionsMenu(null);
assertThat(activity.mActObserver.mOnPrepareOptionsMenuObserved).isTrue();
activity.onOptionsItemSelected(null);
assertThat(activity.mActObserver.mOnOptionsItemSelectedObserved).isTrue();
ac.pause();
assertThat(activity.mActObserver.mOnPauseObserved).isTrue();
ac.stop();
assertThat(activity.mActObserver.mOnStopObserved).isTrue();
ac.destroy();
assertThat(activity.mActObserver.mOnDestroyObserved).isTrue();
}
@Test
@Ignore("b/188888268")
public void runThroughDialogFragmentLifecycles_shouldObserveEverything() {
final TestDialogFragment fragment = new TestDialogFragment();
FragmentController.setupFragment(fragment);
fragment.onCreateOptionsMenu(null, null);
fragment.onPrepareOptionsMenu(null);
fragment.onOptionsItemSelected(null);
assertThat(fragment.mFragObserver.mOnCreateOptionsMenuObserved).isTrue();
assertThat(fragment.mFragObserver.mOnPrepareOptionsMenuObserved).isTrue();
assertThat(fragment.mFragObserver.mOnOptionsItemSelectedObserved).isTrue();
assertThat(fragment.mFragObserver.mOnAttachObserved).isTrue();
assertThat(fragment.mFragObserver.mOnStartObserved).isTrue();
assertThat(fragment.mFragObserver.mOnResumeObserved).isTrue();
fragment.onPause();
assertThat(fragment.mFragObserver.mOnPauseObserved).isTrue();
fragment.onStop();
assertThat(fragment.mFragObserver.mOnStopObserved).isTrue();
fragment.onDestroy();
assertThat(fragment.mFragObserver.mOnDestroyObserved).isTrue();
}
@Test
@Ignore("b/188888268")
public void runThroughFragmentLifecycles_shouldObserveEverything() {
final TestFragment fragment = new TestFragment();
FragmentController.setupFragment(fragment);
fragment.onCreateOptionsMenu(null, null);
fragment.onPrepareOptionsMenu(null);
fragment.onOptionsItemSelected(null);
assertThat(fragment.mFragObserver.mOnCreateOptionsMenuObserved).isTrue();
assertThat(fragment.mFragObserver.mOnPrepareOptionsMenuObserved).isTrue();
assertThat(fragment.mFragObserver.mOnOptionsItemSelectedObserved).isTrue();
assertThat(fragment.mFragObserver.mOnAttachObserved).isTrue();
assertThat(fragment.mFragObserver.mOnStartObserved).isTrue();
assertThat(fragment.mFragObserver.mOnResumeObserved).isTrue();
fragment.onPause();
assertThat(fragment.mFragObserver.mOnPauseObserved).isTrue();
fragment.onStop();
assertThat(fragment.mFragObserver.mOnStopObserved).isTrue();
fragment.onDestroy();
assertThat(fragment.mFragObserver.mOnDestroyObserved).isTrue();
}
@Test
public void addObserverDuringObserve_shoudNotCrash() {
mLifecycle.addObserver(new OnStartObserver(mLifecycle));
mLifecycle.handleLifecycleEvent(ON_START);
}
private static class OptionItemAccepter implements LifecycleObserver, OnOptionsItemSelected {
private boolean mWasCalled = false;
@Override
public boolean onOptionsItemSelected(MenuItem menuItem) {
mWasCalled = true;
return false;
}
}
@Test
@Ignore("b/188888268")
public void onOptionItemSelectedShortCircuitsIfAnObserverHandlesTheMenuItem() {
final TestFragment fragment = new TestFragment();
FragmentController.setupFragment(fragment);
final OptionItemAccepter accepter = new OptionItemAccepter();
fragment.getLifecycle().addObserver(accepter);
fragment.onCreateOptionsMenu(null, null);
fragment.onPrepareOptionsMenu(null);
fragment.onOptionsItemSelected(null);
assertThat(accepter.mWasCalled).isFalse();
}
private class OnStartObserver implements LifecycleObserver, OnStart {
private final Lifecycle mLifecycle;
private OnStartObserver(Lifecycle lifecycle) {
mLifecycle = lifecycle;
}
@Override
public void onStart() {
mLifecycle.addObserver(new LifecycleObserver() {
});
}
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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.
*/
package com.android.settingslib.development;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class DeveloperOptionsPreferenceControllerTest {
private static final String TEST_KEY = "Test_pref_key";
@Mock
private Preference mPreference;
@Mock
private PreferenceScreen mPreferenceScreen;
private DeveloperOptionsPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mController = new DeveloperOptionsPreferenceControllerTestable();
doReturn(mPreference).when(mPreferenceScreen).findPreference(TEST_KEY);
mController.displayPreference(mPreferenceScreen);
}
@Test
public void onDeveloperOptionsEnabled_shouldEnablePreference() {
mController.onDeveloperOptionsEnabled();
verify(mPreference).setEnabled(true);
}
@Test
public void onDeveloperOptionsDisabled_shouldDisablePreference() {
mController.onDeveloperOptionsDisabled();
verify(mPreference).setEnabled(false);
}
private class DeveloperOptionsPreferenceControllerTestable extends
DeveloperOptionsPreferenceController {
DeveloperOptionsPreferenceControllerTestable() {
super(RuntimeEnvironment.application);
}
@Override
public String getPreferenceKey() {
return TEST_KEY;
}
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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.
*/
package com.android.settingslib.development;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.os.UserManager;
import android.provider.Settings;
import com.android.settingslib.testutils.shadow.ShadowUserManager;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadow.api.Shadow;
@RunWith(RobolectricTestRunner.class)
public class DevelopmentSettingsEnablerTest {
private Context mContext;
private ShadowUserManager mUserManager;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mUserManager = Shadow.extract(mContext.getSystemService(UserManager.class));
mUserManager.setIsAdminUser(true);
}
@Test
public void isEnabled_settingsOn_noRestriction_isAdmin_shouldReturnTrue() {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0);
assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isFalse();
DevelopmentSettingsEnabler.setDevelopmentSettingsEnabled(mContext, true);
assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isTrue();
}
@Test
public void isEnabled_settingsOff_noRestriction_isAdmin_shouldReturnFalse() {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isTrue();
DevelopmentSettingsEnabler.setDevelopmentSettingsEnabled(mContext, false);
assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isFalse();
}
@Test
public void isEnabled_settingsOn_noRestriction_notAdmin_shouldReturnFalse() {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
mUserManager.setIsAdminUser(false);
assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isFalse();
}
}

View File

@@ -0,0 +1,191 @@
/*
* 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.
*/
package com.android.settingslib.development;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Answers.RETURNS_DEEP_STUBS;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
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.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settingslib.RestrictedSwitchPreference;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadows.ShadowApplication;
@RunWith(RobolectricTestRunner.class)
public class EnableAdbPreferenceControllerTest {
private static final ComponentName TEST_COMPONENT_NAME = new ComponentName("test", "test");
@Mock(answer = RETURNS_DEEP_STUBS)
private PreferenceScreen mScreen;
@Mock
private UserManager mUserManager;
@Mock
private PackageManager mPackageManager;
@Mock
private DevicePolicyManager mDevicePolicyManager;
private Context mContext;
private RestrictedSwitchPreference mPreference;
private ConcreteEnableAdbPreferenceController mController;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
ShadowApplication shadowContext = ShadowApplication.getInstance();
shadowContext.setSystemService(Context.USER_SERVICE, mUserManager);
mContext = spy(RuntimeEnvironment.application);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mContext.getSystemService(DevicePolicyManager.class)).thenReturn(mDevicePolicyManager);
doReturn(mContext).when(mContext).createPackageContextAsUser(
any(String.class), anyInt(), any(UserHandle.class));
mController = new ConcreteEnableAdbPreferenceController(mContext);
mPreference = new RestrictedSwitchPreference(mContext);
mPreference.setKey(mController.getPreferenceKey());
when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference);
}
@Test
public void displayPreference_isNotAdmin_shouldRemovePreference() {
when(mUserManager.isAdminUser()).thenReturn(false);
mController.displayPreference(mScreen);
assertThat(mPreference.isVisible()).isFalse();
}
@Test
public void displayPreference_isAdmin_shouldNotRemovePreference() {
when(mUserManager.isAdminUser()).thenReturn(true);
mController.displayPreference(mScreen);
assertThat(mPreference.isVisible()).isTrue();
}
@Test
public void resetPreference_shouldUncheck() {
when(mUserManager.isAdminUser()).thenReturn(true);
mController.displayPreference(mScreen);
mPreference.setChecked(true);
mController.resetPreference();
assertThat(mPreference.isChecked()).isFalse();
}
@Test
public void handlePreferenceTreeClick_shouldUpdateSettings() {
when(mUserManager.isAdminUser()).thenReturn(true);
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.ADB_ENABLED, 1);
mPreference.setChecked(true);
mController.displayPreference(mScreen);
mController.handlePreferenceTreeClick(mPreference);
assertThat(Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ADB_ENABLED, 0)).isEqualTo(0);
}
@Test
public void handlePreferenceTreeClick_isMonkeyUser_shouldBeFalse() {
mController = spy(mController);
doReturn(true).when(mController).isUserAMonkey();
when(mUserManager.isAdminUser()).thenReturn(true);
mController.displayPreference(mScreen);
final boolean handled = mController.handlePreferenceTreeClick(mPreference);
assertThat(handled).isFalse();
}
@Test
public void updateState_settingsOn_shouldCheck() {
when(mUserManager.isAdminUser()).thenReturn(true);
when(mDevicePolicyManager.getProfileOwner()).thenReturn(TEST_COMPONENT_NAME);
when(mDevicePolicyManager.isUsbDataSignalingEnabled(
)).thenReturn(true);
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.ADB_ENABLED, 1);
mPreference.setChecked(false);
mController.displayPreference(mScreen);
mController.updateState(mPreference);
assertThat(mPreference.isChecked()).isTrue();
}
@Test
public void updateState_settingsOff_shouldUncheck() {
when(mUserManager.isAdminUser()).thenReturn(true);
when(mDevicePolicyManager.getProfileOwner()).thenReturn(TEST_COMPONENT_NAME);
when(mDevicePolicyManager.isUsbDataSignalingEnabled(
)).thenReturn(true);
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.ADB_ENABLED, 0);
mPreference.setChecked(true);
mController.displayPreference(mScreen);
mController.updateState(mPreference);
assertThat(mPreference.isChecked()).isFalse();
}
class ConcreteEnableAdbPreferenceController extends AbstractEnableAdbPreferenceController {
private ConcreteEnableAdbPreferenceController(Context context) {
super(context);
}
@Override
public void showConfirmationDialog(Preference preference) {
// Don't show a dialog, just set setting.
writeAdbSetting(true);
}
@Override
public boolean isConfirmationDialogShowing() {
return false;
}
@Override
public void dismissConfirmationDialog() {
}
}
}

View File

@@ -0,0 +1,188 @@
/*
* 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.
*/
package com.android.settingslib.development;
import static com.android.settingslib.development.AbstractLogdSizePreferenceController
.DEFAULT_SNET_TAG;
import static com.android.settingslib.development.AbstractLogdSizePreferenceController
.LOW_RAM_CONFIG_PROPERTY_KEY;
import static com.android.settingslib.development.AbstractLogdSizePreferenceController
.SELECT_LOGD_MINIMUM_SIZE_VALUE;
import static com.android.settingslib.development.AbstractLogdSizePreferenceController
.SELECT_LOGD_OFF_SIZE_MARKER_VALUE;
import static com.android.settingslib.development.AbstractLogdSizePreferenceController
.SELECT_LOGD_SIZE_PROPERTY;
import static com.android.settingslib.development.AbstractLogdSizePreferenceController
.SELECT_LOGD_SNET_TAG_PROPERTY;
import static com.android.settingslib.development.AbstractLogdSizePreferenceController
.SELECT_LOGD_TAG_PROPERTY;
import static com.android.settingslib.development.AbstractLogdSizePreferenceController
.SELECT_LOGD_TAG_SILENCE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.os.SystemProperties;
import androidx.preference.ListPreference;
import androidx.preference.PreferenceScreen;
import com.android.settingslib.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class LogdSizePreferenceControllerTest {
@Mock
private ListPreference mListPreference;
@Mock
private PreferenceScreen mPreferenceScreen;
/**
* List Values
*
* 0: off
* 1: 64k
* 2: 256k
* 3: 1M
* 4: 4M
* 5: 16M
*/
private String[] mListValues;
private String[] mListSummaries;
private Context mContext;
private AbstractLogdSizePreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mController = new AbstractLogdSizePreferenceController(RuntimeEnvironment.application) {
};
mListValues = mContext.getResources().getStringArray(R.array.select_logd_size_values);
mListSummaries = mContext.getResources().getStringArray(R.array.select_logd_size_summaries);
doReturn(mListPreference).when(mPreferenceScreen)
.findPreference(mController.getPreferenceKey());
mController.displayPreference(mPreferenceScreen);
}
@Test
public void testUpdateLogdSizeValues_lowRamEntries() {
SystemProperties.set(LOW_RAM_CONFIG_PROPERTY_KEY, "true");
mController.updateLogdSizeValues();
verify(mListPreference).setEntries(R.array.select_logd_size_lowram_titles);
}
@Test
public void testUpdateLogdSizeValues_silence() {
SystemProperties.set(
AbstractLogdSizePreferenceController.SELECT_LOGD_TAG_PROPERTY,
AbstractLogdSizePreferenceController.SELECT_LOGD_TAG_SILENCE);
SystemProperties.set(
AbstractLogdSizePreferenceController.SELECT_LOGD_SIZE_PROPERTY,
AbstractLogdSizePreferenceController.SELECT_LOGD_DEFAULT_SIZE_VALUE);
mController.updateLogdSizeValues();
verify(mListPreference).setValue(
AbstractLogdSizePreferenceController.SELECT_LOGD_OFF_SIZE_MARKER_VALUE);
}
@Test
public void onPreferenceChange_noTagsSizeValueOff_shouldSetTagAndSnetTagAndSet64KSize() {
mController.onPreferenceChange(mListPreference, SELECT_LOGD_OFF_SIZE_MARKER_VALUE);
final String tag = SystemProperties.get(SELECT_LOGD_TAG_PROPERTY);
final String logSize = SystemProperties.get(SELECT_LOGD_SIZE_PROPERTY);
final String snetTag = SystemProperties.get(SELECT_LOGD_SNET_TAG_PROPERTY);
assertThat(tag).isEqualTo(SELECT_LOGD_TAG_SILENCE);
assertThat(logSize).isEqualTo(SELECT_LOGD_MINIMUM_SIZE_VALUE);
assertThat(snetTag).isEqualTo(DEFAULT_SNET_TAG);
}
@Test
public void onPreferenceChange_noTagsSizeValue64K_shouldNotSetTagAndSet64KSize() {
mController.onPreferenceChange(mListPreference, SELECT_LOGD_MINIMUM_SIZE_VALUE);
final String tag = SystemProperties.get(SELECT_LOGD_TAG_PROPERTY);
final String logSize = SystemProperties.get(SELECT_LOGD_SIZE_PROPERTY);
final String snetTag = SystemProperties.get(SELECT_LOGD_SNET_TAG_PROPERTY);
assertThat(tag).isEmpty();
assertThat(logSize).isEqualTo(SELECT_LOGD_MINIMUM_SIZE_VALUE);
assertThat(snetTag).isEmpty();
}
@Test
public void onPreferenceChange_set1M_shouldUpdateSettingLogSizeTo1M() {
mController.onPreferenceChange(mListPreference, mListValues[3]);
final String logSize = SystemProperties.get(SELECT_LOGD_SIZE_PROPERTY);
assertThat(logSize).isEqualTo(mListValues[3]);
}
@Test
public void onPreferenceChange_noValue_shouldUpdateSettingToEmpty() {
mController.onPreferenceChange(mListPreference, "" /* new value */);
final String logSize = SystemProperties.get(SELECT_LOGD_SIZE_PROPERTY);
assertThat(logSize).isEmpty();
}
@Test
public void updateLogdSizeValues_noValueSet_shouldSetDefaultTo64K() {
SystemProperties.set(SELECT_LOGD_SIZE_PROPERTY, "" /* new value */);
mController.updateLogdSizeValues();
verify(mListPreference).setValue(mListValues[2]);
verify(mListPreference).setSummary(mListSummaries[2]);
}
@Test
public void updateLogdSizeValues_noValueSetLowRamSet_shouldSetDefaultTo64K() {
SystemProperties.set(LOW_RAM_CONFIG_PROPERTY_KEY, Boolean.toString(true));
SystemProperties.set(SELECT_LOGD_SIZE_PROPERTY, "" /* new value */);
mController.updateLogdSizeValues();
verify(mListPreference).setValue(mListValues[1]);
verify(mListPreference).setSummary(mListSummaries[1]);
}
@Test
public void updateLogdSizeValues_64KSet_shouldSet64K() {
SystemProperties.set(SELECT_LOGD_SIZE_PROPERTY, mListValues[1]);
mController.updateLogdSizeValues();
verify(mListPreference).setValue(mListValues[1]);
verify(mListPreference).setSummary(mListSummaries[1]);
}
}

View File

@@ -0,0 +1,148 @@
/*
* 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.
*/
package com.android.settingslib.development;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import android.os.SystemProperties;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class LogpersistPreferenceControllerTest {
private LifecycleOwner mLifecycleOwner;
private Lifecycle mLifecycle;
@Mock
private ListPreference mListPreference;
@Mock
private PreferenceScreen mPreferenceScreen;
private AbstractLogpersistPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
SystemProperties.set("ro.debuggable", "1");
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
mController = new AbstractLogpersistPreferenceController(RuntimeEnvironment.application,
mLifecycle) {
@Override
public void showConfirmationDialog(Preference preference) {}
@Override
public void dismissConfirmationDialog() {}
@Override
public boolean isConfirmationDialogShowing() {
return false;
}
};
doReturn(mListPreference).when(mPreferenceScreen)
.findPreference(mController.getPreferenceKey());
mController.displayPreference(mPreferenceScreen);
}
@Test
public void testAvailable() {
SystemProperties.set("ro.debuggable", "");
assertThat(mController.isAvailable()).isFalse();
SystemProperties.set("ro.debuggable", "1");
assertThat(mController.isAvailable()).isTrue();
SystemProperties.set("ro.debuggable", "0");
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void testUpdateLogpersistValues_null() {
SystemProperties.set(
AbstractLogpersistPreferenceController.ACTUAL_LOGPERSIST_PROPERTY,
AbstractLogpersistPreferenceController.SELECT_LOGPERSIST_PROPERTY_SERVICE);
mController.updateLogpersistValues();
verify(mListPreference, atLeastOnce()).setValue("all");
}
@Test
public void testUpdateLogpersistValues_all() {
SystemProperties.set(
AbstractLogpersistPreferenceController.ACTUAL_LOGPERSIST_PROPERTY,
AbstractLogpersistPreferenceController.SELECT_LOGPERSIST_PROPERTY_SERVICE);
SystemProperties.set(
AbstractLogpersistPreferenceController.ACTUAL_LOGPERSIST_PROPERTY_BUFFER,
"all");
mController.updateLogpersistValues();
verify(mListPreference, atLeastOnce()).setValue("all");
}
@Test
public void testUpdateLogpersistValues_defaultSecurityKernel() {
SystemProperties.set(
AbstractLogpersistPreferenceController.ACTUAL_LOGPERSIST_PROPERTY,
AbstractLogpersistPreferenceController.SELECT_LOGPERSIST_PROPERTY_SERVICE);
SystemProperties.set(
AbstractLogpersistPreferenceController.ACTUAL_LOGPERSIST_PROPERTY_BUFFER,
"default,security,kernel");
mController.updateLogpersistValues();
verify(mListPreference, atLeastOnce()).setValue("default,security,kernel");
}
@Test
public void testUpdateLogpersistValues_kernel() {
SystemProperties.set(
AbstractLogpersistPreferenceController.ACTUAL_LOGPERSIST_PROPERTY,
AbstractLogpersistPreferenceController.SELECT_LOGPERSIST_PROPERTY_SERVICE);
SystemProperties.set(
AbstractLogpersistPreferenceController.ACTUAL_LOGPERSIST_PROPERTY_BUFFER,
"kernel");
mController.updateLogpersistValues();
verify(mListPreference, atLeastOnce()).setValue("kernel");
}
@Test
public void testUpdateLogpersistValues_mainSecuritykernel() {
SystemProperties.set(
AbstractLogpersistPreferenceController.ACTUAL_LOGPERSIST_PROPERTY,
AbstractLogpersistPreferenceController.SELECT_LOGPERSIST_PROPERTY_SERVICE);
SystemProperties.set(
AbstractLogpersistPreferenceController.ACTUAL_LOGPERSIST_PROPERTY_BUFFER,
"main,security,kernel");
mController.updateLogpersistValues();
verify(mListPreference, atLeastOnce()).setValue("all");
}
}

View File

@@ -0,0 +1,76 @@
/*
* 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.
*/
package com.android.settingslib.development;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.os.IBinder;
import android.os.Parcel;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class SystemPropPokerTest {
@Spy
private SystemPropPoker mSystemPropPoker;
@Spy
private SystemPropPoker.PokerTask mPokerTask;
@Mock
private IBinder mMockBinder;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
doReturn(mPokerTask).when(mSystemPropPoker).createPokerTask();
doReturn(new String[] {"testService"}).when(mPokerTask).listServices();
doReturn(mMockBinder).when(mPokerTask).checkService("testService");
doReturn(true).when(mMockBinder)
.transact(anyInt(), any(Parcel.class), nullable(Parcel.class), anyInt());
}
@Test
public void testPoke() throws Exception {
mSystemPropPoker.poke();
verify(mMockBinder, atLeastOnce())
.transact(anyInt(), any(Parcel.class), nullable(Parcel.class), anyInt());
}
@Test
public void testPokeBlocking() throws Exception {
mSystemPropPoker.blockPokes();
mSystemPropPoker.poke();
verify(mMockBinder, never())
.transact(anyInt(), any(Parcel.class), nullable(Parcel.class), anyInt());
mSystemPropPoker.unblockPokes();
mSystemPropPoker.poke();
verify(mMockBinder, atLeastOnce())
.transact(anyInt(), any(Parcel.class), nullable(Parcel.class), anyInt());
}
}

View File

@@ -0,0 +1,106 @@
/*
* 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.
*/
package com.android.settingslib.deviceinfo;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.Mockito.doReturn;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@RunWith(RobolectricTestRunner.class)
public class BluetoothAddressPreferenceControllerTest {
@Mock
private Context mContext;
@Mock
private Lifecycle mLifecycle;
@Mock
private PreferenceScreen mScreen;
@Mock
private Preference mPreference;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
doReturn(mPreference).when(mScreen)
.findPreference(AbstractBluetoothAddressPreferenceController.KEY_BT_ADDRESS);
}
@Implements(BluetoothAdapter.class)
public static class ShadowEmptyBluetoothAdapter {
@Implementation
public static BluetoothAdapter getDefaultAdapter() {
return null;
}
}
@Test
@Config(shadows = ShadowEmptyBluetoothAdapter.class)
public void testNoBluetooth() {
final AbstractBluetoothAddressPreferenceController bluetoothAddressPreferenceController =
new ConcreteBluetoothAddressPreferenceController(mContext, mLifecycle);
assertWithMessage("Should not show pref if no bluetooth")
.that(bluetoothAddressPreferenceController.isAvailable())
.isFalse();
}
@Test
public void testHasBluetooth() {
final AbstractBluetoothAddressPreferenceController bluetoothAddressPreferenceController =
new ConcreteBluetoothAddressPreferenceController(mContext, mLifecycle);
assertWithMessage("Should show pref if bluetooth is present")
.that(bluetoothAddressPreferenceController.isAvailable())
.isTrue();
}
@Test
public void testHasBluetoothStateChangedFilter() {
final AbstractBluetoothAddressPreferenceController bluetoothAddressPreferenceController =
new ConcreteBluetoothAddressPreferenceController(mContext, mLifecycle);
assertWithMessage("Filter should have BluetoothAdapter.ACTION_STATE_CHANGED")
.that(bluetoothAddressPreferenceController.getConnectivityIntents())
.asList().contains(BluetoothAdapter.ACTION_STATE_CHANGED);
}
private static class ConcreteBluetoothAddressPreferenceController
extends AbstractBluetoothAddressPreferenceController {
public ConcreteBluetoothAddressPreferenceController(Context context,
Lifecycle lifecycle) {
super(context, lifecycle);
}
}
}

View File

@@ -0,0 +1,120 @@
/*
* 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.
*/
package com.android.settingslib.deviceinfo;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.IntentFilter;
import android.os.Handler;
import com.android.settingslib.core.lifecycle.Lifecycle;
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 org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class ConnectivityPreferenceControllerTest {
@Mock
private Context mContext;
@Mock
private Lifecycle mLifecycle;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testBroadcastReceiver() {
final AbstractConnectivityPreferenceController preferenceController =
spy(new ConcreteConnectivityPreferenceController(mContext, mLifecycle));
final ArgumentCaptor<BroadcastReceiver> receiverArgumentCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
final ArgumentCaptor<IntentFilter> filterArgumentCaptor =
ArgumentCaptor.forClass(IntentFilter.class);
doReturn(new String[] {"Filter1", "Filter2"})
.when(preferenceController).getConnectivityIntents();
preferenceController.onStart();
verify(mContext, times(1))
.registerReceiver(receiverArgumentCaptor.capture(),
filterArgumentCaptor.capture(),
anyString(), nullable(Handler.class), anyInt());
final BroadcastReceiver receiver = receiverArgumentCaptor.getValue();
final IntentFilter filter = filterArgumentCaptor.getValue();
assertWithMessage("intent filter should match 'Filter1'")
.that(filter.matchAction("Filter1"))
.isTrue();
assertWithMessage("intent filter should match 'Filter2'")
.that(filter.matchAction("Filter2"))
.isTrue();
preferenceController.onStop();
verify(mContext, times(1)).unregisterReceiver(receiver);
}
private static class ConcreteConnectivityPreferenceController
extends AbstractConnectivityPreferenceController {
private ConcreteConnectivityPreferenceController(Context context,
Lifecycle lifecycle) {
super(context, lifecycle);
}
@Override
public boolean isAvailable() {
return false;
}
@Override
public String getPreferenceKey() {
return null;
}
@Override
protected String[] getConnectivityIntents() {
return new String[0];
}
@Override
protected void updateConnectivity() {
}
}
}

View File

@@ -0,0 +1,101 @@
/*
* 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.
*/
package com.android.settingslib.deviceinfo;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import android.content.Context;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.shadows.ShadowSubscriptionManager;
@RunWith(RobolectricTestRunner.class)
public class ImsStatusPreferenceControllerTest {
@Mock
private Context mContext;
@Mock
private Lifecycle mLifecycle;
@Mock
private PreferenceScreen mScreen;
@Mock
private Preference mPreference;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
doReturn(mPreference).when(mScreen)
.findPreference(AbstractImsStatusPreferenceController.KEY_IMS_REGISTRATION_STATE);
}
@Test
public void testIsAvailable() {
ShadowSubscriptionManager.setDefaultDataSubscriptionId(1234);
CarrierConfigManager carrierConfigManager = mock(CarrierConfigManager.class);
doReturn(carrierConfigManager).when(mContext).getSystemService(CarrierConfigManager.class);
PersistableBundle config = new PersistableBundle(1);
config.putBoolean(CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, true);
doReturn(config).when(carrierConfigManager).getConfigForSubId(anyInt());
final AbstractImsStatusPreferenceController imsStatusPreferenceController =
new ConcreteImsStatusPreferenceController(mContext, mLifecycle);
assertWithMessage("Should be available when IMS registration is true").that(
imsStatusPreferenceController.isAvailable()).isTrue();
config.putBoolean(CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false);
assertWithMessage("Should not be available when IMS registration is false")
.that(imsStatusPreferenceController.isAvailable()).isFalse();
doReturn(null).when(carrierConfigManager).getConfigForSubId(anyInt());
assertWithMessage("Should not be available when IMS registration is false")
.that(imsStatusPreferenceController.isAvailable()).isFalse();
doReturn(null).when(mContext).getSystemService(CarrierConfigManager.class);
assertWithMessage("Should not be available when CarrierConfigManager service is null")
.that(imsStatusPreferenceController.isAvailable()).isFalse();
}
private static class ConcreteImsStatusPreferenceController
extends AbstractImsStatusPreferenceController {
private ConcreteImsStatusPreferenceController(Context context,
Lifecycle lifecycle) {
super(context, lifecycle);
}
}
}

View File

@@ -0,0 +1,82 @@
/*
* 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.
*/
package com.android.settingslib.deviceinfo;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.Mockito.doReturn;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import java.util.Arrays;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class IpAddressPreferenceControllerTest {
@Mock
private Context mContext;
@Mock
private Lifecycle mLifecycle;
@Mock
private PreferenceScreen mScreen;
@Mock
private Preference mPreference;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
doReturn(mPreference).when(mScreen)
.findPreference(AbstractIpAddressPreferenceController.KEY_IP_ADDRESS);
}
@Test
public void testHasIntentFilters() {
final AbstractIpAddressPreferenceController ipAddressPreferenceController =
new ConcreteIpAddressPreferenceController(mContext, mLifecycle);
final List<String> expectedIntents = Arrays.asList(
ConnectivityManager.CONNECTIVITY_ACTION,
WifiManager.ACTION_LINK_CONFIGURATION_CHANGED,
WifiManager.NETWORK_STATE_CHANGED_ACTION);
assertWithMessage("Intent filter should contain expected intents")
.that(ipAddressPreferenceController.getConnectivityIntents())
.asList().containsAtLeastElementsIn(expectedIntents);
}
private static class ConcreteIpAddressPreferenceController extends
AbstractIpAddressPreferenceController {
private ConcreteIpAddressPreferenceController(Context context, Lifecycle lifecycle) {
super(context, lifecycle);
}
}
}

View File

@@ -0,0 +1,96 @@
/*
* 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.
*/
package com.android.settingslib.deviceinfo;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.content.Context;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class SerialNumberPreferenceControllerTest {
@Mock
private PreferenceScreen mScreen;
private Context mContext;
private Preference mPreference;
private AbstractSerialNumberPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mPreference = new Preference(mContext);
mPreference.setKey(AbstractSerialNumberPreferenceController.KEY_SERIAL_NUMBER);
when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference);
}
@Test
public void testIsAvaiable_noSerial_shouldReturnFalse() {
mController = new TestPreferenceController(mContext, null);
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void testIsAvailable_hasSerial_shouldReturnTrue() {
mController = new TestPreferenceController(mContext, "123");
assertThat(mController.isAvailable()).isTrue();
}
@Test
public void testDisplay_noSerial_shouldHidePreference() {
mController = new TestPreferenceController(mContext, null);
mController.displayPreference(mScreen);
assertThat(mPreference.isVisible()).isFalse();
}
@Test
public void testDisplay_hasSerial_shouldSetSummary() {
final String serial = "123";
mController = new TestPreferenceController(mContext, serial);
mController.displayPreference(mScreen);
assertThat(mPreference.isVisible()).isTrue();
assertThat(mPreference.getSummary()).isEqualTo(serial);
}
private static class TestPreferenceController
extends AbstractSerialNumberPreferenceController {
TestPreferenceController(Context context, String serialNumber) {
super(context, serialNumber);
}
}
}

View File

@@ -0,0 +1,100 @@
/*
* 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.
*/
package com.android.settingslib.deviceinfo;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.os.SystemClock;
import android.text.format.DateUtils;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.shadows.ShadowLooper;
@RunWith(RobolectricTestRunner.class)
public class UptimePreferenceControllerTest {
@Mock
private Context mContext;
@Mock
private Lifecycle mLifecycle;
@Mock
private PreferenceScreen mScreen;
@Mock
private Preference mPreference;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
doReturn(mPreference).when(mScreen)
.findPreference(AbstractUptimePreferenceController.KEY_UPTIME);
}
@Test
public void testDisplayPreference() {
final AbstractUptimePreferenceController uptimePreferenceController =
new ConcreteUptimePreferenceController(mContext, mLifecycle);
uptimePreferenceController.displayPreference(mScreen);
// SystemClock is shadowed so it shouldn't advance unexpectedly while the test is running
verify(mPreference).setSummary(
DateUtils.formatElapsedTime(SystemClock.elapsedRealtime() / 1000));
}
@Test
public void testUptimeTick() {
final AbstractUptimePreferenceController uptimePreferenceController =
new ConcreteUptimePreferenceController(mContext, null /* lifecycle */);
uptimePreferenceController.displayPreference(mScreen);
verify(mPreference, times(1)).setSummary(any(CharSequence.class));
uptimePreferenceController.onStart();
verify(mPreference, times(2)).setSummary(any(CharSequence.class));
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
verify(mPreference, times(3)).setSummary(any(CharSequence.class));
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
verify(mPreference, times(4)).setSummary(any(CharSequence.class));
}
private static class ConcreteUptimePreferenceController
extends AbstractUptimePreferenceController {
private ConcreteUptimePreferenceController(Context context,
Lifecycle lifecycle) {
super(context, lifecycle);
}
}
}

View File

@@ -0,0 +1,118 @@
/*
* 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.
*/
package com.android.settingslib.deviceinfo;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import android.annotation.SuppressLint;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settingslib.R;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.Arrays;
import java.util.List;
@SuppressLint("HardwareIds")
@RunWith(RobolectricTestRunner.class)
public class WifiMacAddressPreferenceControllerTest {
@Mock
private Lifecycle mLifecycle;
@Mock
private PreferenceScreen mScreen;
@Mock
private WifiManager mWifiManager;
@Mock
private WifiInfo mWifiInfo;
private AbstractWifiMacAddressPreferenceController mController;
private Context mContext;
private Preference mPreference;
private static final String TEST_MAC_ADDRESS = "00:11:22:33:44:55";
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
mPreference = new Preference(mContext);
doReturn(mPreference).when(mScreen)
.findPreference(AbstractWifiMacAddressPreferenceController.KEY_WIFI_MAC_ADDRESS);
doReturn(mWifiManager).when(mContext).getSystemService(WifiManager.class);
doReturn(mWifiInfo).when(mWifiManager).getConnectionInfo();
mController = new ConcreteWifiMacAddressPreferenceController(mContext, mLifecycle);
}
@Test
public void testHasIntentFilters() {
final List<String> expectedIntents = Arrays.asList(
ConnectivityManager.CONNECTIVITY_ACTION,
WifiManager.ACTION_LINK_CONFIGURATION_CHANGED,
WifiManager.NETWORK_STATE_CHANGED_ACTION);
assertWithMessage("Intent filter should contain expected intents")
.that(mController.getConnectivityIntents())
.asList().containsAtLeastElementsIn(expectedIntents);
}
@Test
public void updateConnectivity_null_setMacUnavailable() {
doReturn(null).when(mWifiManager).getFactoryMacAddresses();
mController.displayPreference(mScreen);
assertThat(mPreference.getSummary())
.isEqualTo(mContext.getString(R.string.status_unavailable));
}
@Test
public void updateConnectivity_validMac_setValidMac() {
final String[] macAddresses = new String[]{TEST_MAC_ADDRESS};
doReturn(macAddresses).when(mWifiManager).getFactoryMacAddresses();
mController.displayPreference(mScreen);
assertThat(mPreference.getSummary()).isEqualTo(TEST_MAC_ADDRESS);
}
private static class ConcreteWifiMacAddressPreferenceController
extends AbstractWifiMacAddressPreferenceController {
private ConcreteWifiMacAddressPreferenceController(Context context,
Lifecycle lifecycle) {
super(context, lifecycle);
}
}
}

View File

@@ -0,0 +1,67 @@
/*
* 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.
*/
package com.android.settingslib.display;
import static com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MAX;
import static com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MIN;
import static com.google.common.truth.Truth.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class BrightnessUtilsTest {
private static final int MIN_INT = 1;
private static final int MAX_INT = 255;
private static final float MIN_FLOAT = 0.0f;
private static final float MAX_FLOAT = 1.0f;
@Test
public void linearToGamma_minValue_shouldReturnMin() {
assertThat(BrightnessUtils.convertLinearToGamma(MIN_INT, MIN_INT, MAX_INT))
.isEqualTo(GAMMA_SPACE_MIN);
assertThat(BrightnessUtils.convertLinearToGammaFloat(MIN_FLOAT, MIN_FLOAT, MAX_FLOAT))
.isEqualTo(GAMMA_SPACE_MIN);
}
@Test
public void linearToGamma_maxValue_shouldReturnGammaSpaceMax() {
assertThat(BrightnessUtils.convertLinearToGamma(MAX_INT, MIN_INT, MAX_INT))
.isEqualTo(GAMMA_SPACE_MAX);
assertThat(BrightnessUtils.convertLinearToGammaFloat(MAX_FLOAT, MIN_FLOAT, MAX_FLOAT))
.isEqualTo(GAMMA_SPACE_MAX);
}
@Test
public void gammaToLinear_minValue_shouldReturnMin() {
assertThat(BrightnessUtils.convertGammaToLinear(GAMMA_SPACE_MIN, MIN_INT, MAX_INT))
.isEqualTo(MIN_INT);
assertThat(BrightnessUtils.convertGammaToLinearFloat(GAMMA_SPACE_MIN, MIN_FLOAT, MAX_FLOAT))
.isEqualTo(MIN_FLOAT);
}
@Test
public void gammaToLinear_gammaSpaceValue_shouldReturnMax() {
assertThat(BrightnessUtils.convertGammaToLinear(GAMMA_SPACE_MAX, MIN_INT, MAX_INT))
.isEqualTo(MAX_INT);
assertThat(BrightnessUtils.convertGammaToLinearFloat(GAMMA_SPACE_MAX, MIN_FLOAT, MAX_FLOAT))
.isEqualTo(MAX_FLOAT);
}
}

View File

@@ -0,0 +1,284 @@
/*
* Copyright (C) 2019 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.settingslib.drawer;
import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_ORDER;
import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_GROUP_KEY;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SEARCHABLE;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWITCH_URI;
import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
import static com.google.common.truth.Truth.assertThat;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.UserHandle;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowPackageManager;
@RunWith(RobolectricTestRunner.class)
public class ActivityTileTest {
private Context mContext;
private ActivityInfo mActivityInfo;
private Tile mTile;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mActivityInfo = new ActivityInfo();
mActivityInfo.applicationInfo = new ApplicationInfo();
mActivityInfo.packageName = mContext.getPackageName();
mActivityInfo.name = "abc";
mActivityInfo.icon = com.android.internal.R.drawable.ic_plus;
mActivityInfo.metaData = new Bundle();
mTile = new ActivityTile(mActivityInfo, "category");
}
@Test
public void isPrimaryProfileOnly_profilePrimary_shouldReturnTrue() {
mActivityInfo.metaData.putString(META_DATA_KEY_PROFILE, PROFILE_PRIMARY);
assertThat(mTile.isPrimaryProfileOnly()).isTrue();
}
@Test
public void isPrimaryProfileOnly_profileAll_shouldReturnFalse() {
mActivityInfo.metaData.putString(META_DATA_KEY_PROFILE, PROFILE_ALL);
assertThat(mTile.isPrimaryProfileOnly()).isFalse();
}
@Test
public void isPrimaryProfileOnly_noExplicitValue_shouldReturnFalse() {
assertThat(mTile.isPrimaryProfileOnly()).isFalse();
}
@Test
public void isPrimaryProfileOnly_nullMetadata_shouldReturnFalse() {
mActivityInfo.metaData = null;
assertThat(mTile.isPrimaryProfileOnly()).isFalse();
}
@Test
public void getIcon_noContextOrMetadata_returnNull() {
mActivityInfo.metaData = null;
final Tile tile = new ActivityTile(mActivityInfo, "category");
assertThat(tile.getIcon(null)).isNull();
assertThat(tile.getIcon(RuntimeEnvironment.application)).isNull();
}
@Test
public void getIcon_providedByUri_returnNull() {
mActivityInfo.metaData.putString(META_DATA_PREFERENCE_ICON_URI, "content://foobar/icon");
assertThat(mTile.getIcon(RuntimeEnvironment.application)).isNull();
}
@Test
public void getIcon_hasIconMetadata_returnIcon() {
mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON, android.R.drawable.ic_info);
assertThat(mTile.getIcon(RuntimeEnvironment.application).getResId())
.isEqualTo(android.R.drawable.ic_info);
}
@Test
public void getIcon_transparentColorInMetadata_returnNull() {
mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON, android.R.color.transparent);
assertThat(mTile.getIcon(RuntimeEnvironment.application)).isNull();
}
@Test
public void isIconTintable_hasMetadata_shouldReturnIconTintableMetadata() {
final Tile tile = new ActivityTile(mActivityInfo, "category");
mActivityInfo.metaData.putBoolean(TileUtils.META_DATA_PREFERENCE_ICON_TINTABLE, false);
assertThat(tile.isIconTintable(RuntimeEnvironment.application)).isFalse();
mActivityInfo.metaData.putBoolean(TileUtils.META_DATA_PREFERENCE_ICON_TINTABLE, true);
assertThat(tile.isIconTintable(RuntimeEnvironment.application)).isTrue();
}
@Test
public void isIconTintable_noIcon_shouldReturnFalse() {
final Tile tile = new ActivityTile(mActivityInfo, "category");
assertThat(tile.isIconTintable(RuntimeEnvironment.application)).isFalse();
}
@Test
public void isIconTintable_noTintableMetadata_shouldReturnFalse() {
final Tile tile = new ActivityTile(mActivityInfo, "category");
mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON, android.R.drawable.ic_info);
assertThat(tile.isIconTintable(RuntimeEnvironment.application)).isFalse();
}
@Test
public void getPriority_noMetadata_return0() {
final Tile tile = new ActivityTile(mActivityInfo, "category");
assertThat(tile.getOrder()).isEqualTo(0);
}
@Test
public void getPriority_badMetadata_return0() {
mActivityInfo.metaData.putString(META_DATA_KEY_ORDER, "1");
final Tile tile = new ActivityTile(mActivityInfo, "category");
assertThat(tile.getOrder()).isEqualTo(0);
}
@Test
public void getPriority_validMetadata_returnMetadataValue() {
mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 1);
final Tile tile = new ActivityTile(mActivityInfo, "category");
assertThat(tile.getOrder()).isEqualTo(1);
}
@Test
public void getTitle_shouldEnsureMetadataNotStale() {
final ResolveInfo info = new ResolveInfo();
info.activityInfo = mActivityInfo;
final ShadowPackageManager spm = Shadow.extract(mContext.getPackageManager());
spm.addResolveInfoForIntent(
new Intent().setClassName(mActivityInfo.packageName, mActivityInfo.name), info);
final Tile tile = new ActivityTile(mActivityInfo, "category");
final long staleTimeStamp = -10000;
tile.mLastUpdateTime = staleTimeStamp;
tile.getTitle(RuntimeEnvironment.application);
assertThat(tile.mLastUpdateTime).isNotEqualTo(staleTimeStamp);
}
@Test
public void getTitle_noActivity_returnNull() {
final ResolveInfo info = new ResolveInfo();
info.activityInfo = mActivityInfo;
final ShadowPackageManager spm = Shadow.extract(mContext.getPackageManager());
spm.removePackage(mActivityInfo.packageName);
final Tile tile = new ActivityTile(mActivityInfo, "category");
tile.mComponentInfo = null;
assertThat(tile.getTitle(RuntimeEnvironment.application)).isNull();
}
@Test
public void hasPendingIntent_empty_returnsFalse() {
final Tile tile = new ActivityTile(mActivityInfo, "category");
assertThat(tile.hasPendingIntent()).isFalse();
}
@Test
public void hasPendingIntent_notEmpty_returnsTrue() {
final Tile tile = new ActivityTile(mActivityInfo, "category");
tile.pendingIntentMap.put(
UserHandle.CURRENT, PendingIntent.getActivity(mContext, 0, new Intent(), 0));
assertThat(tile.hasPendingIntent()).isTrue();
}
@Test
public void hasGroupKey_empty_returnsFalse() {
final Tile tile = new ActivityTile(mActivityInfo, "category");
assertThat(tile.hasGroupKey()).isFalse();
}
@Test
public void hasGroupKey_notEmpty_returnsTrue() {
mActivityInfo.metaData.putString(META_DATA_PREFERENCE_GROUP_KEY, "test_key");
final Tile tile = new ActivityTile(mActivityInfo, "category");
assertThat(tile.hasGroupKey()).isTrue();
}
@Test
public void getGroupKey_empty_returnsNull() {
final Tile tile = new ActivityTile(mActivityInfo, "category");
assertThat(tile.getGroupKey()).isNull();
}
@Test
public void getGroupKey_notEmpty_returnsValue() {
mActivityInfo.metaData.putString(META_DATA_PREFERENCE_GROUP_KEY, "test_key");
final Tile tile = new ActivityTile(mActivityInfo, "category");
assertThat(tile.getGroupKey()).isEqualTo("test_key");
}
@Test
public void getType_withoutSwitch_returnsAction() {
final Tile tile = new ActivityTile(mActivityInfo, "category");
assertThat(tile.getType()).isEqualTo(Tile.Type.ACTION);
}
@Test
public void getType_withSwitch_returnsSwitchWithAction() {
mActivityInfo.metaData.putString(META_DATA_PREFERENCE_SWITCH_URI, "test://testabc/");
final Tile tile = new ActivityTile(mActivityInfo, "category");
assertThat(tile.getType()).isEqualTo(Tile.Type.SWITCH_WITH_ACTION);
}
@Test
public void isSearchable_nullMetadata_isTrue() {
mActivityInfo.metaData = null;
final Tile tile = new ActivityTile(mActivityInfo, "category");
assertThat(tile.isSearchable()).isTrue();
}
@Test
public void isSearchable_notSet_isTrue() {
final Tile tile = new ActivityTile(mActivityInfo, "category");
assertThat(tile.isSearchable()).isTrue();
}
@Test
public void isSearchable_isSet_false() {
mActivityInfo.metaData.putBoolean(META_DATA_PREFERENCE_SEARCHABLE, false);
final Tile tile = new ActivityTile(mActivityInfo, "category");
assertThat(tile.isSearchable()).isFalse();
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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.
*/
package com.android.settingslib.drawer;
import static com.google.common.truth.Truth.assertThat;
import android.util.ArraySet;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import java.util.Set;
@RunWith(RobolectricTestRunner.class)
public class CategoryKeyTest {
@Test
public void testKeyCompatMap_allOldCategoryKeyAreMapped() {
assertThat(CategoryKey.KEY_COMPAT_MAP.size()).isEqualTo(4);
}
@Test
public void removingAnyKeyBreaksCompiler() {
// The keys in this test can be added but cannot be removed. Removing any key will remove
// categories from Settings app. Bad things will happen.
final Set<String> allKeys = new ArraySet<>();
// DO NOT REMOVE ANYTHING BELOW
allKeys.add(CategoryKey.CATEGORY_HOMEPAGE);
allKeys.add(CategoryKey.CATEGORY_CONNECT);
allKeys.add(CategoryKey.CATEGORY_DEVICE);
allKeys.add(CategoryKey.CATEGORY_APPS);
allKeys.add(CategoryKey.CATEGORY_APPS_DEFAULT);
allKeys.add(CategoryKey.CATEGORY_BATTERY);
allKeys.add(CategoryKey.CATEGORY_DISPLAY);
allKeys.add(CategoryKey.CATEGORY_SOUND);
allKeys.add(CategoryKey.CATEGORY_STORAGE);
allKeys.add(CategoryKey.CATEGORY_SECURITY);
allKeys.add(CategoryKey.CATEGORY_SECURITY_LOCKSCREEN);
allKeys.add(CategoryKey.CATEGORY_ACCOUNT);
allKeys.add(CategoryKey.CATEGORY_ACCOUNT_DETAIL);
allKeys.add(CategoryKey.CATEGORY_SYSTEM);
allKeys.add(CategoryKey.CATEGORY_SYSTEM_LANGUAGE);
allKeys.add(CategoryKey.CATEGORY_SYSTEM_DEVELOPMENT);
allKeys.add(CategoryKey.CATEGORY_GESTURES);
allKeys.add(CategoryKey.CATEGORY_NIGHT_DISPLAY);
allKeys.add(CategoryKey.CATEGORY_SMART_BATTERY_SETTINGS);
allKeys.add(CategoryKey.CATEGORY_COMMUNAL_SETTINGS);
// DO NOT REMOVE ANYTHING ABOVE
assertThat(allKeys.size()).isEqualTo(20);
}
}

View File

@@ -0,0 +1,472 @@
/*
* 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.settingslib.drawer;
import static com.android.settingslib.drawer.EntriesProvider.EXTRA_ENTRY_DATA;
import static com.android.settingslib.drawer.EntriesProvider.EXTRA_SWITCH_CHECKED_STATE;
import static com.android.settingslib.drawer.EntriesProvider.EXTRA_SWITCH_DATA;
import static com.android.settingslib.drawer.EntriesProvider.EXTRA_SWITCH_SET_CHECKED_ERROR;
import static com.android.settingslib.drawer.EntriesProvider.EXTRA_SWITCH_SET_CHECKED_ERROR_MESSAGE;
import static com.android.settingslib.drawer.EntriesProvider.METHOD_GET_DYNAMIC_SUMMARY;
import static com.android.settingslib.drawer.EntriesProvider.METHOD_GET_DYNAMIC_TITLE;
import static com.android.settingslib.drawer.EntriesProvider.METHOD_GET_ENTRY_DATA;
import static com.android.settingslib.drawer.EntriesProvider.METHOD_GET_PROVIDER_ICON;
import static com.android.settingslib.drawer.EntriesProvider.METHOD_GET_SWITCH_DATA;
import static com.android.settingslib.drawer.EntriesProvider.METHOD_IS_CHECKED;
import static com.android.settingslib.drawer.EntriesProvider.METHOD_ON_CHECKED_CHANGED;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_PENDING_INTENT;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE;
import static com.google.common.truth.Truth.assertThat;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ProviderInfo;
import android.os.Bundle;
import com.android.settingslib.drawer.EntryController.MetaData;
import com.android.settingslib.drawer.PrimarySwitchControllerTest.TestPrimarySwitchController;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class EntriesProviderTest {
@Rule
public final ExpectedException thrown = ExpectedException.none();
private Context mContext;
private ProviderInfo mProviderInfo;
private TestEntriesProvider mEntriesProvider;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mEntriesProvider = new TestEntriesProvider();
mProviderInfo = new ProviderInfo();
mProviderInfo.authority = "auth";
}
@Test
public void attachInfo_noController_shouldThrowIllegalArgumentException() {
thrown.expect(IllegalArgumentException.class);
mEntriesProvider.attachInfo(mContext, mProviderInfo);
}
@Test
public void attachInfo_NoKeyInController_shouldThrowNullPointerException() {
thrown.expect(NullPointerException.class);
final TestEntryController controller = new TestEntryController();
mEntriesProvider.addController(controller);
mEntriesProvider.attachInfo(mContext, mProviderInfo);
}
@Test
public void attachInfo_NoMetaDataInController_shouldThrowNullPointerException() {
thrown.expect(NullPointerException.class);
final TestEntryController controller = new TestEntryController();
controller.setKey("123");
mEntriesProvider.addController(controller);
mEntriesProvider.attachInfo(mContext, mProviderInfo);
}
@Test
public void attachInfo_duplicateKey_shouldThrowIllegalArgumentException() {
thrown.expect(IllegalArgumentException.class);
final TestEntryController controller1 = new TestEntryController();
final TestEntryController controller2 = new TestEntryController();
controller1.setKey("123");
controller2.setKey("123");
controller1.setMetaData(new MetaData("category"));
controller2.setMetaData(new MetaData("category"));
mEntriesProvider.addController(controller1);
mEntriesProvider.addController(controller2);
mEntriesProvider.attachInfo(mContext, mProviderInfo);
}
@Test
public void attachInfo_hasDifferentControllers_shouldNotThrowException() {
final TestEntryController controller1 = new TestEntryController();
final TestEntryController controller2 = new TestEntryController();
controller1.setKey("123");
controller2.setKey("456");
controller1.setMetaData(new MetaData("category"));
controller2.setMetaData(new MetaData("category"));
mEntriesProvider.addController(controller1);
mEntriesProvider.addController(controller2);
mEntriesProvider.attachInfo(mContext, mProviderInfo);
}
@Test
public void getEntryData_shouldNotReturnPrimarySwitchData() {
final EntryController controller = new TestPrimarySwitchController("123");
mEntriesProvider.addController(controller);
mEntriesProvider.attachInfo(mContext, mProviderInfo);
final Bundle switchData = mEntriesProvider.call(METHOD_GET_ENTRY_DATA, "uri",
null /* extras*/);
final ArrayList<Bundle> dataList = switchData.getParcelableArrayList(EXTRA_ENTRY_DATA);
assertThat(dataList).isEmpty();
}
@Test
public void getEntryData_shouldReturnDataList() {
final TestEntryController controller = new TestEntryController();
final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
controller.setKey("123");
controller.setMetaData(new MetaData("category").setPendingIntent(pendingIntent));
mEntriesProvider.addController(controller);
mEntriesProvider.attachInfo(mContext, mProviderInfo);
final Bundle entryData = mEntriesProvider.call(METHOD_GET_ENTRY_DATA, "uri",
null /* extras*/);
final ArrayList<Bundle> dataList = entryData.getParcelableArrayList(EXTRA_ENTRY_DATA);
assertThat(dataList).hasSize(1);
assertThat(dataList.get(0).getString(META_DATA_PREFERENCE_KEYHINT)).isEqualTo("123");
assertThat(dataList.get(0).getParcelable(META_DATA_PREFERENCE_PENDING_INTENT,
PendingIntent.class))
.isEqualTo(pendingIntent);
}
@Test
public void getSwitchData_shouldReturnDataList() {
final TestEntryController controller = new TestEntryController();
final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
controller.setKey("123");
controller.setMetaData(new MetaData("category").setPendingIntent(pendingIntent));
mEntriesProvider.addController(controller);
mEntriesProvider.attachInfo(mContext, mProviderInfo);
final Bundle entryData = mEntriesProvider.call(METHOD_GET_SWITCH_DATA, "uri",
null /* extras*/);
final ArrayList<Bundle> dataList = entryData.getParcelableArrayList(EXTRA_SWITCH_DATA);
assertThat(dataList).hasSize(1);
assertThat(dataList.get(0).getString(META_DATA_PREFERENCE_KEYHINT)).isEqualTo("123");
assertThat(dataList.get(0).getParcelable(META_DATA_PREFERENCE_PENDING_INTENT,
PendingIntent.class))
.isEqualTo(pendingIntent);
}
@Test
public void getEntryDataByKey_shouldReturnData() {
final Bundle extras = new Bundle();
extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
final TestEntryController controller = new TestEntryController();
controller.setKey("123");
controller.setMetaData(new MetaData("category"));
mEntriesProvider.addController(controller);
mEntriesProvider.attachInfo(mContext, mProviderInfo);
final Bundle entryData = mEntriesProvider.call(METHOD_GET_ENTRY_DATA, "uri", extras);
assertThat(entryData.getString(META_DATA_PREFERENCE_KEYHINT)).isEqualTo("123");
}
@Test
public void getSwitchDataByKey_shouldReturnData() {
final Bundle extras = new Bundle();
extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
final TestEntryController controller = new TestEntryController();
controller.setKey("123");
controller.setMetaData(new MetaData("category"));
mEntriesProvider.addController(controller);
mEntriesProvider.attachInfo(mContext, mProviderInfo);
final Bundle entryData = mEntriesProvider.call(METHOD_GET_SWITCH_DATA, "uri", extras);
assertThat(entryData.getString(META_DATA_PREFERENCE_KEYHINT)).isEqualTo("123");
}
@Test
public void isSwitchChecked_shouldReturnCheckedState() {
final Bundle extras = new Bundle();
extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
final TestSwitchController controller = new TestSwitchController();
controller.setKey("123");
controller.setMetaData(new MetaData("category"));
mEntriesProvider.addController(controller);
mEntriesProvider.attachInfo(mContext, mProviderInfo);
controller.setSwitchChecked(true);
Bundle result = mEntriesProvider.call(METHOD_IS_CHECKED, "uri", extras);
assertThat(result.getBoolean(EXTRA_SWITCH_CHECKED_STATE)).isTrue();
controller.setSwitchChecked(false);
result = mEntriesProvider.call(METHOD_IS_CHECKED, "uri", extras);
assertThat(result.getBoolean(EXTRA_SWITCH_CHECKED_STATE)).isFalse();
}
@Test
public void getProviderIcon_noImplementInterface_shouldReturnNull() {
final Bundle extras = new Bundle();
extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
final TestEntryController controller = new TestEntryController();
controller.setKey("123");
controller.setMetaData(new MetaData("category"));
mEntriesProvider.addController(controller);
mEntriesProvider.attachInfo(mContext, mProviderInfo);
final Bundle iconBundle = mEntriesProvider.call(METHOD_GET_PROVIDER_ICON, "uri", extras);
assertThat(iconBundle).isNull();
}
@Test
public void getProviderIcon_implementInterface_shouldReturnIcon() {
final Bundle extras = new Bundle();
extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
final TestEntryController controller = new TestDynamicController();
controller.setKey("123");
controller.setMetaData(new MetaData("category"));
mEntriesProvider.addController(controller);
mEntriesProvider.attachInfo(mContext, mProviderInfo);
final Bundle iconBundle = mEntriesProvider.call(METHOD_GET_PROVIDER_ICON, "uri", extras);
assertThat(iconBundle).isEqualTo(TestDynamicController.ICON_BUNDLE);
}
@Test
public void getDynamicTitle_noImplementInterface_shouldReturnNull() {
final Bundle extras = new Bundle();
extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
final TestEntryController controller = new TestEntryController();
controller.setKey("123");
controller.setMetaData(new MetaData("category"));
mEntriesProvider.addController(controller);
mEntriesProvider.attachInfo(mContext, mProviderInfo);
final Bundle result = mEntriesProvider.call(METHOD_GET_DYNAMIC_TITLE, "uri", extras);
assertThat(result).isNull();
}
@Test
public void getDynamicTitle_implementInterface_shouldReturnTitle() {
final Bundle extras = new Bundle();
extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
final TestEntryController controller = new TestDynamicController();
controller.setKey("123");
controller.setMetaData(new MetaData("category"));
mEntriesProvider.addController(controller);
mEntriesProvider.attachInfo(mContext, mProviderInfo);
final Bundle result = mEntriesProvider.call(METHOD_GET_DYNAMIC_TITLE, "uri", extras);
assertThat(result.getString(META_DATA_PREFERENCE_TITLE))
.isEqualTo(TestDynamicController.TITLE);
}
@Test
public void getDynamicSummary_noImplementInterface_shouldReturnNull() {
final Bundle extras = new Bundle();
extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
final TestEntryController controller = new TestEntryController();
controller.setKey("123");
controller.setMetaData(new MetaData("category"));
mEntriesProvider.addController(controller);
mEntriesProvider.attachInfo(mContext, mProviderInfo);
final Bundle result = mEntriesProvider.call(METHOD_GET_DYNAMIC_SUMMARY, "uri", extras);
assertThat(result).isNull();
}
@Test
public void getDynamicSummary_implementInterface_shouldReturnSummary() {
final Bundle extras = new Bundle();
extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
final TestEntryController controller = new TestDynamicController();
controller.setKey("123");
controller.setMetaData(new MetaData("category"));
mEntriesProvider.addController(controller);
mEntriesProvider.attachInfo(mContext, mProviderInfo);
final Bundle result = mEntriesProvider.call(METHOD_GET_DYNAMIC_SUMMARY, "uri", extras);
assertThat(result.getString(META_DATA_PREFERENCE_SUMMARY))
.isEqualTo(TestDynamicController.SUMMARY);
}
@Test
public void onSwitchCheckedChangedSuccess_shouldReturnNoError() {
final Bundle extras = new Bundle();
extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
final TestSwitchController controller = new TestSwitchController();
controller.setKey("123");
controller.setMetaData(new MetaData("category"));
mEntriesProvider.addController(controller);
mEntriesProvider.attachInfo(mContext, mProviderInfo);
final Bundle result = mEntriesProvider.call(METHOD_ON_CHECKED_CHANGED, "uri", extras);
assertThat(result.getBoolean(EXTRA_SWITCH_SET_CHECKED_ERROR)).isFalse();
}
@Test
public void onSwitchCheckedChangedFailed_shouldReturnErrorMessage() {
final Bundle extras = new Bundle();
extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
final TestSwitchController controller = new TestSwitchController();
controller.setKey("123");
controller.setMetaData(new MetaData("category"));
controller.setSwitchErrorMessage("error");
mEntriesProvider.addController(controller);
mEntriesProvider.attachInfo(mContext, mProviderInfo);
final Bundle result = mEntriesProvider.call(METHOD_ON_CHECKED_CHANGED, "uri", extras);
assertThat(result.getBoolean(EXTRA_SWITCH_SET_CHECKED_ERROR)).isTrue();
assertThat(result.getString(EXTRA_SWITCH_SET_CHECKED_ERROR_MESSAGE)).isEqualTo("error");
}
private static class TestEntriesProvider extends EntriesProvider {
private List<EntryController> mControllers;
@Override
protected List<EntryController> createEntryControllers() {
return mControllers;
}
void addController(EntryController controller) {
if (mControllers == null) {
mControllers = new ArrayList<>();
}
mControllers.add(controller);
}
}
private static class TestEntryController extends EntryController {
private String mKey;
private MetaData mMetaData;
@Override
public String getKey() {
return mKey;
}
@Override
protected MetaData getMetaData() {
return mMetaData;
}
void setKey(String key) {
mKey = key;
}
void setMetaData(MetaData metaData) {
mMetaData = metaData;
}
}
private static class TestSwitchController extends EntryController implements ProviderSwitch {
private String mKey;
private MetaData mMetaData;
private boolean mChecked;
private String mErrorMsg;
@Override
public String getKey() {
return mKey;
}
@Override
protected MetaData getMetaData() {
return mMetaData;
}
@Override
public boolean isSwitchChecked() {
return mChecked;
}
@Override
public boolean onSwitchCheckedChanged(boolean checked) {
return mErrorMsg == null ? true : false;
}
@Override
public String getSwitchErrorMessage(boolean attemptedChecked) {
return mErrorMsg;
}
void setKey(String key) {
mKey = key;
}
void setMetaData(MetaData metaData) {
mMetaData = metaData;
}
void setSwitchChecked(boolean checked) {
mChecked = checked;
}
void setSwitchErrorMessage(String errorMsg) {
mErrorMsg = errorMsg;
}
}
private static class TestDynamicController extends TestEntryController
implements ProviderIcon, DynamicTitle, DynamicSummary {
static final String TITLE = "title";
static final String SUMMARY = "summary";
static final Bundle ICON_BUNDLE = new Bundle();
@Override
public Bundle getProviderIcon() {
return ICON_BUNDLE;
}
@Override
public String getDynamicTitle() {
return TITLE;
}
@Override
public String getDynamicSummary() {
return SUMMARY;
}
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2019 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.settingslib.drawer;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class PrimarySwitchControllerTest {
@Rule
public final ExpectedException thrown = ExpectedException.none();
private PrimarySwitchController mController;
@Before
public void setUp() {
mController = new TestPrimarySwitchController("123");
}
@Test
public void getMetaData_shouldThrowUnsupportedOperationException() {
thrown.expect(UnsupportedOperationException.class);
mController.getMetaData();
}
@Test
public void getBundle_shouldThrowUnsupportedOperationException() {
thrown.expect(UnsupportedOperationException.class);
mController.getBundle();
}
static class TestPrimarySwitchController extends PrimarySwitchController {
private String mKey;
TestPrimarySwitchController(String key) {
mKey = key;
}
@Override
public String getSwitchKey() {
return mKey;
}
@Override
protected boolean isChecked() {
return true;
}
@Override
protected boolean onCheckedChanged(boolean checked) {
return true;
}
@Override
protected String getErrorMessage(boolean attemptedChecked) {
return null;
}
}
}

View File

@@ -0,0 +1,291 @@
/*
* Copyright (C) 2019 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.settingslib.drawer;
import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_ORDER;
import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_GROUP_KEY;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SEARCHABLE;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWITCH_URI;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE;
import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
import static com.google.common.truth.Truth.assertThat;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.UserHandle;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowPackageManager;
@RunWith(RobolectricTestRunner.class)
public class ProviderTileTest {
@Rule
public final ExpectedException thrown = ExpectedException.none();
private Context mContext;
private ProviderInfo mProviderInfo;
private Bundle mMetaData;
private Tile mTile;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mProviderInfo = new ProviderInfo();
mProviderInfo.applicationInfo = new ApplicationInfo();
mProviderInfo.packageName = mContext.getPackageName();
mProviderInfo.name = "abc";
mProviderInfo.authority = "authority";
mMetaData = new Bundle();
mMetaData.putString(META_DATA_PREFERENCE_KEYHINT, "key");
mMetaData.putString(META_DATA_PREFERENCE_TITLE, "title");
mMetaData.putInt(META_DATA_PREFERENCE_ICON, com.android.internal.R.drawable.ic_plus);
mTile = new ProviderTile(mProviderInfo, "category", mMetaData);
}
@Test
public void isPrimaryProfileOnly_profilePrimary_shouldReturnTrue() {
mMetaData.putString(META_DATA_KEY_PROFILE, PROFILE_PRIMARY);
assertThat(mTile.isPrimaryProfileOnly()).isTrue();
}
@Test
public void isPrimaryProfileOnly_profileAll_shouldReturnFalse() {
mMetaData.putString(META_DATA_KEY_PROFILE, PROFILE_ALL);
assertThat(mTile.isPrimaryProfileOnly()).isFalse();
}
@Test
public void isPrimaryProfileOnly_noExplicitValue_shouldReturnFalse() {
assertThat(mTile.isPrimaryProfileOnly()).isFalse();
}
@Test
public void getIcon_noContextOrMetadata_shouldThrowNullPointerException() {
thrown.expect(NullPointerException.class);
final Tile tile = new ProviderTile(mProviderInfo, "category", null);
}
@Test
public void getIcon_hasIconMetadata_returnIcon() {
mMetaData.putInt(META_DATA_PREFERENCE_ICON, android.R.drawable.ic_info);
assertThat(mTile.getIcon(RuntimeEnvironment.application).getResId())
.isEqualTo(android.R.drawable.ic_info);
}
@Test
public void isIconTintable_hasMetadata_shouldReturnIconTintableMetadata() {
final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
mMetaData.putBoolean(TileUtils.META_DATA_PREFERENCE_ICON_TINTABLE, false);
assertThat(tile.isIconTintable(RuntimeEnvironment.application)).isFalse();
mMetaData.putBoolean(TileUtils.META_DATA_PREFERENCE_ICON_TINTABLE, true);
assertThat(tile.isIconTintable(RuntimeEnvironment.application)).isTrue();
}
@Test
public void isIconTintable_noIcon_shouldReturnFalse() {
final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
assertThat(tile.isIconTintable(RuntimeEnvironment.application)).isFalse();
}
@Test
public void isIconTintable_noTintableMetadata_shouldReturnFalse() {
final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
mMetaData.putInt(META_DATA_PREFERENCE_ICON, android.R.drawable.ic_info);
assertThat(tile.isIconTintable(RuntimeEnvironment.application)).isFalse();
}
@Test
public void getPriority_noMetadata_return0() {
final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
assertThat(tile.getOrder()).isEqualTo(0);
}
@Test
public void getPriority_badMetadata_return0() {
mMetaData.putString(META_DATA_KEY_ORDER, "1");
final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
assertThat(tile.getOrder()).isEqualTo(0);
}
@Test
public void getPriority_validMetadata_returnMetadataValue() {
mMetaData.putInt(META_DATA_KEY_ORDER, 1);
final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
assertThat(tile.getOrder()).isEqualTo(1);
}
@Test
@Config(shadows = ShadowTileUtils.class)
public void getTitle_shouldEnsureMetadataNotStale() {
final ResolveInfo info = new ResolveInfo();
info.providerInfo = mProviderInfo;
final ShadowPackageManager spm = Shadow.extract(mContext.getPackageManager());
spm.addResolveInfoForIntent(
new Intent().setClassName(mProviderInfo.packageName, mProviderInfo.name), info);
ShadowTileUtils.setMetaData(mMetaData);
final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
final long staleTimeStamp = -10000;
tile.mLastUpdateTime = staleTimeStamp;
tile.getTitle(RuntimeEnvironment.application);
assertThat(tile.mLastUpdateTime).isNotEqualTo(staleTimeStamp);
}
@Test
public void hasPendingIntent_empty_returnsFalse() {
final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
assertThat(tile.hasPendingIntent()).isFalse();
}
@Test
public void hasPendingIntent_notEmpty_returnsTrue() {
final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
tile.pendingIntentMap.put(
UserHandle.CURRENT, PendingIntent.getActivity(mContext, 0, new Intent(), 0));
assertThat(tile.hasPendingIntent()).isTrue();
}
@Test
public void hasGroupKey_empty_returnsFalse() {
final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
assertThat(tile.hasGroupKey()).isFalse();
}
@Test
public void hasGroupKey_notEmpty_returnsTrue() {
mMetaData.putString(META_DATA_PREFERENCE_GROUP_KEY, "test_key");
final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
assertThat(tile.hasGroupKey()).isTrue();
}
@Test
public void getGroupKey_empty_returnsNull() {
final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
assertThat(tile.getGroupKey()).isNull();
}
@Test
public void getGroupKey_notEmpty_returnsValue() {
mMetaData.putString(META_DATA_PREFERENCE_GROUP_KEY, "test_key");
final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
assertThat(tile.getGroupKey()).isEqualTo("test_key");
}
@Test
public void getType_withSwitch_returnsSwitch() {
mMetaData.putString(META_DATA_PREFERENCE_SWITCH_URI, "test://testabc/");
final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
assertThat(tile.getType()).isEqualTo(Tile.Type.SWITCH);
}
@Test
public void getType_withSwitchAndPendingIntent_returnsSwitchWithAction() {
mMetaData.putString(META_DATA_PREFERENCE_SWITCH_URI, "test://testabc/");
final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
tile.pendingIntentMap.put(
UserHandle.CURRENT, PendingIntent.getActivity(mContext, 0, new Intent(), 0));
assertThat(tile.getType()).isEqualTo(Tile.Type.SWITCH_WITH_ACTION);
}
@Test
public void getType_withPendingIntent_returnsExternalAction() {
final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
tile.pendingIntentMap.put(
UserHandle.CURRENT, PendingIntent.getActivity(mContext, 0, new Intent(), 0));
assertThat(tile.getType()).isEqualTo(Tile.Type.EXTERNAL_ACTION);
}
@Test
public void getType_withoutSwitchAndPendingIntent_returnsGroup() {
final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
assertThat(tile.getType()).isEqualTo(Tile.Type.GROUP);
}
@Test
public void isSearchable_notSet_isTrue() {
final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
assertThat(tile.isSearchable()).isTrue();
}
@Test
public void isSearchable_isSet_false() {
mMetaData.putBoolean(META_DATA_PREFERENCE_SEARCHABLE, false);
final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
assertThat(tile.isSearchable()).isFalse();
}
@Implements(TileUtils.class)
private static class ShadowTileUtils {
private static Bundle sMetaData;
@Implementation
protected static Bundle getEntryDataFromProvider(Context context, String authority,
String key) {
return sMetaData;
}
private static void setMetaData(Bundle metaData) {
sMetaData = metaData;
}
}
}

View File

@@ -0,0 +1,403 @@
/*
* Copyright (C) 2019 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.settingslib.drawer;
import static com.android.settingslib.drawer.SwitchesProvider.EXTRA_SWITCH_CHECKED_STATE;
import static com.android.settingslib.drawer.SwitchesProvider.EXTRA_SWITCH_DATA;
import static com.android.settingslib.drawer.SwitchesProvider.EXTRA_SWITCH_SET_CHECKED_ERROR;
import static com.android.settingslib.drawer.SwitchesProvider.EXTRA_SWITCH_SET_CHECKED_ERROR_MESSAGE;
import static com.android.settingslib.drawer.SwitchesProvider.METHOD_GET_DYNAMIC_SUMMARY;
import static com.android.settingslib.drawer.SwitchesProvider.METHOD_GET_DYNAMIC_TITLE;
import static com.android.settingslib.drawer.SwitchesProvider.METHOD_GET_PROVIDER_ICON;
import static com.android.settingslib.drawer.SwitchesProvider.METHOD_GET_SWITCH_DATA;
import static com.android.settingslib.drawer.SwitchesProvider.METHOD_IS_CHECKED;
import static com.android.settingslib.drawer.SwitchesProvider.METHOD_ON_CHECKED_CHANGED;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.content.pm.ProviderInfo;
import android.os.Bundle;
import com.android.settingslib.drawer.PrimarySwitchControllerTest.TestPrimarySwitchController;
import com.android.settingslib.drawer.SwitchController.MetaData;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class SwitchesProviderTest {
@Rule
public final ExpectedException thrown = ExpectedException.none();
private Context mContext;
private ProviderInfo mProviderInfo;
private TestSwitchesProvider mSwitchesProvider;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mSwitchesProvider = new TestSwitchesProvider();
mProviderInfo = new ProviderInfo();
mProviderInfo.authority = "auth";
}
@Test
public void attachInfo_noController_shouldThrowIllegalArgumentException() {
thrown.expect(IllegalArgumentException.class);
mSwitchesProvider.attachInfo(mContext, mProviderInfo);
}
@Test
public void attachInfo_NoSwitchKeyInController_shouldThrowNullPointerException() {
thrown.expect(NullPointerException.class);
final TestSwitchController controller = new TestSwitchController();
mSwitchesProvider.addSwitchController(controller);
mSwitchesProvider.attachInfo(mContext, mProviderInfo);
}
@Test
public void attachInfo_NoMetaDataInController_shouldThrowNullPointerException() {
thrown.expect(NullPointerException.class);
final TestSwitchController controller = new TestSwitchController();
controller.setKey("123");
mSwitchesProvider.addSwitchController(controller);
mSwitchesProvider.attachInfo(mContext, mProviderInfo);
}
@Test
public void attachInfo_duplicateSwitchKey_shouldThrowIllegalArgumentException() {
thrown.expect(IllegalArgumentException.class);
final TestSwitchController controller1 = new TestSwitchController();
final TestSwitchController controller2 = new TestSwitchController();
controller1.setKey("123");
controller2.setKey("123");
controller1.setMetaData(new MetaData("category"));
controller2.setMetaData(new MetaData("category"));
mSwitchesProvider.addSwitchController(controller1);
mSwitchesProvider.addSwitchController(controller2);
mSwitchesProvider.attachInfo(mContext, mProviderInfo);
}
@Test
public void attachInfo_hasDifferentControllers_shouldNotThrowException() {
final TestSwitchController controller1 = new TestSwitchController();
final TestSwitchController controller2 = new TestSwitchController();
controller1.setKey("123");
controller2.setKey("456");
controller1.setMetaData(new MetaData("category"));
controller2.setMetaData(new MetaData("category"));
mSwitchesProvider.addSwitchController(controller1);
mSwitchesProvider.addSwitchController(controller2);
mSwitchesProvider.attachInfo(mContext, mProviderInfo);
}
@Test
public void getSwitchData_shouldNotReturnPrimarySwitchData() {
final SwitchController controller = new TestPrimarySwitchController("123");
mSwitchesProvider.addSwitchController(controller);
mSwitchesProvider.attachInfo(mContext, mProviderInfo);
final Bundle switchData = mSwitchesProvider.call(METHOD_GET_SWITCH_DATA, "uri" ,
null /* extras*/);
final ArrayList<Bundle> dataList = switchData.getParcelableArrayList(EXTRA_SWITCH_DATA);
assertThat(dataList).isEmpty();
}
@Test
public void getSwitchData_shouldReturnDataList() {
final TestSwitchController controller = new TestSwitchController();
controller.setKey("123");
controller.setMetaData(new MetaData("category"));
mSwitchesProvider.addSwitchController(controller);
mSwitchesProvider.attachInfo(mContext, mProviderInfo);
final Bundle switchData = mSwitchesProvider.call(METHOD_GET_SWITCH_DATA, "uri" ,
null /* extras*/);
final ArrayList<Bundle> dataList = switchData.getParcelableArrayList(EXTRA_SWITCH_DATA);
assertThat(dataList).hasSize(1);
assertThat(dataList.get(0).getString(META_DATA_PREFERENCE_KEYHINT)).isEqualTo("123");
}
@Test
public void getSwitchDataByKey_shouldReturnData() {
final Bundle extras = new Bundle();
extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
final TestSwitchController controller = new TestSwitchController();
controller.setKey("123");
controller.setMetaData(new MetaData("category"));
mSwitchesProvider.addSwitchController(controller);
mSwitchesProvider.attachInfo(mContext, mProviderInfo);
final Bundle switchData = mSwitchesProvider.call(METHOD_GET_SWITCH_DATA, "uri" , extras);
assertThat(switchData.getString(META_DATA_PREFERENCE_KEYHINT)).isEqualTo("123");
}
@Test
public void isChecked_shouldReturnCheckedState() {
final Bundle extras = new Bundle();
extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
final TestSwitchController controller = new TestSwitchController();
controller.setKey("123");
controller.setMetaData(new MetaData("category"));
mSwitchesProvider.addSwitchController(controller);
mSwitchesProvider.attachInfo(mContext, mProviderInfo);
controller.setChecked(true);
Bundle result = mSwitchesProvider.call(METHOD_IS_CHECKED, "uri" , extras);
assertThat(result.getBoolean(EXTRA_SWITCH_CHECKED_STATE)).isTrue();
controller.setChecked(false);
result = mSwitchesProvider.call(METHOD_IS_CHECKED, "uri" , extras);
assertThat(result.getBoolean(EXTRA_SWITCH_CHECKED_STATE)).isFalse();
}
@Test
public void getProviderIcon_noImplementInterface_shouldReturnNull() {
final Bundle extras = new Bundle();
extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
final TestSwitchController controller = new TestSwitchController();
controller.setKey("123");
controller.setMetaData(new MetaData("category"));
mSwitchesProvider.addSwitchController(controller);
mSwitchesProvider.attachInfo(mContext, mProviderInfo);
final Bundle iconBundle = mSwitchesProvider.call(METHOD_GET_PROVIDER_ICON, "uri" , extras);
assertThat(iconBundle).isNull();
}
@Test
public void getProviderIcon_implementInterface_shouldReturnIcon() {
final Bundle extras = new Bundle();
extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
final TestSwitchController controller = new TestDynamicSwitchController();
controller.setKey("123");
controller.setMetaData(new MetaData("category"));
mSwitchesProvider.addSwitchController(controller);
mSwitchesProvider.attachInfo(mContext, mProviderInfo);
final Bundle iconBundle = mSwitchesProvider.call(METHOD_GET_PROVIDER_ICON, "uri" , extras);
assertThat(iconBundle).isEqualTo(TestDynamicSwitchController.ICON_BUNDLE);
}
@Test
public void getDynamicTitle_noImplementInterface_shouldReturnNull() {
final Bundle extras = new Bundle();
extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
final TestSwitchController controller = new TestSwitchController();
controller.setKey("123");
controller.setMetaData(new MetaData("category"));
mSwitchesProvider.addSwitchController(controller);
mSwitchesProvider.attachInfo(mContext, mProviderInfo);
final Bundle result = mSwitchesProvider.call(METHOD_GET_DYNAMIC_TITLE, "uri" , extras);
assertThat(result).isNull();
}
@Test
public void getDynamicTitle_implementInterface_shouldReturnTitle() {
final Bundle extras = new Bundle();
extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
final TestSwitchController controller = new TestDynamicSwitchController();
controller.setKey("123");
controller.setMetaData(new MetaData("category"));
mSwitchesProvider.addSwitchController(controller);
mSwitchesProvider.attachInfo(mContext, mProviderInfo);
final Bundle result = mSwitchesProvider.call(METHOD_GET_DYNAMIC_TITLE, "uri" , extras);
assertThat(result.getString(META_DATA_PREFERENCE_TITLE))
.isEqualTo(TestDynamicSwitchController.TITLE);
}
@Test
public void getDynamicSummary_noImplementInterface_shouldReturnNull() {
final Bundle extras = new Bundle();
extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
final TestSwitchController controller = new TestSwitchController();
controller.setKey("123");
controller.setMetaData(new MetaData("category"));
mSwitchesProvider.addSwitchController(controller);
mSwitchesProvider.attachInfo(mContext, mProviderInfo);
final Bundle result = mSwitchesProvider.call(METHOD_GET_DYNAMIC_SUMMARY, "uri" , extras);
assertThat(result).isNull();
}
@Test
public void getDynamicSummary_implementInterface_shouldReturnSummary() {
final Bundle extras = new Bundle();
extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
final TestSwitchController controller = new TestDynamicSwitchController();
controller.setKey("123");
controller.setMetaData(new MetaData("category"));
mSwitchesProvider.addSwitchController(controller);
mSwitchesProvider.attachInfo(mContext, mProviderInfo);
final Bundle result = mSwitchesProvider.call(METHOD_GET_DYNAMIC_SUMMARY, "uri" , extras);
assertThat(result.getString(META_DATA_PREFERENCE_SUMMARY))
.isEqualTo(TestDynamicSwitchController.SUMMARY);
}
@Test
public void onCheckedChangedSuccess_shouldReturnNoError() {
final Bundle extras = new Bundle();
extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
final TestSwitchController controller = new TestSwitchController();
controller.setKey("123");
controller.setMetaData(new MetaData("category"));
mSwitchesProvider.addSwitchController(controller);
mSwitchesProvider.attachInfo(mContext, mProviderInfo);
final Bundle result = mSwitchesProvider.call(METHOD_ON_CHECKED_CHANGED, "uri" , extras);
assertThat(result.getBoolean(EXTRA_SWITCH_SET_CHECKED_ERROR)).isFalse();
}
@Test
public void onCheckedChangedFailed_shouldReturnErrorMessage() {
final Bundle extras = new Bundle();
extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
final TestSwitchController controller = new TestSwitchController();
controller.setKey("123");
controller.setMetaData(new MetaData("category"));
controller.setErrorMessage("error");
mSwitchesProvider.addSwitchController(controller);
mSwitchesProvider.attachInfo(mContext, mProviderInfo);
final Bundle result = mSwitchesProvider.call(METHOD_ON_CHECKED_CHANGED, "uri" , extras);
assertThat(result.getBoolean(EXTRA_SWITCH_SET_CHECKED_ERROR)).isTrue();
assertThat(result.getString(EXTRA_SWITCH_SET_CHECKED_ERROR_MESSAGE)).isEqualTo("error");
}
private class TestSwitchesProvider extends SwitchesProvider {
private List<SwitchController> mControllers;
@Override
protected List<SwitchController> createSwitchControllers() {
return mControllers;
}
void addSwitchController(SwitchController controller) {
if (mControllers == null) {
mControllers = new ArrayList<>();
}
mControllers.add(controller);
}
}
private static class TestSwitchController extends SwitchController {
private String mKey;
private MetaData mMetaData;
private boolean mChecked;
private String mErrorMsg;
@Override
public String getSwitchKey() {
return mKey;
}
@Override
protected MetaData getMetaData() {
return mMetaData;
}
@Override
protected boolean isChecked() {
return mChecked;
}
@Override
protected boolean onCheckedChanged(boolean checked) {
return mErrorMsg == null ? true : false;
}
@Override
protected String getErrorMessage(boolean attemptedChecked) {
return mErrorMsg;
}
void setKey(String key) {
mKey = key;
}
void setMetaData(MetaData metaData) {
mMetaData = metaData;
}
void setChecked(boolean checked) {
mChecked = checked;
}
void setErrorMessage(String errorMsg) {
mErrorMsg = errorMsg;
}
}
private static class TestDynamicSwitchController extends TestSwitchController
implements ProviderIcon, DynamicTitle, DynamicSummary {
static final String TITLE = "title";
static final String SUMMARY = "summary";
static final Bundle ICON_BUNDLE = new Bundle();
@Override
public Bundle getProviderIcon() {
return ICON_BUNDLE;
}
@Override
public String getDynamicTitle() {
return TITLE;
}
@Override
public String getDynamicSummary() {
return SUMMARY;
}
}
}

View File

@@ -0,0 +1,527 @@
/*
* 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.
*/
package com.android.settingslib.drawer;
import static com.android.settingslib.drawer.TileUtils.IA_SETTINGS_ACTION;
import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_PENDING_INTENT;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY_URI;
import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
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.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.RuntimeEnvironment.application;
import android.app.ActivityManager;
import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings.Global;
import android.util.ArrayMap;
import android.util.Pair;
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 org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = TileUtilsTest.ShadowTileUtils.class)
public class TileUtilsTest {
private Context mContext;
@Mock
private PackageManager mPackageManager;
@Mock
private Resources mResources;
@Mock
private UserManager mUserManager;
@Mock
private ContentResolver mContentResolver;
@Mock
private Context mUserContext;
@Mock
private ContentResolver mUserContentResolver;
private static final String URI_GET_SUMMARY = "content://authority/text/summary";
private static final String URI_GET_ICON = "content://authority/icon/my_icon";
@Before
public void setUp() throws NameNotFoundException {
mContext = spy(application);
MockitoAnnotations.initMocks(this);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.getResourcesForApplication(anyString())).thenReturn(mResources);
when(mPackageManager.getResourcesForApplication((String) isNull())).thenReturn(mResources);
when(mPackageManager.getApplicationInfo(eq("abc"), anyInt()))
.thenReturn(application.getApplicationInfo());
mContentResolver = spy(application.getContentResolver());
when(mContext.getContentResolver()).thenReturn(mContentResolver);
when(mContext.getPackageName()).thenReturn("com.android.settings");
when(mUserContext.getContentResolver()).thenReturn(mUserContentResolver);
ShadowTileUtils.sCallRealEntryDataFromProvider = false;
}
@Test
public void getTilesForIntent_shouldParseCategory() {
final String testCategory = "category1";
Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
List<Tile> outTiles = new ArrayList<>();
List<ResolveInfo> info = new ArrayList<>();
info.add(newInfo(true, testCategory));
when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt()))
.thenReturn(info);
when(mPackageManager.queryIntentContentProvidersAsUser(any(Intent.class), anyInt(),
anyInt())).thenReturn(info);
TileUtils.loadTilesForAction(mContext, UserHandle.CURRENT, IA_SETTINGS_ACTION, addedCache,
null /* defaultCategory */, outTiles, false /* usePriority */);
assertThat(outTiles).hasSize(2);
assertThat(outTiles.get(0).getCategory()).isEqualTo(testCategory);
assertThat(outTiles.get(1).getCategory()).isEqualTo(testCategory);
}
@Test
public void getTilesForIntent_shouldParseKeyHintForSystemApp() {
String keyHint = "key";
Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
List<Tile> outTiles = new ArrayList<>();
List<ResolveInfo> info = new ArrayList<>();
ResolveInfo resolveInfo = newInfo(true, null /* category */, keyHint);
info.add(resolveInfo);
when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt()))
.thenReturn(info);
when(mPackageManager.queryIntentContentProvidersAsUser(any(Intent.class), anyInt(),
anyInt())).thenReturn(info);
TileUtils.loadTilesForAction(mContext, UserHandle.CURRENT, IA_SETTINGS_ACTION, addedCache,
null /* defaultCategory */, outTiles, false /* requiresSettings */);
assertThat(outTiles).hasSize(2);
assertThat(outTiles.get(0).getKey(mContext)).isEqualTo(keyHint);
assertThat(outTiles.get(1).getKey(mContext)).isEqualTo(keyHint);
}
@Test
public void getTilesForIntent_shouldSkipNonSystemApp() {
final String testCategory = "category1";
Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
List<Tile> outTiles = new ArrayList<>();
List<ResolveInfo> info = new ArrayList<>();
info.add(newInfo(false, testCategory));
when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt()))
.thenReturn(info);
when(mPackageManager.queryIntentContentProvidersAsUser(any(Intent.class), anyInt(),
anyInt())).thenReturn(info);
TileUtils.loadTilesForAction(mContext, UserHandle.CURRENT, IA_SETTINGS_ACTION,
addedCache, null /* defaultCategory */, outTiles, false /* requiresSettings */);
assertThat(outTiles).isEmpty();
}
@Test
public void getCategories_withPackageName() {
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
Map<Pair<String, String>, Tile> cache = new ArrayMap<>();
Global.putInt(mContext.getContentResolver(), Global.DEVICE_PROVISIONED, 1);
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
List<UserHandle> userHandleList = new ArrayList<>();
userHandleList.add(new UserHandle(ActivityManager.getCurrentUser()));
when(mUserManager.getUserProfiles()).thenReturn(userHandleList);
TileUtils.getCategories(mContext, cache);
verify(mPackageManager, atLeastOnce()).queryIntentActivitiesAsUser(
intentCaptor.capture(), anyInt(), anyInt());
verify(mPackageManager, atLeastOnce()).queryIntentContentProvidersAsUser(
intentCaptor.capture(), anyInt(), anyInt());
assertThat(intentCaptor.getAllValues().get(0).getPackage())
.isEqualTo(TileUtils.SETTING_PKG);
}
@Test
public void getTilesForIntent_shouldReadMetadataTitleAsString() {
Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
List<Tile> outTiles = new ArrayList<>();
List<ResolveInfo> info = new ArrayList<>();
ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON,
URI_GET_SUMMARY, "my title", 0, PROFILE_ALL);
info.add(resolveInfo);
when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt()))
.thenReturn(info);
when(mPackageManager.queryIntentContentProvidersAsUser(any(Intent.class), anyInt(),
anyInt())).thenReturn(info);
TileUtils.loadTilesForAction(mContext, UserHandle.CURRENT, IA_SETTINGS_ACTION, addedCache,
null /* defaultCategory */, outTiles, false /* usePriority */);
assertThat(outTiles).hasSize(2);
assertThat(outTiles.get(0).getTitle(mContext)).isEqualTo("my title");
assertThat(outTiles.get(1).getTitle(mContext)).isEqualTo("my title");
}
@Test
public void getTilesForIntent_shouldReadMetadataTitleFromResource() {
Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
List<Tile> outTiles = new ArrayList<>();
List<ResolveInfo> info = new ArrayList<>();
ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON,
URI_GET_SUMMARY, null, 123, PROFILE_ALL);
info.add(resolveInfo);
when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt()))
.thenReturn(info);
when(mPackageManager.queryIntentContentProvidersAsUser(any(Intent.class), anyInt(),
anyInt())).thenReturn(info);
when(mResources.getString(eq(123)))
.thenReturn("my localized title");
TileUtils.loadTilesForAction(mContext, UserHandle.CURRENT, IA_SETTINGS_ACTION, addedCache,
null /* defaultCategory */, outTiles, false /* usePriority */);
assertThat(outTiles).hasSize(2);
assertThat(outTiles.get(0).getTitle(mContext)).isEqualTo("my localized title");
assertThat(outTiles.get(1).getTitle(mContext)).isEqualTo("my localized title");
}
@Test
public void getTilesForIntent_shouldNotTintIconIfInSettingsPackage() {
Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
List<Tile> outTiles = new ArrayList<>();
List<ResolveInfo> info = new ArrayList<>();
ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON,
URI_GET_SUMMARY, null, 123, PROFILE_ALL);
resolveInfo.activityInfo.packageName = "com.android.settings";
resolveInfo.activityInfo.applicationInfo.packageName = "com.android.settings";
info.add(resolveInfo);
when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt()))
.thenReturn(info);
when(mPackageManager.queryIntentContentProvidersAsUser(any(Intent.class), anyInt(),
anyInt())).thenReturn(info);
TileUtils.loadTilesForAction(mContext, UserHandle.CURRENT, IA_SETTINGS_ACTION, addedCache,
null /* defaultCategory */, outTiles, false /* usePriority */);
assertThat(outTiles.get(0).isIconTintable(mContext)).isFalse();
assertThat(outTiles.get(1).isIconTintable(mContext)).isFalse();
}
@Test
public void getTilesForIntent_tileAlreadyInCache_shouldUpdateMetaData() {
final Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
final List<Tile> outTiles = new ArrayList<>();
final List<ResolveInfo> info = new ArrayList<>();
final ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON,
URI_GET_SUMMARY, null, 123, PROFILE_ALL);
resolveInfo.activityInfo.packageName = "com.android.settings";
resolveInfo.activityInfo.applicationInfo.packageName = "com.android.settings";
info.add(resolveInfo);
when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt()))
.thenReturn(info);
TileUtils.loadTilesForAction(mContext, UserHandle.CURRENT, IA_SETTINGS_ACTION, addedCache,
null /* defaultCategory */, outTiles, false /* usePriority */);
assertThat(outTiles).hasSize(1);
final Bundle oldMetadata = outTiles.get(0).getMetaData();
resolveInfo.activityInfo.metaData = new Bundle(oldMetadata);
resolveInfo.activityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON,
com.android.internal.R.drawable.ic_phone);
outTiles.clear();
TileUtils.loadTilesForAction(mContext, UserHandle.CURRENT, IA_SETTINGS_ACTION, addedCache,
null /* defaultCategory */, outTiles, false /* usePriority */);
assertThat(outTiles).hasSize(1);
final Bundle newMetaData = outTiles.get(0).getMetaData();
assertThat(newMetaData).isNotSameInstanceAs(oldMetadata);
}
@Test
public void getTilesForIntent_shouldMarkIconTintableIfMetadataSet() {
Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
List<Tile> outTiles = new ArrayList<>();
List<ResolveInfo> info = new ArrayList<>();
ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON,
URI_GET_SUMMARY, null, 123, PROFILE_ALL);
resolveInfo.activityInfo.metaData
.putBoolean(TileUtils.META_DATA_PREFERENCE_ICON_TINTABLE, true);
info.add(resolveInfo);
when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt()))
.thenReturn(info);
when(mPackageManager.queryIntentContentProvidersAsUser(any(Intent.class), anyInt(),
anyInt())).thenReturn(info);
TileUtils.loadTilesForAction(mContext, UserHandle.CURRENT, IA_SETTINGS_ACTION, addedCache,
null /* defaultCategory */, outTiles, false /* usePriority */);
assertThat(outTiles.get(0).isIconTintable(mContext)).isTrue();
assertThat(outTiles.get(1).isIconTintable(mContext)).isTrue();
}
@Test
public void getTilesForIntent_shouldProcessUriContentForSystemApp() {
Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
List<Tile> outTiles = new ArrayList<>();
List<ResolveInfo> info = new ArrayList<>();
ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON,
URI_GET_SUMMARY);
info.add(resolveInfo);
when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt()))
.thenReturn(info);
when(mPackageManager.queryIntentContentProvidersAsUser(any(Intent.class), anyInt(),
anyInt())).thenReturn(info);
TileUtils.loadTilesForAction(mContext, UserHandle.CURRENT, IA_SETTINGS_ACTION, addedCache,
null /* defaultCategory */, outTiles, false /* usePriority */);
assertThat(outTiles).hasSize(2);
}
@Test
public void loadTilesForAction_isPrimaryProfileOnly_shouldSkipNonPrimaryUserTiles() {
Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
List<Tile> outTiles = new ArrayList<>();
List<ResolveInfo> info = new ArrayList<>();
ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON,
URI_GET_SUMMARY, null, 123, PROFILE_PRIMARY);
info.add(resolveInfo);
when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt()))
.thenReturn(info);
when(mPackageManager.queryIntentContentProvidersAsUser(any(Intent.class), anyInt(),
anyInt())).thenReturn(info);
TileUtils.loadTilesForAction(mContext, new UserHandle(10), IA_SETTINGS_ACTION,
addedCache, null /* defaultCategory */, outTiles, false /* requiresSettings */);
assertThat(outTiles).isEmpty();
}
@Test
public void loadTilesForAction_multipleUserProfiles_updatesUserHandle() {
Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
List<Tile> outTiles = new ArrayList<>();
List<ResolveInfo> info = new ArrayList<>();
ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON,
URI_GET_SUMMARY, null, 123, PROFILE_ALL);
info.add(resolveInfo);
when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt()))
.thenReturn(info);
TileUtils.loadTilesForAction(mContext, UserHandle.CURRENT, IA_SETTINGS_ACTION,
addedCache, null /* defaultCategory */, outTiles, false /* requiresSettings */);
TileUtils.loadTilesForAction(mContext, new UserHandle(10), IA_SETTINGS_ACTION,
addedCache, null /* defaultCategory */, outTiles, false /* requiresSettings */);
assertThat(outTiles).hasSize(1);
assertThat(outTiles.get(0).userHandle)
.containsExactly(UserHandle.CURRENT, new UserHandle(10));
}
@Test
public void loadTilesForAction_forUserProvider_getEntryDataFromProvider_inContextOfGivenUser() {
ShadowTileUtils.sCallRealEntryDataFromProvider = true;
UserHandle userHandle = new UserHandle(10);
doReturn(mUserContext).when(mContext).createContextAsUser(eq(userHandle), anyInt());
Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
List<Tile> outTiles = new ArrayList<>();
List<ResolveInfo> info = new ArrayList<>();
ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON,
URI_GET_SUMMARY, null, 123, PROFILE_ALL);
info.add(resolveInfo);
when(mPackageManager.queryIntentContentProvidersAsUser(any(Intent.class), anyInt(),
anyInt())).thenReturn(info);
TileUtils.loadTilesForAction(mContext, userHandle, IA_SETTINGS_ACTION,
addedCache, null /* defaultCategory */, outTiles, false /* requiresSettings */);
verify(mUserContentResolver, atLeastOnce())
.acquireUnstableProvider(any(Uri.class));
}
@Test
public void loadTilesForAction_withPendingIntent_updatesPendingIntentMap() {
Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
List<Tile> outTiles = new ArrayList<>();
List<ResolveInfo> info = new ArrayList<>();
ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON,
URI_GET_SUMMARY, null, 123, PROFILE_ALL);
PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
resolveInfo.activityInfo.metaData
.putParcelable(META_DATA_PREFERENCE_PENDING_INTENT, pendingIntent);
info.add(resolveInfo);
when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt()))
.thenReturn(info);
TileUtils.loadTilesForAction(mContext, UserHandle.CURRENT, IA_SETTINGS_ACTION,
addedCache, null /* defaultCategory */, outTiles, false /* requiresSettings */);
TileUtils.loadTilesForAction(mContext, new UserHandle(10), IA_SETTINGS_ACTION,
addedCache, null /* defaultCategory */, outTiles, false /* requiresSettings */);
assertThat(outTiles).hasSize(1);
assertThat(outTiles.get(0).pendingIntentMap).containsExactly(
UserHandle.CURRENT, pendingIntent, new UserHandle(10), pendingIntent);
}
public static ResolveInfo newInfo(boolean systemApp, String category) {
return newInfo(systemApp, category, null);
}
private static ResolveInfo newInfo(boolean systemApp, String category, String keyHint) {
return newInfo(systemApp, category, keyHint, null, null);
}
private static ResolveInfo newInfo(boolean systemApp, String category, String keyHint,
String iconUri, String summaryUri) {
return newInfo(systemApp, category, keyHint, iconUri, summaryUri, null, 0, PROFILE_ALL);
}
private static ResolveInfo newInfo(boolean systemApp, String category, String keyHint,
String iconUri, String summaryUri, String title, int titleResId, String profile) {
final Bundle metaData = newMetaData(category, keyHint, iconUri, summaryUri, title,
titleResId, profile);
final ResolveInfo info = new ResolveInfo();
info.system = systemApp;
info.activityInfo = new ActivityInfo();
info.activityInfo.packageName = "abc";
info.activityInfo.name = "123";
info.activityInfo.metaData = metaData;
info.activityInfo.applicationInfo = new ApplicationInfo();
info.providerInfo = new ProviderInfo();
info.providerInfo.packageName = "abc";
info.providerInfo.name = "456";
info.providerInfo.authority = "auth";
info.providerInfo.metaData = metaData;
ShadowTileUtils.setMetaData(metaData);
info.providerInfo.applicationInfo = new ApplicationInfo();
if (systemApp) {
info.activityInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
info.providerInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
}
return info;
}
private static Bundle newMetaData(String category, String keyHint, String iconUri,
String summaryUri, String title, int titleResId, String profile) {
final Bundle metaData = new Bundle();
metaData.putString("com.android.settings.category", category);
metaData.putInt(META_DATA_PREFERENCE_ICON, 314159);
metaData.putString(META_DATA_PREFERENCE_SUMMARY, "static-summary");
if (keyHint != null) {
metaData.putString(META_DATA_PREFERENCE_KEYHINT, keyHint);
}
if (iconUri != null) {
metaData.putString(META_DATA_PREFERENCE_ICON_URI, iconUri);
}
if (summaryUri != null) {
metaData.putString(META_DATA_PREFERENCE_SUMMARY_URI, summaryUri);
}
if (titleResId != 0) {
metaData.putInt(TileUtils.META_DATA_PREFERENCE_TITLE, titleResId);
} else if (title != null) {
metaData.putString(TileUtils.META_DATA_PREFERENCE_TITLE, title);
}
if (profile != null) {
metaData.putString(META_DATA_KEY_PROFILE, profile);
}
return metaData;
}
@Implements(TileUtils.class)
static class ShadowTileUtils {
private static Bundle sMetaData;
private static boolean sCallRealEntryDataFromProvider;
@Implementation
protected static List<Bundle> getEntryDataFromProvider(Context context, String authority) {
if (sCallRealEntryDataFromProvider) {
return Shadow.directlyOn(
TileUtils.class,
"getEntryDataFromProvider",
ReflectionHelpers.ClassParameter.from(Context.class, context),
ReflectionHelpers.ClassParameter.from(String.class, authority));
}
return Arrays.asList(sMetaData);
}
private static void setMetaData(Bundle metaData) {
sMetaData = metaData;
}
}
}

View File

@@ -0,0 +1,182 @@
/*
* 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.settingslib.dream;
import static com.android.settingslib.dream.DreamBackend.COMPLICATION_TYPE_DATE;
import static com.android.settingslib.dream.DreamBackend.COMPLICATION_TYPE_HOME_CONTROLS;
import static com.android.settingslib.dream.DreamBackend.COMPLICATION_TYPE_TIME;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.provider.Settings;
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 org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowSettings;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowSettings.ShadowSecure.class})
public final class DreamBackendTest {
private static final int[] SUPPORTED_DREAM_COMPLICATIONS =
{COMPLICATION_TYPE_HOME_CONTROLS, COMPLICATION_TYPE_DATE,
COMPLICATION_TYPE_TIME};
private static final List<Integer> SUPPORTED_DREAM_COMPLICATIONS_LIST = Arrays.stream(
SUPPORTED_DREAM_COMPLICATIONS).boxed().collect(
Collectors.toList());
@Mock
private Context mContext;
@Mock
private ContentResolver mMockResolver;
private DreamBackend mBackend;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mContext.getApplicationContext()).thenReturn(mContext);
when(mContext.getContentResolver()).thenReturn(mMockResolver);
final Resources res = mock(Resources.class);
when(mContext.getResources()).thenReturn(res);
when(res.getIntArray(
com.android.internal.R.array.config_supportedDreamComplications)).thenReturn(
SUPPORTED_DREAM_COMPLICATIONS);
when(res.getStringArray(
com.android.internal.R.array.config_disabledDreamComponents)).thenReturn(
new String[]{});
when(res.getStringArray(
com.android.internal.R.array.config_loggable_dream_prefixes)).thenReturn(
new String[]{});
mBackend = new DreamBackend(mContext);
}
@After
public void tearDown() {
ShadowSettings.ShadowSecure.reset();
}
@Test
public void testComplicationsEnabledByDefault() {
setControlsEnabledOnLockscreen(true);
assertThat(mBackend.getComplicationsEnabled()).isTrue();
assertThat(mBackend.getEnabledComplications()).containsExactlyElementsIn(
SUPPORTED_DREAM_COMPLICATIONS_LIST);
}
@Test
public void testEnableComplicationExplicitly() {
setControlsEnabledOnLockscreen(true);
mBackend.setComplicationsEnabled(true);
assertThat(mBackend.getEnabledComplications()).containsExactlyElementsIn(
SUPPORTED_DREAM_COMPLICATIONS_LIST);
assertThat(mBackend.getComplicationsEnabled()).isTrue();
}
@Test
public void testDisableComplications() {
setControlsEnabledOnLockscreen(true);
mBackend.setComplicationsEnabled(false);
assertThat(mBackend.getEnabledComplications())
.containsExactly(COMPLICATION_TYPE_HOME_CONTROLS);
assertThat(mBackend.getComplicationsEnabled()).isFalse();
}
@Test
public void testHomeControlsDisabled_ComplicationsEnabled() {
setControlsEnabledOnLockscreen(true);
mBackend.setComplicationsEnabled(true);
mBackend.setHomeControlsEnabled(false);
// Home controls should not be enabled, only date and time.
final List<Integer> enabledComplications =
Arrays.asList(COMPLICATION_TYPE_DATE, COMPLICATION_TYPE_TIME);
assertThat(mBackend.getEnabledComplications())
.containsExactlyElementsIn(enabledComplications);
}
@Test
public void testHomeControlsDisabled_ComplicationsDisabled() {
setControlsEnabledOnLockscreen(true);
mBackend.setComplicationsEnabled(false);
mBackend.setHomeControlsEnabled(false);
assertThat(mBackend.getEnabledComplications()).isEmpty();
}
@Test
public void testHomeControlsEnabled_ComplicationsDisabled() {
setControlsEnabledOnLockscreen(true);
mBackend.setComplicationsEnabled(false);
mBackend.setHomeControlsEnabled(true);
final List<Integer> enabledComplications =
Collections.singletonList(COMPLICATION_TYPE_HOME_CONTROLS);
assertThat(mBackend.getEnabledComplications())
.containsExactlyElementsIn(enabledComplications);
}
@Test
public void testHomeControlsEnabled_ComplicationsEnabled() {
setControlsEnabledOnLockscreen(true);
mBackend.setComplicationsEnabled(true);
mBackend.setHomeControlsEnabled(true);
final List<Integer> enabledComplications =
Arrays.asList(
COMPLICATION_TYPE_HOME_CONTROLS,
COMPLICATION_TYPE_DATE,
COMPLICATION_TYPE_TIME
);
assertThat(mBackend.getEnabledComplications())
.containsExactlyElementsIn(enabledComplications);
}
@Test
public void testHomeControlsEnabled_lockscreenDisabled() {
setControlsEnabledOnLockscreen(false);
mBackend.setComplicationsEnabled(true);
mBackend.setHomeControlsEnabled(true);
// Home controls should not be enabled, only date and time.
final List<Integer> enabledComplications =
Arrays.asList(
COMPLICATION_TYPE_DATE,
COMPLICATION_TYPE_TIME
);
assertThat(mBackend.getEnabledComplications())
.containsExactlyElementsIn(enabledComplications);
}
private void setControlsEnabledOnLockscreen(boolean enabled) {
Settings.Secure.putInt(
mContext.getContentResolver(),
Settings.Secure.LOCKSCREEN_SHOW_CONTROLS,
enabled ? 1 : 0);
}
}

View File

@@ -0,0 +1,156 @@
/*
* 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.settingslib.emergencynumber;
import static android.telephony.emergency.EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE;
import static com.android.settingslib.emergencynumber.EmergencyNumberUtils.EMERGENCY_GESTURE_CALL_NUMBER;
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.anyString;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.emergency.EmergencyNumber;
import android.util.ArrayMap;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@RunWith(RobolectricTestRunner.class)
public class EmergencyNumberUtilsTest {
private static final String TELEPHONY_EMERGENCY_NUMBER = "1234";
private static final String USER_OVERRIDE_EMERGENCY_NUMBER = "5678";
@Mock
private Context mContext;
@Mock
private PackageManager mPackageManager;
@Mock
private TelephonyManager mTelephonyManager;
@Mock
private ContentResolver mContentResolver;
@Mock
private CarrierConfigManager mCarrierConfigManager;
private EmergencyNumberUtils mUtils;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mContext.getContentResolver()).thenReturn(mContentResolver);
when(mContext.getSystemService(CarrierConfigManager.class)).thenReturn(
mCarrierConfigManager);
}
@Test
public void getDefaultPoliceNumber_noTelephony_shouldReturnDefault() {
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(false);
mUtils = new EmergencyNumberUtils(mContext);
assertThat(mUtils.getDefaultPoliceNumber()).isEqualTo(
EmergencyNumberUtils.FALL_BACK_NUMBER);
}
@Test
public void getDefaultPoliceNumber_hasTelephony_shouldLoadFromTelephony() {
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true);
when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
addEmergencyNumberToTelephony(TELEPHONY_EMERGENCY_NUMBER);
mUtils = new EmergencyNumberUtils(mContext);
assertThat(mUtils.getDefaultPoliceNumber()).isEqualTo(TELEPHONY_EMERGENCY_NUMBER);
}
@Test
public void getPoliceNumber_hasUserOverride_shouldLoadFromUserOverride() {
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true);
when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
addEmergencyNumberToTelephony(TELEPHONY_EMERGENCY_NUMBER);
Bundle bundle = new Bundle();
bundle.putString(EMERGENCY_GESTURE_CALL_NUMBER, USER_OVERRIDE_EMERGENCY_NUMBER);
when(mContentResolver.call(any(Uri.class), anyString(), nullable(String.class), nullable(
Bundle.class))).thenReturn(bundle);
mUtils = new EmergencyNumberUtils(mContext);
assertThat(mUtils.getPoliceNumber()).isEqualTo(USER_OVERRIDE_EMERGENCY_NUMBER);
}
@Test
public void getPoliceNumber_noUserOverride_shouldLoadFromTelephony() {
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true);
when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
addEmergencyNumberToTelephony(TELEPHONY_EMERGENCY_NUMBER);
mUtils = new EmergencyNumberUtils(mContext);
assertThat(mUtils.getPoliceNumber()).isEqualTo(TELEPHONY_EMERGENCY_NUMBER);
}
@Test
public void getPoliceNumber_hasCarrierPrefix_shouldRemovePrefix() {
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true);
when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
final String prefix = "*272";
final PersistableBundle bundle = new PersistableBundle();
bundle.putStringArray(CarrierConfigManager.KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY,
new String[]{prefix});
when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
addEmergencyNumberToTelephony(prefix + TELEPHONY_EMERGENCY_NUMBER);
mUtils = new EmergencyNumberUtils(mContext);
assertThat(mUtils.getPoliceNumber()).isEqualTo(TELEPHONY_EMERGENCY_NUMBER);
}
private void addEmergencyNumberToTelephony(String number) {
final int subId = SubscriptionManager.getDefaultSubscriptionId();
EmergencyNumber emergencyNumber = mock(EmergencyNumber.class);
when(emergencyNumber.isInEmergencyServiceCategories(EMERGENCY_SERVICE_CATEGORY_POLICE))
.thenReturn(true);
Map<Integer, List<EmergencyNumber>> numbers = new ArrayMap<>();
List<EmergencyNumber> numbersForSubId = new ArrayList<>();
numbersForSubId.add(emergencyNumber);
numbers.put(subId, numbersForSubId);
when(mTelephonyManager.getEmergencyNumberList(anyInt())).thenReturn(numbers);
when(emergencyNumber.getNumber()).thenReturn(number);
}
}

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.settingslib.enterprise;
import static com.google.common.truth.Truth.assertWithMessage;
import android.content.ComponentName;
import android.content.Context;
import android.os.UserHandle;
import android.util.DebugUtils;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
/**
* Utils related to the action disabled by admin dialogs.
*/
// NOTE: must be public because of DebugUtils.constantToString() call
public final class ActionDisabledByAdminControllerTestUtils {
static final int ENFORCEMENT_ADMIN_USER_ID = 123;
static final UserHandle ENFORCEMENT_ADMIN_USER = UserHandle.of(ENFORCEMENT_ADMIN_USER_ID);
static final String SUPPORT_MESSAGE = "support message";
static final ComponentName ADMIN_COMPONENT =
new ComponentName("some.package.name", "some.package.name.SomeClass");
static final EnforcedAdmin ENFORCED_ADMIN = new EnforcedAdmin(
ADMIN_COMPONENT, UserHandle.of(ENFORCEMENT_ADMIN_USER_ID));
static final EnforcedAdmin ENFORCED_ADMIN_WITHOUT_COMPONENT = new EnforcedAdmin(
/* component= */ null, UserHandle.of(ENFORCEMENT_ADMIN_USER_ID));
static final String URL = "https://testexample.com";
// NOTE: fields below must be public because of DebugUtils.constantToString() call
public static final int LEARN_MORE_ACTION_NONE = 0;
public static final int LEARN_MORE_ACTION_SHOW_ADMIN_POLICIES = 1;
public static final int LEARN_MORE_ACTION_SHOW_ADMIN_SETTINGS = 2;
public static final int LEARN_MORE_ACTION_LAUNCH_HELP_PAGE = 3;
private int mLearnMoreButtonAction = LEARN_MORE_ACTION_NONE;
ActionDisabledLearnMoreButtonLauncher createLearnMoreButtonLauncher() {
return new ActionDisabledLearnMoreButtonLauncher() {
@Override
public void setLearnMoreButton(Runnable action) {
action.run();
}
@Override
protected void launchShowAdminPolicies(Context context, UserHandle user,
ComponentName admin) {
mLearnMoreButtonAction = LEARN_MORE_ACTION_SHOW_ADMIN_POLICIES;
}
@Override
protected void launchShowAdminSettings(Context context) {
mLearnMoreButtonAction = LEARN_MORE_ACTION_SHOW_ADMIN_SETTINGS;
}
@Override
public void showHelpPage(Context context, String url, UserHandle userHandle) {
mLearnMoreButtonAction = LEARN_MORE_ACTION_LAUNCH_HELP_PAGE;
}
@Override
protected boolean isSameProfileGroup(Context context, int enforcementAdminUserId) {
return true;
}
};
}
void assertLearnMoreAction(int learnMoreActionShowAdminPolicies) {
assertWithMessage("action").that(actionToString(mLearnMoreButtonAction))
.isEqualTo(actionToString(learnMoreActionShowAdminPolicies));
}
private static String actionToString(int action) {
return DebugUtils.constantToString(ActionDisabledByAdminControllerTestUtils.class,
"LEARN_MORE_ACTION_", action);
}
}

View File

@@ -0,0 +1,232 @@
/*
* 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.settingslib.enterprise;
import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ADMIN_COMPONENT;
import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ENFORCED_ADMIN;
import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ENFORCED_ADMIN_WITHOUT_COMPONENT;
import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ENFORCEMENT_ADMIN_USER;
import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ENFORCEMENT_ADMIN_USER_ID;
import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.URL;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.UserHandle;
import android.os.UserManager;
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.Spy;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)// NOTE: this test doesn't need RoboElectric...
public final class ActionDisabledLearnMoreButtonLauncherTest {
private static final int CONTEXT_USER_ID = -ENFORCEMENT_ADMIN_USER_ID;
private static final UserHandle CONTEXT_USER = new UserHandle(CONTEXT_USER_ID);
@Rule
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Mock
private Context mContext;
@Mock
private DevicePolicyManager mDevicePolicyManager;
@Mock
private UserManager mUserManager;
@Spy
private ActionDisabledLearnMoreButtonLauncher mLauncher;
@Captor
private ArgumentCaptor<Runnable> mLearnMoreActionCaptor;
@Captor
private ArgumentCaptor<Intent> mIntentCaptor;
@Before
public void setUp() {
when(mContext.getUserId()).thenReturn(CONTEXT_USER_ID);
when(mUserManager.getProcessUserId()).thenReturn(CONTEXT_USER_ID);
when(mContext.getSystemService(DevicePolicyManager.class)).thenReturn(mDevicePolicyManager);
when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
}
@Test
public void testSetupLearnMoreButtonToShowAdminPolicies_nullContext() {
assertThrows(NullPointerException.class,
() -> mLauncher.setupLearnMoreButtonToShowAdminPolicies(/* context= */ null,
ENFORCEMENT_ADMIN_USER_ID, ENFORCED_ADMIN));
}
@Test
public void testSetupLearnMoreButtonToShowAdminPolicies_nullEnforcedAdmin() {
assertThrows(NullPointerException.class,
() -> mLauncher.setupLearnMoreButtonToShowAdminPolicies(/* context= */ null,
ENFORCEMENT_ADMIN_USER_ID, /* enforcedAdmin= */ null));
}
@Test
public void testSetupLearnMoreButtonToShowAdminPolicies_differentProfileGroup_noDeviceOwner() {
mockDifferentProfileGroup();
mockEnforcementAdminIsNotDeviceOwner();
mLauncher.setupLearnMoreButtonToShowAdminPolicies(mContext, ENFORCEMENT_ADMIN_USER_ID,
ENFORCED_ADMIN);
verify(mLauncher, never()).setLearnMoreButton(any());
}
@Test
public void testSetupLearnMoreButtonToShowAdminPolicies_differentGroup_noSystemDeviceOwner() {
mockDifferentProfileGroup();
mockDeviceOwner(ENFORCEMENT_ADMIN_USER_ID);
mLauncher.setupLearnMoreButtonToShowAdminPolicies(mContext, ENFORCEMENT_ADMIN_USER_ID,
ENFORCED_ADMIN);
verify(mLauncher, never()).setLearnMoreButton(any());
}
@Test
public void testSetupLearnMoreButtonToShowAdminPolicies_differentGroup_systemDeviceOwner() {
mockDifferentProfileGroup();
mockDeviceOwner(UserHandle.USER_SYSTEM);
mLauncher.setupLearnMoreButtonToShowAdminPolicies(mContext, UserHandle.USER_SYSTEM,
ENFORCED_ADMIN_WITHOUT_COMPONENT);
tapLearnMore();
verify(mLauncher, never()).launchShowAdminPolicies(any(), any(), any());
verify(mLauncher).launchShowAdminSettings(mContext);
verifyFinishSelf();
}
@Test
public void testSetupLearnMoreButtonToShowAdminPolicies_sameProfileGroup_noDeviceOwner() {
mockSameProfileGroup();
mockEnforcementAdminIsNotDeviceOwner();
mLauncher.setupLearnMoreButtonToShowAdminPolicies(mContext, ENFORCEMENT_ADMIN_USER_ID,
ENFORCED_ADMIN_WITHOUT_COMPONENT);
tapLearnMore();
verify(mLauncher, never()).launchShowAdminPolicies(any(), any(), any());
verify(mLauncher).launchShowAdminSettings(mContext);
verifyFinishSelf();
}
@Test
public void testSetupLearnMoreButtonToShowAdminPolicies_sameProfileGroup_noSystemDeviceOwner() {
mockSameProfileGroup();
mockDeviceOwner(ENFORCEMENT_ADMIN_USER_ID);
mLauncher.setupLearnMoreButtonToShowAdminPolicies(mContext, ENFORCEMENT_ADMIN_USER_ID,
ENFORCED_ADMIN_WITHOUT_COMPONENT);
tapLearnMore();
verify(mLauncher, never()).launchShowAdminPolicies(any(), any(), any());
verify(mLauncher).launchShowAdminSettings(mContext);
verifyFinishSelf();
}
@Test
public void testSetupLearnMoreButtonToShowAdminPolicies_showsLearnMoreButton_withComponent() {
mockSameProfileGroup();
mockEnforcementAdminIsNotDeviceOwner();
mLauncher.setupLearnMoreButtonToShowAdminPolicies(mContext, ENFORCEMENT_ADMIN_USER_ID,
ENFORCED_ADMIN);
tapLearnMore();
verify(mLauncher).launchShowAdminPolicies(mContext, ENFORCEMENT_ADMIN_USER,
ADMIN_COMPONENT);
verify(mLauncher, never()).launchShowAdminSettings(any());
verifyFinishSelf();
}
@Test
public void testSetupLearnMoreButtonToLaunchHelpPage_nullContext() {
assertThrows(NullPointerException.class,
() -> mLauncher.setupLearnMoreButtonToLaunchHelpPage(
/* context= */ null, URL, CONTEXT_USER));
}
@Test
public void testSetupLearnMoreButtonToLaunchHelpPage_nullUrl() {
assertThrows(NullPointerException.class,
() -> mLauncher.setupLearnMoreButtonToLaunchHelpPage(
mContext, /* url= */ null, CONTEXT_USER));
}
@Test
public void testSetupLearnMoreButtonToLaunchHelpPage() {
mLauncher.setupLearnMoreButtonToLaunchHelpPage(mContext, URL, CONTEXT_USER);
tapLearnMore();
verify(mContext).startActivityAsUser(mIntentCaptor.capture(), eq(CONTEXT_USER));
Intent intent = mIntentCaptor.getValue();
assertWithMessage("wrong url on intent %s", intent).that(intent.getData())
.isEqualTo(Uri.parse(URL));
verifyFinishSelf();
}
private void mockDifferentProfileGroup() {
// No need to mock anything - isSameProfileGroup() will return false by default
}
private void mockSameProfileGroup() {
when(mUserManager.isSameProfileGroup(ENFORCEMENT_ADMIN_USER_ID, CONTEXT_USER_ID))
.thenReturn(true);
}
private void mockEnforcementAdminIsNotDeviceOwner() {
when(mDevicePolicyManager.getDeviceOwnerUserId()).thenReturn(ENFORCEMENT_ADMIN_USER_ID + 1);
}
private void mockDeviceOwner(int userId) {
when(mDevicePolicyManager.getDeviceOwnerUserId()).thenReturn(userId);
}
private void tapLearnMore() {
verify(mLauncher).setLearnMoreButton(mLearnMoreActionCaptor.capture());
mLearnMoreActionCaptor.getValue().run();
}
private void verifyFinishSelf() {
verify(mLauncher).finishSelf();
}
}

View File

@@ -0,0 +1,87 @@
/*
* 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.settingslib.enterprise;
import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ENFORCED_ADMIN;
import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ENFORCEMENT_ADMIN_USER_ID;
import static com.android.settingslib.enterprise.FakeDeviceAdminStringProvider.DEFAULT_DEVICE_ADMIN_STRING_PROVIDER;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.TestCase.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.UserHandle;
import android.provider.Settings;
import com.android.settingslib.RestrictedLockUtils;
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 org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class BiometricActionDisabledByAdminControllerTest {
@Mock
private Context mContext;
private ActionDisabledByAdminControllerTestUtils mTestUtils;
private BiometricActionDisabledByAdminController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mTestUtils = new ActionDisabledByAdminControllerTestUtils();
mController = new BiometricActionDisabledByAdminController(
DEFAULT_DEVICE_ADMIN_STRING_PROVIDER);
mController.initialize(mTestUtils.createLearnMoreButtonLauncher());
mController.updateEnforcedAdmin(ENFORCED_ADMIN, ENFORCEMENT_ADMIN_USER_ID);
}
@Test
public void buttonClicked() {
ComponentName componentName = new ComponentName("com.android.test", "AThing");
RestrictedLockUtils.EnforcedAdmin enforcedAdmin = new RestrictedLockUtils.EnforcedAdmin(
componentName, new UserHandle(UserHandle.myUserId()));
DialogInterface.OnClickListener listener =
mController.getPositiveButtonListener(mContext, enforcedAdmin);
assertNotNull("Biometric Controller must supply a non-null listener", listener);
listener.onClick(mock(DialogInterface.class), 0 /* which */);
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext).startActivity(intentCaptor.capture());
assertEquals(Settings.ACTION_MANAGE_SUPERVISOR_RESTRICTED_SETTING,
intentCaptor.getValue().getAction());
assertEquals(Settings.SUPERVISOR_VERIFICATION_SETTING_BIOMETRICS,
intentCaptor.getValue().getIntExtra(
Settings.EXTRA_SUPERVISOR_RESTRICTED_SETTING_KEY, -1));
assertEquals(componentName.getPackageName(), intentCaptor.getValue().getPackage());
}
}

View File

@@ -0,0 +1,110 @@
/*
* 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.settingslib.enterprise;
import androidx.annotation.Nullable;
class FakeDeviceAdminStringProvider implements DeviceAdminStringProvider {
static final String DEFAULT_DISABLED_BY_POLICY_TITLE = "default_disabled_by_policy_title";
static final String DISALLOW_ADJUST_VOLUME_TITLE = "disallow_adjust_volume_title";
static final String DISALLOW_OUTGOING_CALLS_TITLE = "disallow_outgoing_calls_title";
static final String DISALLOW_SMS_TITLE = "disallow_sms_title";
static final String DISABLE_CAMERA_TITLE = "disable_camera_title";
static final String DISABLE_SCREEN_CAPTURE_TITLE = "disable_screen_capture_title";
static final String SUSPENDED_PACKAGES_TITLE = "suspended_packages_title";
static final String DEFAULT_DISABLED_BY_POLICY_CONTENT = "default_disabled_by_policy_content";
static final String DEFAULT_DISABLED_BY_POLICY_TITLE_FINANCED_DEVICE =
"default_disabled_by_policy_title_financed_device";
static final String DEFAULT_BIOMETRIC_TITLE = "biometric_title";
static final String DEFAULT_BIOMETRIC_CONTENTS = "biometric_contents";
static final String DISABLED_BY_PARENT_CONTENT = "disabled_by_parent_constent";
static final DeviceAdminStringProvider DEFAULT_DEVICE_ADMIN_STRING_PROVIDER =
new FakeDeviceAdminStringProvider(/* url = */ null);
private final String mUrl;
FakeDeviceAdminStringProvider(@Nullable String url) {
mUrl = url;
}
@Override
public String getDefaultDisabledByPolicyTitle() {
return DEFAULT_DISABLED_BY_POLICY_TITLE;
}
@Override
public String getDisallowAdjustVolumeTitle() {
return DISALLOW_ADJUST_VOLUME_TITLE;
}
@Override
public String getDisallowOutgoingCallsTitle() {
return DISALLOW_OUTGOING_CALLS_TITLE;
}
@Override
public String getDisallowSmsTitle() {
return DISALLOW_SMS_TITLE;
}
@Override
public String getDisableCameraTitle() {
return DISABLE_CAMERA_TITLE;
}
@Override
public String getDisableScreenCaptureTitle() {
return DISABLE_SCREEN_CAPTURE_TITLE;
}
@Override
public String getSuspendPackagesTitle() {
return SUSPENDED_PACKAGES_TITLE;
}
@Override
public String getDefaultDisabledByPolicyContent() {
return DEFAULT_DISABLED_BY_POLICY_CONTENT;
}
@Override
public String getLearnMoreHelpPageUrl() {
return mUrl;
}
@Override
public String getDisabledByPolicyTitleForFinancedDevice() {
return DEFAULT_DISABLED_BY_POLICY_TITLE_FINANCED_DEVICE;
}
@Override
public String getDisabledBiometricsParentConsentTitle() {
return DEFAULT_BIOMETRIC_TITLE;
}
@Override
public String getDisabledByParentContent() {
return DISABLED_BY_PARENT_CONTENT;
}
@Override
public String getDisabledBiometricsParentConsentContent() {
return DEFAULT_BIOMETRIC_CONTENTS;
}
}

View File

@@ -0,0 +1,83 @@
/*
* 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.settingslib.enterprise;
import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ENFORCED_ADMIN;
import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ENFORCEMENT_ADMIN_USER_ID;
import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.LEARN_MORE_ACTION_SHOW_ADMIN_POLICIES;
import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.SUPPORT_MESSAGE;
import static com.android.settingslib.enterprise.FakeDeviceAdminStringProvider.DEFAULT_DEVICE_ADMIN_STRING_PROVIDER;
import static com.android.settingslib.enterprise.FakeDeviceAdminStringProvider.DEFAULT_DISABLED_BY_POLICY_TITLE_FINANCED_DEVICE;
import static com.google.common.truth.Truth.assertThat;
import android.app.Activity;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.android.controller.ActivityController;
@RunWith(RobolectricTestRunner.class)
public class FinancedDeviceActionDisabledByAdminControllerTest {
private final Context mContext = ApplicationProvider.getApplicationContext();
private final Activity mActivity = ActivityController.of(new Activity()).get();
private final ActionDisabledByAdminControllerTestUtils mTestUtils =
new ActionDisabledByAdminControllerTestUtils();
private final FinancedDeviceActionDisabledByAdminController mController =
new FinancedDeviceActionDisabledByAdminController(
DEFAULT_DEVICE_ADMIN_STRING_PROVIDER);
@Before
public void setUp() {
mActivity.setTheme(androidx.appcompat.R.style.Theme_AppCompat_DayNight);
mController.initialize(mTestUtils.createLearnMoreButtonLauncher());
mController.updateEnforcedAdmin(ENFORCED_ADMIN, ENFORCEMENT_ADMIN_USER_ID);
}
@Test
public void setupLearnMoreButton_negativeButtonSet() {
mController.setupLearnMoreButton(mContext);
mTestUtils.assertLearnMoreAction(LEARN_MORE_ACTION_SHOW_ADMIN_POLICIES);
}
@Test
public void getAdminSupportTitleResource_works() {
assertThat(mController.getAdminSupportTitle(null))
.isEqualTo(DEFAULT_DISABLED_BY_POLICY_TITLE_FINANCED_DEVICE);
}
@Test
public void getAdminSupportContentString_withSupportMessage_returnsSupportMessage() {
assertThat(mController.getAdminSupportContentString(mContext, SUPPORT_MESSAGE))
.isEqualTo(SUPPORT_MESSAGE);
}
@Test
public void getAdminSupportContentString_noSupportMessage_returnsNull() {
assertThat(mController.getAdminSupportContentString(mContext, /* supportMessage= */ null))
.isNull();
}
}

View File

@@ -0,0 +1,125 @@
/*
* 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.settingslib.enterprise;
import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ENFORCED_ADMIN;
import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ENFORCEMENT_ADMIN_USER_ID;
import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.SUPPORT_MESSAGE;
import static com.android.settingslib.enterprise.FakeDeviceAdminStringProvider.DEFAULT_DISABLED_BY_POLICY_CONTENT;
import static com.android.settingslib.enterprise.FakeDeviceAdminStringProvider.DEFAULT_DISABLED_BY_POLICY_TITLE;
import static com.android.settingslib.enterprise.FakeDeviceAdminStringProvider.DISALLOW_ADJUST_VOLUME_TITLE;
import static com.google.common.truth.Truth.assertThat;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ResolveInfo;
import android.os.UserHandle;
import android.os.UserManager;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.android.controller.ActivityController;
@RunWith(RobolectricTestRunner.class)
public class ManagedDeviceActionDisabledByAdminControllerTest {
private static UserHandle MANAGED_USER = new UserHandle(123);
private static final String RESTRICTION = UserManager.DISALLOW_ADJUST_VOLUME;
private static final String EMPTY_URL = "";
private static final String SUPPORT_TITLE_FOR_RESTRICTION = DISALLOW_ADJUST_VOLUME_TITLE;
public static final ResolveInfo TEST_RESULT_INFO = new ResolveInfo();
private final Context mContext = ApplicationProvider.getApplicationContext();
private final Activity mActivity = ActivityController.of(new Activity()).get();
private final ActionDisabledByAdminControllerTestUtils mTestUtils =
new ActionDisabledByAdminControllerTestUtils();
@Before
public void setUp() {
mActivity.setTheme(androidx.appcompat.R.style.Theme_AppCompat_DayNight);
}
@Test
public void getAdminSupportTitleResource_noRestriction_works() {
ManagedDeviceActionDisabledByAdminController controller = createController();
assertThat(controller.getAdminSupportTitle(null))
.isEqualTo(DEFAULT_DISABLED_BY_POLICY_TITLE);
}
@Test
public void getAdminSupportTitleResource_withRestriction_works() {
ManagedDeviceActionDisabledByAdminController controller = createController();
assertThat(controller.getAdminSupportTitle(RESTRICTION))
.isEqualTo(DEFAULT_DISABLED_BY_POLICY_TITLE);
}
@Test
public void getAdminSupportContentString_withSupportMessage_returnsSupportMessage() {
ManagedDeviceActionDisabledByAdminController controller = createController();
assertThat(controller.getAdminSupportContentString(mActivity, SUPPORT_MESSAGE))
.isEqualTo(SUPPORT_MESSAGE);
}
@Test
public void getAdminSupportContentString_noSupportMessage_returnsDefault() {
ManagedDeviceActionDisabledByAdminController controller = createController();
assertThat(controller.getAdminSupportContentString(mActivity, /* supportMessage= */ null))
.isEqualTo(DEFAULT_DISABLED_BY_POLICY_CONTENT);
}
private ManagedDeviceActionDisabledByAdminController createController() {
return createController(
/* url= */ null,
/* foregroundUserChecker= */ true,
mContext.getUser(),
/* userContainingBrowser= */ null);
}
private ManagedDeviceActionDisabledByAdminController createController(String url) {
return createController(
url,
/* foregroundUserChecker= */ true,
mContext.getUser(),
/* userContainingBrowser= */ null);
}
private ManagedDeviceActionDisabledByAdminController createController(
String url,
boolean isUserForeground,
UserHandle preferredUserHandle,
UserHandle userContainingBrowser) {
ManagedDeviceActionDisabledByAdminController controller =
new ManagedDeviceActionDisabledByAdminController(
new FakeDeviceAdminStringProvider(url),
preferredUserHandle,
/* foregroundUserChecker= */ (context, userHandle) -> isUserForeground,
/* resolveActivityChecker= */ (packageManager, __, userHandle) ->
userHandle.equals(userContainingBrowser));
controller.initialize(mTestUtils.createLearnMoreButtonLauncher());
controller.updateEnforcedAdmin(ENFORCED_ADMIN, ENFORCEMENT_ADMIN_USER_ID);
return controller;
}
}

View File

@@ -0,0 +1,97 @@
/*
* 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.settingslib.enterprise;
import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ADMIN_COMPONENT;
import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ENFORCED_ADMIN;
import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ENFORCEMENT_ADMIN_USER_ID;
import static com.android.settingslib.enterprise.FakeDeviceAdminStringProvider.DEFAULT_DEVICE_ADMIN_STRING_PROVIDER;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static junit.framework.TestCase.assertEquals;
import static org.mockito.Mockito.mock;
import static org.robolectric.Shadows.shadowOf;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.UserManager;
import android.provider.Settings;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadows.ShadowResolveInfo;
@RunWith(RobolectricTestRunner.class)
public class SupervisedDeviceActionDisabledByAdminControllerTest {
private Context mContext;
private ActionDisabledByAdminControllerTestUtils mTestUtils;
private SupervisedDeviceActionDisabledByAdminController mController;
@Before
public void setUp() {
mContext = Robolectric.buildActivity(Activity.class).setup().get();
mTestUtils = new ActionDisabledByAdminControllerTestUtils();
mController = new SupervisedDeviceActionDisabledByAdminController(
DEFAULT_DEVICE_ADMIN_STRING_PROVIDER, UserManager.DISALLOW_ADD_USER);
mController.initialize(mTestUtils.createLearnMoreButtonLauncher());
mController.updateEnforcedAdmin(ENFORCED_ADMIN, ENFORCEMENT_ADMIN_USER_ID);
}
@Test
public void buttonClicked() {
Uri restrictionUri = Uri.parse("policy:/user_restrictions/no_add_user");
Intent intent = new Intent(Settings.ACTION_MANAGE_SUPERVISOR_RESTRICTED_SETTING)
.setData(restrictionUri)
.setPackage(ADMIN_COMPONENT.getPackageName());
ResolveInfo resolveInfo = ShadowResolveInfo.newResolveInfo("Admin Activity",
ADMIN_COMPONENT.getPackageName(), "InfoActivity");
shadowOf(mContext.getPackageManager()).addResolveInfoForIntent(intent, resolveInfo);
DialogInterface.OnClickListener listener =
mController.getPositiveButtonListener(mContext, ENFORCED_ADMIN);
assertNotNull("Supervision controller must supply a non-null listener", listener);
listener.onClick(mock(DialogInterface.class), 0 /* which */);
Intent nextIntent = shadowOf(RuntimeEnvironment.application).getNextStartedActivity();
assertEquals(Settings.ACTION_MANAGE_SUPERVISOR_RESTRICTED_SETTING,
nextIntent.getAction());
assertEquals(restrictionUri, nextIntent.getData());
assertEquals(ADMIN_COMPONENT.getPackageName(), nextIntent.getPackage());
}
@Test
public void noButton() {
// No supervisor restricted setting Activity
DialogInterface.OnClickListener listener =
mController.getPositiveButtonListener(mContext, ENFORCED_ADMIN);
assertNull("Supervision controller generates null listener", listener);
}
}

View File

@@ -0,0 +1,295 @@
/*
* 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.
*/
package com.android.settingslib.fuelgauge;
import static com.android.settingslib.fuelgauge.BatterySaverLogging.ACTION_SAVER_STATE_MANUAL_UPDATE;
import static com.android.settingslib.fuelgauge.BatterySaverLogging.SAVER_ENABLED_UNKNOWN;
import static com.android.settingslib.fuelgauge.BatterySaverUtils.ACTION_SHOW_AUTO_SAVER_SUGGESTION;
import static com.android.settingslib.fuelgauge.BatterySaverUtils.ACTION_SHOW_START_SAVER_CONFIRMATION;
import static com.android.settingslib.fuelgauge.BatterySaverUtils.KEY_NO_SCHEDULE;
import static com.android.settingslib.fuelgauge.BatterySaverUtils.KEY_PERCENTAGE;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
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.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.os.PowerManager;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
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 org.robolectric.RobolectricTestRunner;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class BatterySaverUtilsTest {
private static final int BATTERY_SAVER_THRESHOLD_1 = 15;
private static final int BATTERY_SAVER_THRESHOLD_2 = 20;
@Mock
private Context mMockContext;
@Mock
private ContentResolver mMockResolver;
@Mock
private PowerManager mMockPowerManager;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mMockContext.getContentResolver()).thenReturn(mMockResolver);
when(mMockContext.getSystemService(eq(PowerManager.class))).thenReturn(mMockPowerManager);
when(mMockPowerManager.setPowerSaveModeEnabled(anyBoolean())).thenReturn(true);
}
@Test
public void testSetPowerSaveMode_enableWithWarning_firstCall_needConfirmationWarning() {
Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null");
Secure.putString(mMockResolver, Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED, "null");
Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, true,
SAVER_ENABLED_UNKNOWN)).isFalse();
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mMockContext, times(1)).sendBroadcast(intentCaptor.capture());
verify(mMockPowerManager, times(0)).setPowerSaveModeEnabled(anyBoolean());
assertThat(intentCaptor.getValue().getAction()).isEqualTo(
ACTION_SHOW_START_SAVER_CONFIRMATION);
// They shouldn't have changed.
assertEquals(-1, Secure.getInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, -1));
assertEquals(-1,
Secure.getInt(mMockResolver, Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED, -1));
assertEquals(-2,
Secure.getInt(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, -2));
}
@Test
public void testSetPowerSaveMode_enableWithWarning_secondCall_expectUpdateIntent() {
// Already acked.
Secure.putInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1);
Secure.putInt(mMockResolver, Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED, 1);
Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, true,
SAVER_ENABLED_UNKNOWN)).isTrue();
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mMockContext, times(1)).sendBroadcast(intentCaptor.capture());
verify(mMockPowerManager, times(1)).setPowerSaveModeEnabled(eq(true));
assertThat(intentCaptor.getValue().getAction()).isEqualTo(
ACTION_SAVER_STATE_MANUAL_UPDATE);
assertEquals(1, Secure.getInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, -1));
assertEquals(1,
Secure.getInt(mMockResolver, Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED, -1));
assertEquals(1,
Secure.getInt(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, -2));
}
@Test
public void testSetPowerSaveMode_enableWithWarning_thirdCall_expectUpdateIntent() {
// Already acked.
Secure.putInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1);
Secure.putInt(mMockResolver, Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED, 1);
Secure.putInt(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, 1);
assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, true,
SAVER_ENABLED_UNKNOWN)).isTrue();
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mMockContext, times(1)).sendBroadcast(intentCaptor.capture());
verify(mMockPowerManager, times(1)).setPowerSaveModeEnabled(eq(true));
assertThat(intentCaptor.getValue().getAction()).isEqualTo(
ACTION_SAVER_STATE_MANUAL_UPDATE);
assertEquals(1, Secure.getInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, -1));
assertEquals(1,
Secure.getInt(mMockResolver, Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED, -1));
assertEquals(2,
Secure.getInt(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, -2));
}
@Test
public void testSetPowerSaveMode_enableWithWarning_5thCall_needAutoSuggestionWarning() {
// Already acked.
Secure.putInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1);
Secure.putInt(mMockResolver, Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED, 1);
Secure.putInt(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, 3);
assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, true,
SAVER_ENABLED_UNKNOWN)).isTrue();
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mMockContext, times(2)).sendBroadcast(intentCaptor.capture());
verify(mMockPowerManager, times(1)).setPowerSaveModeEnabled(eq(true));
List<Intent> values = intentCaptor.getAllValues();
assertThat(values.get(0).getAction()).isEqualTo(ACTION_SHOW_AUTO_SAVER_SUGGESTION);
assertThat(values.get(1).getAction()).isEqualTo(ACTION_SAVER_STATE_MANUAL_UPDATE);
assertEquals(1, Secure.getInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, -1));
assertEquals(1,
Secure.getInt(mMockResolver, Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED, -1));
assertEquals(4,
Secure.getInt(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, -2));
}
@Test
public void testSetPowerSaveMode_enableWithoutWarning_expectUpdateIntent() {
Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null");
Secure.putString(mMockResolver, Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED, "null");
Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, false,
SAVER_ENABLED_UNKNOWN)).isTrue();
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mMockContext, times(1)).sendBroadcast(intentCaptor.capture());
verify(mMockPowerManager, times(1)).setPowerSaveModeEnabled(eq(true));
assertThat(intentCaptor.getValue().getAction()).isEqualTo(
ACTION_SAVER_STATE_MANUAL_UPDATE);
assertEquals(1, Secure.getInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, -1));
assertEquals(1,
Secure.getInt(mMockResolver, Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED, -1));
assertEquals(1, Secure.getInt(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, -2));
}
@Test
public void testSetPowerSaveMode_disableWithoutWarning_expectUpdateIntent() {
verifyDisablePowerSaveMode(/* needFirstTimeWarning= */ false);
}
@Test
public void testSetPowerSaveMode_disableWithWarning_expectUpdateIntent() {
verifyDisablePowerSaveMode(/* needFirstTimeWarning= */ true);
}
@Test
public void testEnsureAutoBatterysaver_setNewPositiveValue_doNotOverwrite() {
Global.putInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
BatterySaverUtils.ensureAutoBatterySaver(mMockContext, BATTERY_SAVER_THRESHOLD_1);
assertThat(Global.getInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, -1))
.isEqualTo(BATTERY_SAVER_THRESHOLD_1);
// Once a positive number is set, ensureAutoBatterySaver() won't overwrite it.
BatterySaverUtils.ensureAutoBatterySaver(mMockContext, BATTERY_SAVER_THRESHOLD_2);
assertThat(Global.getInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, -1))
.isEqualTo(BATTERY_SAVER_THRESHOLD_1);
}
@Test
public void testSetAutoBatterySaverTriggerLevel_setSuppressSuggestion() {
Global.putString(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, "null");
Secure.putString(mMockResolver, Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, "null");
BatterySaverUtils.setAutoBatterySaverTriggerLevel(mMockContext, 0);
assertThat(Global.getInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, -1))
.isEqualTo(0);
assertThat(Secure.getInt(mMockResolver, Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, -1))
.isEqualTo(-1); // not set.
BatterySaverUtils.setAutoBatterySaverTriggerLevel(mMockContext, BATTERY_SAVER_THRESHOLD_1);
assertThat(Global.getInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, -1))
.isEqualTo(BATTERY_SAVER_THRESHOLD_1);
assertThat(Secure.getInt(mMockResolver, Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, -1))
.isEqualTo(1);
}
@Test
public void testGetBatterySaverScheduleKey_returnExpectedKey() {
Global.putInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
Global.putInt(mMockResolver, Global.AUTOMATIC_POWER_SAVE_MODE,
PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
assertThat(BatterySaverUtils.getBatterySaverScheduleKey(mMockContext)).isEqualTo(
KEY_NO_SCHEDULE);
Global.putInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 20);
Global.putInt(mMockResolver, Global.AUTOMATIC_POWER_SAVE_MODE,
PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
assertThat(BatterySaverUtils.getBatterySaverScheduleKey(mMockContext)).isEqualTo(
KEY_PERCENTAGE);
Global.putInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 20);
Global.putInt(mMockResolver, Global.AUTOMATIC_POWER_SAVE_MODE,
PowerManager.POWER_SAVE_MODE_TRIGGER_DYNAMIC);
assertThat(BatterySaverUtils.getBatterySaverScheduleKey(mMockContext)).isEqualTo(
KEY_NO_SCHEDULE);
}
@Test
public void testSetBatterySaverScheduleMode_setSchedule() {
BatterySaverUtils.setBatterySaverScheduleMode(mMockContext, KEY_NO_SCHEDULE, -1);
assertThat(Global.getInt(mMockResolver, Global.AUTOMATIC_POWER_SAVE_MODE, -1))
.isEqualTo(PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
assertThat(Global.getInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, -1))
.isEqualTo(0);
BatterySaverUtils.setBatterySaverScheduleMode(mMockContext, KEY_PERCENTAGE, 20);
assertThat(Global.getInt(mMockResolver, Global.AUTOMATIC_POWER_SAVE_MODE, -1))
.isEqualTo(PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
assertThat(Global.getInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, -1))
.isEqualTo(20);
}
private void verifyDisablePowerSaveMode(boolean needFirstTimeWarning) {
Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null");
Secure.putString(mMockResolver, Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED, "null");
Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
// When disabling, needFirstTimeWarning doesn't matter.
assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, false, needFirstTimeWarning,
SAVER_ENABLED_UNKNOWN)).isTrue();
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mMockContext, times(1)).sendBroadcast(intentCaptor.capture());
verify(mMockPowerManager, times(1)).setPowerSaveModeEnabled(eq(false));
assertThat(intentCaptor.getValue().getAction()).isEqualTo(
ACTION_SAVER_STATE_MANUAL_UPDATE);
assertEquals(-1, Secure.getInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, -1));
assertEquals(-1,
Secure.getInt(mMockResolver, Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED, -1));
assertEquals(-2,
Secure.getInt(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, -2));
}
}

View File

@@ -0,0 +1,122 @@
/*
* 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.settingslib.fuelgauge;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.robolectric.Shadows.shadowOf;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.UserManager;
import android.provider.Settings;
import android.view.accessibility.AccessibilityManager;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.Shadows;
import org.robolectric.shadows.ShadowAccessibilityManager;
import java.util.Arrays;
@RunWith(RobolectricTestRunner.class)
public class BatteryUtilsTest {
private static final String DEFAULT_TTS_PACKAGE = "com.abc.talkback";
private static final String ACCESSIBILITY_PACKAGE = "com.def.talkback";
private Context mContext;
private AccessibilityManager mAccessibilityManager;
private ShadowAccessibilityManager mShadowAccessibilityManager;
@Mock
private AccessibilityServiceInfo mAccessibilityServiceInfo1;
@Mock
private AccessibilityServiceInfo mAccessibilityServiceInfo2;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
doReturn(mContext).when(mContext).getApplicationContext();
mAccessibilityManager = spy(mContext.getSystemService(AccessibilityManager.class));
mShadowAccessibilityManager = shadowOf(mAccessibilityManager);
doReturn(mAccessibilityManager).when(mContext)
.getSystemService(AccessibilityManager.class);
setTtsPackageName(DEFAULT_TTS_PACKAGE);
doReturn(Arrays.asList(mAccessibilityServiceInfo1, mAccessibilityServiceInfo2))
.when(mAccessibilityManager)
.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
doReturn(ACCESSIBILITY_PACKAGE + "/.TalkbackService").when(mAccessibilityServiceInfo1)
.getId();
doReturn("dummy_package_name").when(mAccessibilityServiceInfo2).getId();
}
@Test
public void getBatteryIntent_registerReceiver() {
BatteryUtils.getBatteryIntent(mContext);
verify(mContext).registerReceiver(eq(null), any(IntentFilter.class));
}
@Test
public void getA11yPackageNames_returnDefaultTtsPackageName() {
mShadowAccessibilityManager.setEnabled(false);
assertThat(BatteryUtils.getA11yPackageNames(mContext))
.containsExactly(DEFAULT_TTS_PACKAGE);
}
@Test
public void getA11yPackageNames_returnExpectedPackageNames() {
mShadowAccessibilityManager.setEnabled(true);
assertThat(BatteryUtils.getA11yPackageNames(mContext))
.containsExactly(DEFAULT_TTS_PACKAGE, ACCESSIBILITY_PACKAGE);
}
@Test
public void isWorkProfile_defaultValue_returnFalse() {
assertThat(BatteryUtils.isWorkProfile(mContext)).isFalse();
}
@Test
public void isWorkProfile_workProfileMode_returnTrue() {
final UserManager userManager = mContext.getSystemService(UserManager.class);
Shadows.shadowOf(userManager).setManagedProfile(true);
Shadows.shadowOf(userManager).setIsSystemUser(false);
assertThat(BatteryUtils.isWorkProfile(mContext)).isTrue();
}
private void setTtsPackageName(String defaultTtsPackageName) {
Settings.Secure.putString(mContext.getContentResolver(),
Settings.Secure.TTS_DEFAULT_SYNTH, defaultTtsPackageName);
}
}

View File

@@ -0,0 +1,182 @@
/*
* 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.
*/
package com.android.settingslib.fuelgauge;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.IDeviceIdleController;
import com.android.settingslib.testutils.shadow.ShadowDefaultDialerManager;
import com.android.settingslib.testutils.shadow.ShadowSmsApplication;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowPackageManager;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowDefaultDialerManager.class, ShadowSmsApplication.class})
public class PowerAllowlistBackendTest {
private static final String PACKAGE_ONE = "com.example.packageone";
private static final String PACKAGE_TWO = "com.example.packagetwo";
private static final int UID = 12345;
@Mock
private IDeviceIdleController mDeviceIdleService;
@Mock
private DevicePolicyManager mDevicePolicyManager;
@Mock
private AppOpsManager mAppOpsManager;
private PowerAllowlistBackend mPowerAllowlistBackend;
private ShadowPackageManager mPackageManager;
private Context mContext;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
doReturn(mContext).when(mContext).getApplicationContext();
doReturn(new String[] {}).when(mDeviceIdleService).getFullPowerWhitelist();
doReturn(new String[] {}).when(mDeviceIdleService).getSystemPowerWhitelist();
doReturn(new String[] {}).when(mDeviceIdleService).getSystemPowerWhitelistExceptIdle();
doNothing().when(mDeviceIdleService).addPowerSaveWhitelistApp(anyString());
doNothing().when(mDeviceIdleService).removePowerSaveWhitelistApp(anyString());
mPackageManager = Shadow.extract(mContext.getPackageManager());
mPackageManager.setSystemFeature(PackageManager.FEATURE_TELEPHONY, true);
doReturn(mDevicePolicyManager).when(mContext).getSystemService(DevicePolicyManager.class);
doReturn(mAppOpsManager).when(mContext).getSystemService(AppOpsManager.class);
doReturn(AppOpsManager.MODE_DEFAULT).when(mAppOpsManager).checkOpNoThrow(
AppOpsManager.OP_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS, UID, PACKAGE_ONE);
doReturn(AppOpsManager.MODE_DEFAULT).when(mAppOpsManager).checkOpNoThrow(
AppOpsManager.OP_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS, UID, PACKAGE_TWO);
mPowerAllowlistBackend = new PowerAllowlistBackend(mContext, mDeviceIdleService);
}
@Test
public void testIsAllowlisted() throws Exception {
doReturn(new String[] {PACKAGE_ONE}).when(mDeviceIdleService).getFullPowerWhitelist();
mPowerAllowlistBackend.refreshList();
assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE, UID)).isTrue();
assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO, UID)).isFalse();
assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_ONE}, UID)).isTrue();
assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_TWO}, UID)).isFalse();
mPowerAllowlistBackend.addApp(PACKAGE_TWO);
verify(mDeviceIdleService, atLeastOnce()).addPowerSaveWhitelistApp(PACKAGE_TWO);
assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE, UID)).isTrue();
assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO, UID)).isTrue();
assertThat(mPowerAllowlistBackend.isAllowlisted(
new String[] {PACKAGE_ONE, PACKAGE_TWO}, UID)).isTrue();
mPowerAllowlistBackend.removeApp(PACKAGE_TWO);
verify(mDeviceIdleService, atLeastOnce()).removePowerSaveWhitelistApp(PACKAGE_TWO);
assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE, UID)).isTrue();
assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO, UID)).isFalse();
assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_ONE}, UID)).isTrue();
assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_TWO}, UID)).isFalse();
mPowerAllowlistBackend.removeApp(PACKAGE_ONE);
verify(mDeviceIdleService, atLeastOnce()).removePowerSaveWhitelistApp(PACKAGE_ONE);
assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE, UID)).isFalse();
assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO, UID)).isFalse();
assertThat(mPowerAllowlistBackend.isAllowlisted(
new String[] {PACKAGE_ONE, PACKAGE_TWO}, UID)).isFalse();
}
@Test
public void isAllowlisted_shouldAllowlistDefaultSms() {
final String testSms = "com.android.test.defaultsms";
ShadowSmsApplication.setDefaultSmsApplication(new ComponentName(testSms, "receiver"));
mPowerAllowlistBackend.refreshList();
assertThat(mPowerAllowlistBackend.isAllowlisted(testSms, UID)).isTrue();
assertThat(mPowerAllowlistBackend.isDefaultActiveApp(testSms, UID)).isTrue();
}
@Test
public void isAllowlisted_shouldAllowlistDefaultDialer() {
final String testDialer = "com.android.test.defaultdialer";
ShadowDefaultDialerManager.setDefaultDialerApplication(testDialer);
mPowerAllowlistBackend.refreshList();
assertThat(mPowerAllowlistBackend.isAllowlisted(testDialer, UID)).isTrue();
assertThat(mPowerAllowlistBackend.isDefaultActiveApp(testDialer, UID)).isTrue();
}
@Test
public void isAllowlisted_shouldAllowlistActiveDeviceAdminApp() {
doReturn(true).when(mDevicePolicyManager).packageHasActiveAdmins(PACKAGE_ONE);
assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE, UID)).isTrue();
assertThat(mPowerAllowlistBackend.isDefaultActiveApp(PACKAGE_ONE, UID)).isTrue();
}
@Test
public void isAllowlisted_shouldAllowlistAppWithSystemExemptAppOp() {
doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager).checkOpNoThrow(
AppOpsManager.OP_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS, UID, PACKAGE_ONE);
assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE, UID)).isTrue();
assertThat(mPowerAllowlistBackend.isDefaultActiveApp(PACKAGE_ONE, UID)).isTrue();
}
@Test
public void testIsSystemAllowlisted() throws Exception {
doReturn(new String[] {PACKAGE_ONE}).when(mDeviceIdleService).getSystemPowerWhitelist();
mPowerAllowlistBackend.refreshList();
assertThat(mPowerAllowlistBackend.isSysAllowlisted(PACKAGE_ONE)).isTrue();
assertThat(mPowerAllowlistBackend.isSysAllowlisted(PACKAGE_TWO)).isFalse();
assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE, UID)).isFalse();
}
@Test
public void testIsPowerSaveWhitelistExceptIdleApp() throws Exception {
doReturn(true).when(mDeviceIdleService)
.isPowerSaveWhitelistExceptIdleApp(PACKAGE_ONE);
mPowerAllowlistBackend.refreshList();
assertThat(mPowerAllowlistBackend.isAllowlistedExceptIdle(PACKAGE_ONE)).isTrue();
}
}

View File

@@ -0,0 +1,83 @@
/*
* 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.
*/
package com.android.settingslib.graph;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class)
public class BatteryMeterDrawableBaseTest {
private static final int CRITICAL_LEVEL = 5;
private static final int PADDING = 5;
private static final int HEIGHT = 80;
private static final int WIDTH = 40;
@Mock
private Canvas mCanvas;
private Context mContext;
private BatteryMeterDrawableBase mBatteryMeterDrawableBase;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mBatteryMeterDrawableBase = spy(new BatteryMeterDrawableBase(mContext, 0 /* frameColor */));
ReflectionHelpers.setField(mBatteryMeterDrawableBase, "mCriticalLevel", CRITICAL_LEVEL);
}
@Test
public void testDraw_hasPaddingAndBounds_drawWarningInCorrectPosition() {
mBatteryMeterDrawableBase.setPadding(PADDING, PADDING, PADDING, PADDING);
mBatteryMeterDrawableBase.setBounds(0, 0, WIDTH + 2 * PADDING, HEIGHT + 2 * PADDING);
mBatteryMeterDrawableBase.setBatteryLevel(3);
mBatteryMeterDrawableBase.draw(mCanvas);
// WIDTH * 0.5 + PADDING = 25
// (HEIGHT + TEXT_HEIGHT) * 0.48 + PADDING = 43.3999998
verify(mCanvas).drawText(eq("!"), eq(25f), eq(43.399998f), any(Paint.class));
}
@Test
public void testDraw_hasPaddingAndBounds_drawBatteryLevelInCorrectPosition() {
mBatteryMeterDrawableBase.setPadding(PADDING, PADDING, PADDING, PADDING);
mBatteryMeterDrawableBase.setBounds(0, 0, WIDTH + 2 * PADDING, HEIGHT + 2 * PADDING);
mBatteryMeterDrawableBase.setBatteryLevel(20);
mBatteryMeterDrawableBase.setShowPercent(true);
mBatteryMeterDrawableBase.draw(mCanvas);
// WIDTH * 0.5 + PADDING = 25
// (HEIGHT + TEXT_HEIGHT) * 0.47 + PADDING = 42.6
verify(mCanvas).drawText(eq("20"), eq(25f), eq(42.6f), any(Paint.class));
}
}

View File

@@ -0,0 +1,107 @@
/*
* 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.
*/
package com.android.settingslib.graph;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.graphics.drawable.VectorDrawable;
import com.android.settingslib.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class BluetoothDeviceLayerDrawableTest {
private static final int RES_ID = com.android.internal.R.drawable.ic_phone;
private static final int BATTERY_LEVEL = 15;
private static final float BATTERY_ICON_SCALE = 0.75f;
private static final int BATTERY_ICON_PADDING_TOP = 6;
private static final float TOLERANCE = 0.001f;
private Context mContext;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
}
@Test
public void testCreateLayerDrawable_configCorrect() {
BluetoothDeviceLayerDrawable drawable = BluetoothDeviceLayerDrawable.createLayerDrawable(
mContext, RES_ID, BATTERY_LEVEL);
assertThat(drawable.getDrawable(0)).isInstanceOf(VectorDrawable.class);
assertThat(drawable.getDrawable(1)).isInstanceOf(
BluetoothDeviceLayerDrawable.BatteryMeterDrawable.class);
assertThat(drawable.getLayerInsetStart(1)).isEqualTo(
drawable.getDrawable(0).getIntrinsicWidth());
assertThat(drawable.getLayerInsetTop(1)).isEqualTo(0);
}
@Test
public void testCreateLayerDrawable_withIconScale_configCorrect() {
BluetoothDeviceLayerDrawable drawable = BluetoothDeviceLayerDrawable.createLayerDrawable(
mContext, RES_ID, BATTERY_LEVEL, BATTERY_ICON_SCALE);
assertThat(drawable.getDrawable(0)).isInstanceOf(VectorDrawable.class);
assertThat(drawable.getDrawable(1)).isInstanceOf(
BluetoothDeviceLayerDrawable.BatteryMeterDrawable.class);
assertThat(drawable.getLayerInsetStart(1)).isEqualTo(
drawable.getDrawable(0).getIntrinsicWidth());
assertThat(drawable.getLayerInsetTop(1)).isEqualTo(BATTERY_ICON_PADDING_TOP);
}
@Test
public void testBatteryMeterDrawable_configCorrect() {
BluetoothDeviceLayerDrawable.BatteryMeterDrawable batteryDrawable =
new BluetoothDeviceLayerDrawable.BatteryMeterDrawable(mContext,
R.color.meter_background_color, BATTERY_LEVEL);
assertThat(batteryDrawable.getAspectRatio()).isWithin(TOLERANCE).of(0.35f);
assertThat(batteryDrawable.getRadiusRatio()).isWithin(TOLERANCE).of(0f);
assertThat(batteryDrawable.getBatteryLevel()).isEqualTo(BATTERY_LEVEL);
}
@Test
public void testConstantState_returnTwinBluetoothLayerDrawable() {
BluetoothDeviceLayerDrawable drawable = BluetoothDeviceLayerDrawable.createLayerDrawable(
mContext, RES_ID, BATTERY_LEVEL);
BluetoothDeviceLayerDrawable twinDrawable =
(BluetoothDeviceLayerDrawable) drawable.getConstantState().newDrawable();
assertThat(twinDrawable.getDrawable(0)).isNotNull();
assertThat(twinDrawable.getDrawable(1)).isNotNull();
assertThat(twinDrawable.getLayerInsetTop(1)).isEqualTo(drawable.getLayerInsetTop(1));
}
@Test
public void testCreateLayerDrawable_bluetoothDrawable_hasCorrectFrameColor() {
BluetoothDeviceLayerDrawable drawable = BluetoothDeviceLayerDrawable.createLayerDrawable(
mContext, RES_ID, BATTERY_LEVEL);
BluetoothDeviceLayerDrawable.BatteryMeterDrawable batteryMeterDrawable =
(BluetoothDeviceLayerDrawable.BatteryMeterDrawable) drawable.getDrawable(1);
assertThat(batteryMeterDrawable.mFrameColor).isEqualTo(
mContext.getColor(R.color.meter_background_color));
}
}

View File

@@ -0,0 +1,266 @@
/*
* 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.
*/
package com.android.settingslib.inputmethod;
import static com.google.common.truth.Truth.assertThat;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
@RunWith(RobolectricTestRunner.class)
public class InputMethodAndSubtypeUtilCompatTest {
private static final HashSet<String> EMPTY_STRING_SET = new HashSet<>();
private static HashSet<String> asHashSet(String... strings) {
return new HashSet<>(Arrays.asList(strings));
}
@Test
public void parseInputMethodsAndSubtypesString_EmptyString() {
assertThat(InputMethodAndSubtypeUtilCompat.
parseInputMethodsAndSubtypesString("")).isEmpty();
assertThat(InputMethodAndSubtypeUtilCompat.
parseInputMethodsAndSubtypesString(null)).isEmpty();
}
@Test
public void parseInputMethodsAndSubtypesString_SingleImeNoSubtype() {
HashMap<String, HashSet<String>> r =
InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString("ime0");
assertThat(r).containsExactly("ime0", EMPTY_STRING_SET);
}
@Test
public void parseInputMethodsAndSubtypesString_MultipleImesNoSubtype() {
HashMap<String, HashSet<String>> r =
InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString("ime0:ime1");
assertThat(r).containsExactly("ime0", EMPTY_STRING_SET, "ime1", EMPTY_STRING_SET);
}
@Test
public void parseInputMethodsAndSubtypesString_SingleImeSingleSubtype() {
HashMap<String, HashSet<String>> r =
InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString("ime0;subtype0");
assertThat(r).containsExactly("ime0", asHashSet("subtype0"));
}
@Test
public void parseInputMethodsAndSubtypesString_SingleImeDuplicateSameSubtypes() {
HashMap<String, HashSet<String>> r =
InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString(
"ime0;subtype0;subtype0");
assertThat(r).containsExactly("ime0", asHashSet("subtype0"));
}
@Test
public void parseInputMethodsAndSubtypesString_SingleImeMultipleSubtypes() {
HashMap<String, HashSet<String>> r =
InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString(
"ime0;subtype0;subtype1");
assertThat(r).containsExactly("ime0", asHashSet("subtype0", "subtype1"));
}
@Test
public void parseInputMethodsAndSubtypesString_MultiplePairsOfImeSubtype() {
assertThat(InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString(
"ime0;subtype0:ime1;subtype1"))
.containsExactly("ime0", asHashSet("subtype0"), "ime1", asHashSet("subtype1"));
assertThat(InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString(
"ime0;subtype0;subtype1:ime1;subtype2"))
.containsExactly("ime0", asHashSet("subtype0", "subtype1"),
"ime1", asHashSet("subtype2"));
assertThat(InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString(
"ime0;subtype0;subtype1:ime1;subtype1;subtype2"))
.containsExactly("ime0", asHashSet("subtype0", "subtype1"),
"ime1", asHashSet("subtype1", "subtype2"));
}
@Test
public void parseInputMethodsAndSubtypesString_MixedImeSubtypePairsAndImeNoSubtype() {
HashMap<String, HashSet<String>> r =
InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString(
"ime0;subtype0;subtype1:ime1;subtype1;subtype2:ime2");
assertThat(r).containsExactly("ime0", asHashSet("subtype0", "subtype1"),
"ime1", asHashSet("subtype1", "subtype2"),
"ime2", EMPTY_STRING_SET);
}
@Test
public void buildInputMethodsAndSubtypesString_EmptyInput() {
HashMap<String, HashSet<String>> map = new HashMap<>();
assertThat(map).isEmpty();
}
@Test
public void buildInputMethodsAndSubtypesString_SingleIme() {
HashMap<String, HashSet<String>> map = new HashMap<>();
map.put("ime0", new HashSet<>());
String result = InputMethodAndSubtypeUtilCompat.buildInputMethodsAndSubtypesString(map);
assertThat(result).isEqualTo("ime0");
}
@Test
public void buildInputMethodsAndSubtypesString_SingleImeSingleSubtype() {
HashMap<String, HashSet<String>> map = new HashMap<>();
map.put("ime0", asHashSet("subtype0"));
String result = InputMethodAndSubtypeUtilCompat.buildInputMethodsAndSubtypesString(map);
assertThat(result).isEqualTo("ime0;subtype0");
}
@Test
public void buildInputMethodsAndSubtypesString_SingleImeMultipleSubtypes() {
HashMap<String, HashSet<String>> map = new HashMap<>();
map.put("ime0", asHashSet("subtype0", "subtype1"));
String result = InputMethodAndSubtypeUtilCompat.buildInputMethodsAndSubtypesString(map);
// We do not expect what order will be used to concatenate items in
// InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString() hence accept all possible
// permutations here.
assertThat(result).matches("ime0;subtype0;subtype1|ime0;subtype1;subtype0");
}
@Test
public void buildInputMethodsAndSubtypesString_MultipleImesNoSubtypes() {
HashMap<String, HashSet<String>> map = new HashMap<>();
map.put("ime0", EMPTY_STRING_SET);
map.put("ime1", EMPTY_STRING_SET);
String result = InputMethodAndSubtypeUtilCompat.buildInputMethodsAndSubtypesString(map);
// We do not expect what order will be used to concatenate items in
// InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString() hence accept all possible
// permutations here.
assertThat(result).matches("ime0:ime1|ime1:ime0");
}
@Test
public void buildInputMethodsAndSubtypesString_MultipleImesWithAndWithoutSubtypes() {
HashMap<String, HashSet<String>> map = new HashMap<>();
map.put("ime0", asHashSet("subtype0", "subtype1"));
map.put("ime1", EMPTY_STRING_SET);
String result = InputMethodAndSubtypeUtilCompat.buildInputMethodsAndSubtypesString(map);
// We do not expect what order will be used to concatenate items in
// InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString() hence accept all possible
// permutations here.
assertThat(result).matches("ime0;subtype0;subtype1:ime1|ime0;subtype1;subtype0:ime1"
+ "|ime1:ime0;subtype0;subtype1|ime1:ime0;subtype1;subtype0");
}
@Test
public void buildInputMethodsAndSubtypesString_MultipleImesWithSubtypes() {
HashMap<String, HashSet<String>> map = new HashMap<>();
map.put("ime0", asHashSet("subtype0", "subtype1"));
map.put("ime1", asHashSet("subtype2", "subtype3"));
String result = InputMethodAndSubtypeUtilCompat.buildInputMethodsAndSubtypesString(map);
// We do not expect what order will be used to concatenate items in
// InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString() hence accept all possible
// permutations here.
assertThat(result).matches("ime0;subtype0;subtype1:ime1;subtype2;subtype3"
+ "|ime0;subtype1;subtype0:ime1;subtype2;subtype3"
+ "|ime0;subtype0;subtype1:ime1;subtype3;subtype2"
+ "|ime0;subtype1;subtype0:ime1;subtype3;subtype2"
+ "|ime1;subtype2;subtype3:ime0;subtype0;subtype1"
+ "|ime2;subtype3;subtype2:ime0;subtype0;subtype1"
+ "|ime3;subtype2;subtype3:ime0;subtype1;subtype0"
+ "|ime4;subtype3;subtype2:ime0;subtype1;subtype0");
}
@Test
public void isValidSystemNonAuxAsciiCapableIme() {
// System IME w/ no subtype
assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
createFakeIme(true, false)))
.isFalse();
// System IME w/ non-Aux and non-ASCII-capable "keyboard" subtype
assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
createFakeIme(true, false, createFakeSubtype("keyboard", false, false))))
.isFalse();
// System IME w/ non-Aux and ASCII-capable "keyboard" subtype
assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
createFakeIme(true, false, createFakeSubtype("keyboard", false, true))))
.isTrue();
// System IME w/ Aux and ASCII-capable "keyboard" subtype
assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
createFakeIme(true, true, createFakeSubtype("keyboard", true, true))))
.isFalse();
// System IME w/ non-Aux and ASCII-capable "voice" subtype
assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
createFakeIme(true, false, createFakeSubtype("voice", false, true))))
.isFalse();
// System IME w/ non-Aux and non-ASCII-capable subtype + Non-Aux and ASCII-capable subtype
assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
createFakeIme(true, false,
createFakeSubtype("keyboard", false, true),
createFakeSubtype("keyboard", false, false))))
.isTrue();
// Non-system IME w/ non-Aux and ASCII-capable "keyboard" subtype
assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
createFakeIme(false, false, createFakeSubtype("keyboard", false, true))))
.isFalse();
}
private static InputMethodInfo createFakeIme(boolean isSystem, boolean isAuxIme,
InputMethodSubtype... subtypes) {
final ResolveInfo ri = new ResolveInfo();
final ServiceInfo si = new ServiceInfo();
final ApplicationInfo ai = new ApplicationInfo();
ai.packageName = "com.example.android.fakeime";
ai.enabled = true;
ai.flags |= (isSystem ? ApplicationInfo.FLAG_SYSTEM : 0);
si.applicationInfo = ai;
si.enabled = true;
si.packageName = "com.example.android.fakeime";
si.name = "Fake IME";
si.exported = true;
si.nonLocalizedLabel = "Fake IME";
ri.serviceInfo = si;
return new InputMethodInfo(ri, isAuxIme, "", Arrays.asList(subtypes), 1, false);
}
private static InputMethodSubtype createFakeSubtype(
String mode, boolean isAuxiliary, boolean isAsciiCapable) {
return new InputMethodSubtypeBuilder()
.setSubtypeNameResId(0)
.setSubtypeIconResId(0)
.setSubtypeLocale("en_US")
.setLanguageTag("en-US")
.setSubtypeMode(mode)
.setIsAuxiliary(isAuxiliary)
.setIsAsciiCapable(isAsciiCapable)
.build();
}
}

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