fix: 首次提交
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* 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 InputMethodAndSubtypeUtilTest {
|
||||
|
||||
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(InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString("")).isEmpty();
|
||||
assertThat(InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString(null)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseInputMethodsAndSubtypesString_SingleImeNoSubtype() {
|
||||
HashMap<String, HashSet<String>> r =
|
||||
InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString("ime0");
|
||||
assertThat(r).containsExactly("ime0", EMPTY_STRING_SET);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseInputMethodsAndSubtypesString_MultipleImesNoSubtype() {
|
||||
HashMap<String, HashSet<String>> r =
|
||||
InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString("ime0:ime1");
|
||||
assertThat(r).containsExactly("ime0", EMPTY_STRING_SET, "ime1", EMPTY_STRING_SET);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseInputMethodsAndSubtypesString_SingleImeSingleSubtype() {
|
||||
HashMap<String, HashSet<String>> r =
|
||||
InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString("ime0;subtype0");
|
||||
assertThat(r).containsExactly("ime0", asHashSet("subtype0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseInputMethodsAndSubtypesString_SingleImeDuplicateSameSubtypes() {
|
||||
HashMap<String, HashSet<String>> r =
|
||||
InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString(
|
||||
"ime0;subtype0;subtype0");
|
||||
assertThat(r).containsExactly("ime0", asHashSet("subtype0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseInputMethodsAndSubtypesString_SingleImeMultipleSubtypes() {
|
||||
HashMap<String, HashSet<String>> r =
|
||||
InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString(
|
||||
"ime0;subtype0;subtype1");
|
||||
assertThat(r).containsExactly("ime0", asHashSet("subtype0", "subtype1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseInputMethodsAndSubtypesString_MultiplePairsOfImeSubtype() {
|
||||
assertThat(InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString(
|
||||
"ime0;subtype0:ime1;subtype1"))
|
||||
.containsExactly("ime0", asHashSet("subtype0"), "ime1", asHashSet("subtype1"));
|
||||
assertThat(InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString(
|
||||
"ime0;subtype0;subtype1:ime1;subtype2"))
|
||||
.containsExactly("ime0", asHashSet("subtype0", "subtype1"),
|
||||
"ime1", asHashSet("subtype2"));
|
||||
assertThat(InputMethodAndSubtypeUtil.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 =
|
||||
InputMethodAndSubtypeUtil.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 = InputMethodAndSubtypeUtil.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 = InputMethodAndSubtypeUtil.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 = InputMethodAndSubtypeUtil.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 = InputMethodAndSubtypeUtil.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 = InputMethodAndSubtypeUtil.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 = InputMethodAndSubtypeUtil.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 isValidNonAuxAsciiCapableIme() {
|
||||
// IME w/ no subtype
|
||||
assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(
|
||||
createFakeIme(false)))
|
||||
.isFalse();
|
||||
|
||||
// IME w/ non-Aux and non-ASCII-capable "keyboard" subtype
|
||||
assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(
|
||||
createFakeIme(false, createFakeSubtype("keyboard", false, false))))
|
||||
.isFalse();
|
||||
|
||||
// IME w/ non-Aux and ASCII-capable "keyboard" subtype
|
||||
assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(
|
||||
createFakeIme(false, createFakeSubtype("keyboard", false, true))))
|
||||
.isTrue();
|
||||
|
||||
// IME w/ Aux and ASCII-capable "keyboard" subtype
|
||||
assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(
|
||||
createFakeIme(true, createFakeSubtype("keyboard", true, true))))
|
||||
.isFalse();
|
||||
|
||||
// IME w/ non-Aux and ASCII-capable "voice" subtype
|
||||
assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(
|
||||
createFakeIme(false, createFakeSubtype("voice", false, true))))
|
||||
.isFalse();
|
||||
|
||||
// IME w/ non-Aux and non-ASCII-capable subtype + Non-Aux and ASCII-capable subtype
|
||||
assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(
|
||||
createFakeIme(false,
|
||||
createFakeSubtype("keyboard", false, true),
|
||||
createFakeSubtype("keyboard", false, false))))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
private static InputMethodInfo createFakeIme(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;
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
* 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.license;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.LooperMode;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class LicenseHtmlGeneratorFromXmlTest {
|
||||
private static final String VALID_OLD_XML_STRING =
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
|
||||
+ "<licenses>\n"
|
||||
+ "<file-name contentId=\"0\">/file0</file-name>\n"
|
||||
+ "<file-name contentId=\"0\">/file1</file-name>\n"
|
||||
+ "<file-content contentId=\"0\"><![CDATA[license content #0]]></file-content>\n"
|
||||
+ "</licenses>";
|
||||
|
||||
private static final String VALID_NEW_XML_STRING =
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
|
||||
+ "<licenses>\n"
|
||||
+ "<file-name contentId=\"0\" lib=\"libA\">/file0</file-name>\n"
|
||||
+ "<file-name contentId=\"0\" lib=\"libB\">/file1</file-name>\n"
|
||||
+ "<file-content contentId=\"0\"><![CDATA[license content #0]]></file-content>\n"
|
||||
+ "</licenses>";
|
||||
|
||||
private static final String INVALID_XML_STRING =
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
|
||||
+ "<licenses2>\n"
|
||||
+ "<file-name contentId=\"0\">/file0</file-name>\n"
|
||||
+ "<file-name contentId=\"0\">/file1</file-name>\n"
|
||||
+ "<file-content contentId=\"0\"><![CDATA[license content #0]]></file-content>\n"
|
||||
+ "</licenses2>";
|
||||
|
||||
private static final String HTML_HEAD_STRING =
|
||||
"<html><head>\n"
|
||||
+ "<style type=\"text/css\">\n"
|
||||
+ "body { padding: 0; font-family: sans-serif; }\n"
|
||||
+ ".same-license { background-color: #eeeeee;\n"
|
||||
+ " border-top: 20px solid white;\n"
|
||||
+ " padding: 10px; }\n"
|
||||
+ ".label { font-weight: bold; }\n"
|
||||
+ ".file-list { margin-left: 1em; color: blue; }\n"
|
||||
+ "</style>\n"
|
||||
+ "</head>"
|
||||
+ "<body topmargin=\"0\" leftmargin=\"0\" rightmargin=\"0\" bottommargin=\"0\">\n"
|
||||
+ "<div class=\"toc\">\n";
|
||||
|
||||
private static final String HTML_CUSTOM_HEADING = "Custom heading";
|
||||
|
||||
private static final String HTML_OLD_BODY_STRING =
|
||||
"<ul class=\"files\">\n"
|
||||
+ "<li><a href=\"#id0\">/file0</a></li>\n"
|
||||
+ "<li><a href=\"#id1\">/file0</a></li>\n"
|
||||
+ "<li><a href=\"#id0\">/file1</a></li>\n"
|
||||
+ "</ul>\n"
|
||||
+ "</div><!-- table of contents -->\n"
|
||||
+ "<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">\n"
|
||||
+ "<tr id=\"id0\"><td class=\"same-license\">\n"
|
||||
+ "<div class=\"label\">Notices for file(s):</div>\n"
|
||||
+ "<div class=\"file-list\">\n"
|
||||
+ "/file0 <br/>\n"
|
||||
+ "/file1 <br/>\n"
|
||||
+ "</div><!-- file-list -->\n"
|
||||
+ "<pre class=\"license-text\">\n"
|
||||
+ "license content #0\n"
|
||||
+ "</pre><!-- license-text -->\n"
|
||||
+ "</td></tr><!-- same-license -->\n"
|
||||
+ "<tr id=\"id1\"><td class=\"same-license\">\n"
|
||||
+ "<div class=\"label\">Notices for file(s):</div>\n"
|
||||
+ "<div class=\"file-list\">\n"
|
||||
+ "/file0 <br/>\n"
|
||||
+ "</div><!-- file-list -->\n"
|
||||
+ "<pre class=\"license-text\">\n"
|
||||
+ "license content #1\n"
|
||||
+ "</pre><!-- license-text -->\n"
|
||||
+ "</td></tr><!-- same-license -->\n"
|
||||
+ "</table>\n"
|
||||
+ "<div class=\"path-counts\"><table>\n"
|
||||
+ " <tr><th>Path prefix</th><th>Count</th></tr>\n\n"
|
||||
+ " <tr><td>file0</td><td>1</td></tr>\n"
|
||||
+ " <tr><td>file1</td><td>1</td></tr>\n"
|
||||
+ "</table></div>\n\n"
|
||||
+ "</body></html>\n";
|
||||
|
||||
private static final String HTML_NEW_BODY_STRING =
|
||||
"<strong>Libraries</strong>\n"
|
||||
+ "<ul class=\"libraries\">\n"
|
||||
+ "<li><a href=\"#id0\">libA</a></li>\n"
|
||||
+ "<li><a href=\"#id1\">libB</a></li>\n"
|
||||
+ "<li><a href=\"#id0\">libC</a></li>\n"
|
||||
+ "</ul>\n"
|
||||
+ "<strong>Files</strong>\n"
|
||||
+ "<ul class=\"files\">\n"
|
||||
+ "<li><a href=\"#id0\">/file0 - libA</a></li>\n"
|
||||
+ "<li><a href=\"#id1\">/file0 - libB</a></li>\n"
|
||||
+ "<li><a href=\"#id0\">/file1 - libA</a></li>\n"
|
||||
+ "<li><a href=\"#id0\">/file2 - libC</a></li>\n"
|
||||
+ "</ul>\n"
|
||||
+ "</div><!-- table of contents -->\n"
|
||||
+ "<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">\n"
|
||||
+ "<tr id=\"id0\"><td class=\"same-license\">\n"
|
||||
+ "<div class=\"label\"><strong>libA</strong> used by:</div>\n"
|
||||
+ "<div class=\"file-list\">\n"
|
||||
+ "/file0 <br/>\n"
|
||||
+ "/file1 <br/>\n"
|
||||
+ "</div><!-- file-list -->\n"
|
||||
+ "<div class=\"label\"><strong>libC</strong> used by:</div>\n"
|
||||
+ "<div class=\"file-list\">\n"
|
||||
+ "/file2 <br/>\n"
|
||||
+ "</div><!-- file-list -->\n"
|
||||
+ "<pre class=\"license-text\">\n"
|
||||
+ "license content #0\n"
|
||||
+ "</pre><!-- license-text -->\n"
|
||||
+ "</td></tr><!-- same-license -->\n"
|
||||
+ "<tr id=\"id1\"><td class=\"same-license\">\n"
|
||||
+ "<div class=\"label\"><strong>libB</strong> used by:</div>\n"
|
||||
+ "<div class=\"file-list\">\n"
|
||||
+ "/file0 <br/>\n"
|
||||
+ "</div><!-- file-list -->\n"
|
||||
+ "<pre class=\"license-text\">\n"
|
||||
+ "license content #1\n"
|
||||
+ "</pre><!-- license-text -->\n"
|
||||
+ "</td></tr><!-- same-license -->\n"
|
||||
+ "</table>\n"
|
||||
+ "<div class=\"path-counts\"><table>\n"
|
||||
+ " <tr><th>Path prefix</th><th>Count</th></tr>\n\n"
|
||||
+ " <tr><td>file0</td><td>1</td></tr>\n"
|
||||
+ " <tr><td>file1</td><td>1</td></tr>\n"
|
||||
+ " <tr><td>file2</td><td>1</td></tr>\n"
|
||||
+ "</table></div>\n\n"
|
||||
+ "</body></html>\n";
|
||||
|
||||
private static final String EXPECTED_OLD_HTML_STRING = HTML_HEAD_STRING + HTML_OLD_BODY_STRING;
|
||||
|
||||
private static final String EXPECTED_NEW_HTML_STRING = HTML_HEAD_STRING + HTML_NEW_BODY_STRING;
|
||||
|
||||
private static final String EXPECTED_OLD_HTML_STRING_WITH_CUSTOM_HEADING =
|
||||
HTML_HEAD_STRING + HTML_CUSTOM_HEADING + "\n<br/>\n" + HTML_OLD_BODY_STRING;
|
||||
|
||||
private static final String EXPECTED_NEW_HTML_STRING_WITH_CUSTOM_HEADING =
|
||||
HTML_HEAD_STRING + HTML_CUSTOM_HEADING + "\n<br/>\n" + HTML_NEW_BODY_STRING;
|
||||
|
||||
@Test
|
||||
public void testParseValidXmlStream() throws XmlPullParserException, IOException {
|
||||
Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
|
||||
Map<String, String> contentIdToFileContentMap = new HashMap<>();
|
||||
|
||||
LicenseHtmlGeneratorFromXml.parse(
|
||||
new InputStreamReader(new ByteArrayInputStream(VALID_OLD_XML_STRING.getBytes())),
|
||||
fileNameToLibraryToContentIdMap, contentIdToFileContentMap);
|
||||
|
||||
assertThat(fileNameToLibraryToContentIdMap).hasSize(2);
|
||||
assertThat(fileNameToLibraryToContentIdMap.get("/file0")).hasSize(1);
|
||||
assertThat(fileNameToLibraryToContentIdMap.get("/file1")).hasSize(1);
|
||||
assertThat(fileNameToLibraryToContentIdMap.get("/file0").get(null)).containsExactly("0");
|
||||
assertThat(fileNameToLibraryToContentIdMap.get("/file1").get(null)).containsExactly("0");
|
||||
assertThat(contentIdToFileContentMap.size()).isEqualTo(1);
|
||||
assertThat(contentIdToFileContentMap.get("0")).isEqualTo("license content #0");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseNewValidXmlStream() throws XmlPullParserException, IOException {
|
||||
Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
|
||||
Map<String, String> contentIdToFileContentMap = new HashMap<>();
|
||||
|
||||
LicenseHtmlGeneratorFromXml.parse(
|
||||
new InputStreamReader(new ByteArrayInputStream(VALID_NEW_XML_STRING.getBytes())),
|
||||
fileNameToLibraryToContentIdMap, contentIdToFileContentMap);
|
||||
|
||||
assertThat(fileNameToLibraryToContentIdMap).hasSize(2);
|
||||
assertThat(fileNameToLibraryToContentIdMap.get("/file0")).hasSize(1);
|
||||
assertThat(fileNameToLibraryToContentIdMap.get("/file1")).hasSize(1);
|
||||
assertThat(fileNameToLibraryToContentIdMap.get("/file0").get("libA")).containsExactly("0");
|
||||
assertThat(fileNameToLibraryToContentIdMap.get("/file1").get("libB")).containsExactly("0");
|
||||
assertThat(contentIdToFileContentMap.size()).isEqualTo(1);
|
||||
assertThat(contentIdToFileContentMap.get("0")).isEqualTo("license content #0");
|
||||
}
|
||||
|
||||
@Test(expected = XmlPullParserException.class)
|
||||
public void testParseInvalidXmlStream() throws XmlPullParserException, IOException {
|
||||
Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
|
||||
Map<String, String> contentIdToFileContentMap = new HashMap<>();
|
||||
|
||||
LicenseHtmlGeneratorFromXml.parse(
|
||||
new InputStreamReader(new ByteArrayInputStream(INVALID_XML_STRING.getBytes())),
|
||||
fileNameToLibraryToContentIdMap, contentIdToFileContentMap);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateHtml() throws Exception {
|
||||
List<File> xmlFiles = new ArrayList<>();
|
||||
Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
|
||||
Map<String, String> contentIdToFileContentMap = new HashMap<>();
|
||||
Map<String, Set<String>> toBoth = new HashMap<>();
|
||||
Map<String, Set<String>> toOne = new HashMap<>();
|
||||
|
||||
toBoth.put("", new HashSet<String>(Arrays.asList("0", "1")));
|
||||
toOne.put("", new HashSet<String>(Arrays.asList("0")));
|
||||
|
||||
fileNameToLibraryToContentIdMap.put("/file0", toBoth);
|
||||
fileNameToLibraryToContentIdMap.put("/file1", toOne);
|
||||
contentIdToFileContentMap.put("0", "license content #0");
|
||||
contentIdToFileContentMap.put("1", "license content #1");
|
||||
|
||||
StringWriter output = new StringWriter();
|
||||
LicenseHtmlGeneratorFromXml.generateHtml(
|
||||
xmlFiles, fileNameToLibraryToContentIdMap, contentIdToFileContentMap,
|
||||
new PrintWriter(output), "");
|
||||
assertThat(output.toString()).isEqualTo(EXPECTED_OLD_HTML_STRING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateNewHtml() throws Exception {
|
||||
List<File> xmlFiles = new ArrayList<>();
|
||||
Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
|
||||
Map<String, String> contentIdToFileContentMap = new HashMap<>();
|
||||
Map<String, Set<String>> toBoth = new HashMap<>();
|
||||
Map<String, Set<String>> toOne = new HashMap<>();
|
||||
Map<String, Set<String>> toOther = new HashMap<>();
|
||||
|
||||
toBoth.put("libA", new HashSet<String>(Arrays.asList("0")));
|
||||
toBoth.put("libB", new HashSet<String>(Arrays.asList("1")));
|
||||
toOne.put("libA", new HashSet<String>(Arrays.asList("0")));
|
||||
toOther.put("libC", new HashSet<String>(Arrays.asList("0")));
|
||||
|
||||
fileNameToLibraryToContentIdMap.put("/file0", toBoth);
|
||||
fileNameToLibraryToContentIdMap.put("/file1", toOne);
|
||||
fileNameToLibraryToContentIdMap.put("/file2", toOther);
|
||||
contentIdToFileContentMap.put("0", "license content #0");
|
||||
contentIdToFileContentMap.put("1", "license content #1");
|
||||
|
||||
StringWriter output = new StringWriter();
|
||||
LicenseHtmlGeneratorFromXml.generateHtml(
|
||||
xmlFiles, fileNameToLibraryToContentIdMap, contentIdToFileContentMap,
|
||||
new PrintWriter(output), "");
|
||||
assertThat(output.toString()).isEqualTo(EXPECTED_NEW_HTML_STRING);
|
||||
}
|
||||
|
||||
@Test
|
||||
@LooperMode(LooperMode.Mode.PAUSED)
|
||||
public void testGenerateHtmlWithCustomHeading() throws Exception {
|
||||
List<File> xmlFiles = new ArrayList<>();
|
||||
Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
|
||||
Map<String, String> contentIdToFileContentMap = new HashMap<>();
|
||||
Map<String, Set<String>> toBoth = new HashMap<>();
|
||||
Map<String, Set<String>> toOne = new HashMap<>();
|
||||
|
||||
toBoth.put("", new HashSet<String>(Arrays.asList("0", "1")));
|
||||
toOne.put("", new HashSet<String>(Arrays.asList("0")));
|
||||
|
||||
fileNameToLibraryToContentIdMap.put("/file0", toBoth);
|
||||
fileNameToLibraryToContentIdMap.put("/file1", toOne);
|
||||
contentIdToFileContentMap.put("0", "license content #0");
|
||||
contentIdToFileContentMap.put("1", "license content #1");
|
||||
|
||||
StringWriter output = new StringWriter();
|
||||
LicenseHtmlGeneratorFromXml.generateHtml(
|
||||
xmlFiles, fileNameToLibraryToContentIdMap, contentIdToFileContentMap,
|
||||
new PrintWriter(output), HTML_CUSTOM_HEADING);
|
||||
assertThat(output.toString()).isEqualTo(EXPECTED_OLD_HTML_STRING_WITH_CUSTOM_HEADING);
|
||||
}
|
||||
|
||||
@Test
|
||||
@LooperMode(LooperMode.Mode.PAUSED)
|
||||
public void testGenerateNewHtmlWithCustomHeading() throws Exception {
|
||||
List<File> xmlFiles = new ArrayList<>();
|
||||
Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
|
||||
Map<String, String> contentIdToFileContentMap = new HashMap<>();
|
||||
Map<String, Set<String>> toBoth = new HashMap<>();
|
||||
Map<String, Set<String>> toOne = new HashMap<>();
|
||||
Map<String, Set<String>> toOther = new HashMap<>();
|
||||
|
||||
toBoth.put("libA", new HashSet<String>(Arrays.asList("0")));
|
||||
toBoth.put("libB", new HashSet<String>(Arrays.asList("1")));
|
||||
toOne.put("libA", new HashSet<String>(Arrays.asList("0")));
|
||||
toOther.put("libC", new HashSet<String>(Arrays.asList("0")));
|
||||
|
||||
fileNameToLibraryToContentIdMap.put("/file0", toBoth);
|
||||
fileNameToLibraryToContentIdMap.put("/file1", toOne);
|
||||
fileNameToLibraryToContentIdMap.put("/file2", toOther);
|
||||
contentIdToFileContentMap.put("0", "license content #0");
|
||||
contentIdToFileContentMap.put("1", "license content #1");
|
||||
|
||||
StringWriter output = new StringWriter();
|
||||
LicenseHtmlGeneratorFromXml.generateHtml(
|
||||
xmlFiles, fileNameToLibraryToContentIdMap, contentIdToFileContentMap,
|
||||
new PrintWriter(output), HTML_CUSTOM_HEADING);
|
||||
assertThat(output.toString()).isEqualTo(EXPECTED_NEW_HTML_STRING_WITH_CUSTOM_HEADING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* 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.license;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
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.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
import org.robolectric.annotation.Resetter;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = LicenseHtmlLoaderCompatTest.ShadowLicenseHtmlLoaderCompat.class)
|
||||
public class LicenseHtmlLoaderCompatTest {
|
||||
|
||||
@Mock
|
||||
private Context mContext;
|
||||
private LicenseHtmlLoaderCompat mLoader;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mLoader = new LicenseHtmlLoaderCompat(mContext);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
ShadowLicenseHtmlLoaderCompat.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadInBackground() {
|
||||
ArrayList<File> xmlFiles = new ArrayList<>();
|
||||
xmlFiles.add(new File("test.xml"));
|
||||
File cachedHtmlFile = new File("test.html");
|
||||
|
||||
setupFakeData(xmlFiles, cachedHtmlFile, true, true);
|
||||
|
||||
assertThat(mLoader.loadInBackground()).isEqualTo(cachedHtmlFile);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadInBackgroundWithNoVaildXmlFiles() {
|
||||
ArrayList<File> xmlFiles = new ArrayList<>();
|
||||
File cachedHtmlFile = new File("test.html");
|
||||
|
||||
setupFakeData(xmlFiles, cachedHtmlFile, true, true);
|
||||
|
||||
assertThat(mLoader.loadInBackground()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadInBackgroundWithNonOutdatedCachedHtmlFile() {
|
||||
ArrayList<File> xmlFiles = new ArrayList<>();
|
||||
xmlFiles.add(new File("test.xml"));
|
||||
File cachedHtmlFile = new File("test.html");
|
||||
|
||||
setupFakeData(xmlFiles, cachedHtmlFile, false, true);
|
||||
|
||||
assertThat(mLoader.loadInBackground()).isEqualTo(cachedHtmlFile);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadInBackgroundWithGenerateHtmlFileFailed() {
|
||||
ArrayList<File> xmlFiles = new ArrayList<>();
|
||||
xmlFiles.add(new File("test.xml"));
|
||||
File cachedHtmlFile = new File("test.html");
|
||||
|
||||
setupFakeData(xmlFiles, cachedHtmlFile, true, false);
|
||||
|
||||
assertThat(mLoader.loadInBackground()).isNull();
|
||||
}
|
||||
|
||||
void setupFakeData(ArrayList<File> xmlFiles,
|
||||
File cachedHtmlFile, boolean isCachedHtmlFileOutdated,
|
||||
boolean generateHtmlFileSucceeded) {
|
||||
|
||||
ShadowLicenseHtmlLoaderCompat.sValidXmlFiles = xmlFiles;
|
||||
ShadowLicenseHtmlLoaderCompat.sCachedHtmlFile = cachedHtmlFile;
|
||||
ShadowLicenseHtmlLoaderCompat.sIsCachedHtmlFileOutdated = isCachedHtmlFileOutdated;
|
||||
ShadowLicenseHtmlLoaderCompat.sGenerateHtmlFileSucceeded = generateHtmlFileSucceeded;
|
||||
}
|
||||
|
||||
@Implements(LicenseHtmlLoaderCompat.class)
|
||||
public static class ShadowLicenseHtmlLoaderCompat {
|
||||
|
||||
private static List<File> sValidXmlFiles;
|
||||
private static File sCachedHtmlFile;
|
||||
private static boolean sIsCachedHtmlFileOutdated;
|
||||
private static boolean sGenerateHtmlFileSucceeded;
|
||||
|
||||
@Resetter
|
||||
public static void reset() {
|
||||
sValidXmlFiles = null;
|
||||
sCachedHtmlFile = null;
|
||||
sIsCachedHtmlFileOutdated = false;
|
||||
sGenerateHtmlFileSucceeded = false;
|
||||
}
|
||||
|
||||
@Implementation
|
||||
protected List<File> getVaildXmlFiles() {
|
||||
return sValidXmlFiles;
|
||||
}
|
||||
|
||||
@Implementation
|
||||
protected File getCachedHtmlFile(Context context) {
|
||||
return sCachedHtmlFile;
|
||||
}
|
||||
|
||||
@Implementation
|
||||
protected boolean isCachedHtmlFileOutdated(List<File> xmlFiles,
|
||||
File cachedHtmlFile) {
|
||||
return sIsCachedHtmlFileOutdated;
|
||||
}
|
||||
|
||||
@Implementation
|
||||
protected boolean generateHtmlFile(Context context, List<File> xmlFiles,
|
||||
File htmlFile) {
|
||||
return sGenerateHtmlFileSucceeded;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.location;
|
||||
|
||||
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 final class InjectedSettingTest {
|
||||
|
||||
private static final String TEST_STRING = "test";
|
||||
|
||||
@Test
|
||||
public void buildWithoutPackageName_ShouldReturnNull() {
|
||||
assertThat(((new InjectedSetting.Builder())
|
||||
.setClassName(TEST_STRING)
|
||||
.setTitle(TEST_STRING)
|
||||
.setSettingsActivity(TEST_STRING).build())).isNull();
|
||||
}
|
||||
|
||||
private InjectedSetting getTestSetting() {
|
||||
return new InjectedSetting.Builder()
|
||||
.setPackageName(TEST_STRING)
|
||||
.setClassName(TEST_STRING)
|
||||
.setTitle(TEST_STRING)
|
||||
.setSettingsActivity(TEST_STRING).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals() {
|
||||
InjectedSetting setting1 = getTestSetting();
|
||||
InjectedSetting setting2 = getTestSetting();
|
||||
assertThat(setting1).isEqualTo(setting2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHashCode() {
|
||||
InjectedSetting setting = getTestSetting();
|
||||
assertThat(setting.hashCode()).isEqualTo(1225314048);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package com.android.settingslib.location;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.isA;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.AppOpsManager.OpEntry;
|
||||
import android.app.AppOpsManager.AttributedOpEntry;
|
||||
import android.app.AppOpsManager.PackageOps;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Process;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.LongSparseArray;
|
||||
|
||||
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.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class RecentLocationAppsTest {
|
||||
|
||||
private static final int TEST_UID = 1234;
|
||||
private static final long NOW = System.currentTimeMillis();
|
||||
// App running duration in milliseconds
|
||||
private static final int DURATION = 10;
|
||||
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 Context mContext;
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
@Mock
|
||||
private AppOpsManager mAppOpsManager;
|
||||
@Mock
|
||||
private Resources mResources;
|
||||
@Mock
|
||||
private UserManager mUserManager;
|
||||
private int mTestUserId;
|
||||
private RecentLocationApps mRecentLocationApps;
|
||||
|
||||
@Before
|
||||
public void setUp() throws NameNotFoundException {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
when(mContext.getPackageManager()).thenReturn(mPackageManager);
|
||||
when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
|
||||
when(mContext.getResources()).thenReturn(mResources);
|
||||
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");
|
||||
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(RecentLocationApps.LOCATION_REQUEST_OPS)).thenReturn(
|
||||
appOps);
|
||||
mockTestApplicationInfos(mTestUserId, TEST_PACKAGE_NAMES);
|
||||
|
||||
mRecentLocationApps = new RecentLocationApps(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAppList_shouldFilterRecentApps() {
|
||||
List<RecentLocationApps.Request> requests = mRecentLocationApps.getAppList(true);
|
||||
// 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).requestFinishTime).isEqualTo(ONE_MIN_AGO + DURATION);
|
||||
assertThat(requests.get(1).packageName).isEqualTo(TEST_PACKAGE_NAMES[1]);
|
||||
assertThat(requests.get(1).requestFinishTime).isEqualTo(TWENTY_THREE_HOURS_AGO + DURATION);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAppList_shouldNotShowAndroidOS() throws NameNotFoundException {
|
||||
// Add android OS to the list of apps.
|
||||
PackageOps androidSystemPackageOps =
|
||||
createPackageOps(
|
||||
RecentLocationApps.ANDROID_SYSTEM_PACKAGE_NAME,
|
||||
Process.SYSTEM_UID,
|
||||
AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION,
|
||||
ONE_MIN_AGO,
|
||||
DURATION);
|
||||
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(RecentLocationApps.LOCATION_REQUEST_OPS)).thenReturn(
|
||||
appOps);
|
||||
mockTestApplicationInfos(
|
||||
Process.SYSTEM_UID, RecentLocationApps.ANDROID_SYSTEM_PACKAGE_NAME);
|
||||
|
||||
List<RecentLocationApps.Request> requests = mRecentLocationApps.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).requestFinishTime).isEqualTo(ONE_MIN_AGO + DURATION);
|
||||
assertThat(requests.get(1).packageName).isEqualTo(TEST_PACKAGE_NAMES[1]);
|
||||
assertThat(requests.get(1).requestFinishTime).isEqualTo(TWENTY_THREE_HOURS_AGO + DURATION);
|
||||
}
|
||||
|
||||
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_MONITOR_LOCATION,
|
||||
time[i],
|
||||
DURATION);
|
||||
packageOpsList.add(packageOps);
|
||||
}
|
||||
return packageOpsList;
|
||||
}
|
||||
|
||||
private PackageOps createPackageOps(
|
||||
String packageName, int uid, int op, long time, int duration) {
|
||||
return new PackageOps(
|
||||
packageName,
|
||||
uid,
|
||||
Collections.singletonList(createOpEntryWithTime(op, time, duration)));
|
||||
}
|
||||
|
||||
private OpEntry createOpEntryWithTime(int op, long time, int duration) {
|
||||
final LongSparseArray<AppOpsManager.NoteOpEvent> accessEvents = new LongSparseArray<>();
|
||||
accessEvents.put(AppOpsManager.makeKey(AppOpsManager.UID_STATE_TOP,
|
||||
AppOpsManager.OP_FLAG_SELF), new AppOpsManager.NoteOpEvent(time, duration, null));
|
||||
|
||||
return new OpEntry(op, AppOpsManager.MODE_ALLOWED, Collections.singletonMap(null,
|
||||
new AttributedOpEntry(op, false, accessEvents, null)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright 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.media;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
|
||||
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 BluetoothMediaDeviceTest {
|
||||
|
||||
private static final String TEST_ADDRESS = "11:22:33:44:55:66";
|
||||
|
||||
@Mock
|
||||
private CachedBluetoothDevice mDevice;
|
||||
|
||||
private Context mContext;
|
||||
private BluetoothMediaDevice mBluetoothMediaDevice;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
|
||||
when(mDevice.isActiveDevice(BluetoothProfile.A2DP)).thenReturn(true);
|
||||
when(mDevice.isActiveDevice(BluetoothProfile.HEARING_AID)).thenReturn(true);
|
||||
when(mDevice.isActiveDevice(BluetoothProfile.LE_AUDIO)).thenReturn(true);
|
||||
|
||||
mBluetoothMediaDevice = new BluetoothMediaDevice(mContext, mDevice, null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isCachedBluetoothDeviceConnected_deviceConnected_returnTrue() {
|
||||
when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
|
||||
when(mDevice.isConnected()).thenReturn(true);
|
||||
|
||||
assertThat(mBluetoothMediaDevice.isConnected()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isCachedBluetoothDeviceConnected_deviceNotConnected_returnFalse() {
|
||||
when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
|
||||
when(mDevice.isConnected()).thenReturn(false);
|
||||
|
||||
assertThat(mBluetoothMediaDevice.isConnected()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isFastPairDevice_isUntetheredHeadset_returnTrue() {
|
||||
final BluetoothDevice bluetoothDevice = mock(BluetoothDevice.class);
|
||||
when(mDevice.getDevice()).thenReturn(bluetoothDevice);
|
||||
|
||||
final String value = "True";
|
||||
final byte[] bytes = value.getBytes();
|
||||
when(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
|
||||
.thenReturn(bytes);
|
||||
|
||||
assertThat(mBluetoothMediaDevice.isFastPairDevice()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isFastPairDevice_isNotUntetheredHeadset_returnFalse() {
|
||||
final BluetoothDevice bluetoothDevice = mock(BluetoothDevice.class);
|
||||
when(mDevice.getDevice()).thenReturn(bluetoothDevice);
|
||||
|
||||
final String value = "asjdaioshfaio";
|
||||
final byte[] bytes = value.getBytes();
|
||||
when(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
|
||||
.thenReturn(bytes);
|
||||
|
||||
assertThat(mBluetoothMediaDevice.isFastPairDevice()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIcon_isNotFastPairDevice_drawableTypeIsNotBitmapDrawable() {
|
||||
final BluetoothDevice bluetoothDevice = mock(BluetoothDevice.class);
|
||||
when(mDevice.getDevice()).thenReturn(bluetoothDevice);
|
||||
|
||||
final String value = "False";
|
||||
final byte[] bytes = value.getBytes();
|
||||
when(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
|
||||
.thenReturn(bytes);
|
||||
|
||||
assertThat(mBluetoothMediaDevice.getIcon() instanceof BitmapDrawable).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getId_returnsCachedBluetoothDeviceAddress() {
|
||||
when(mDevice.getAddress()).thenReturn(TEST_ADDRESS);
|
||||
assertThat(mBluetoothMediaDevice.getId()).isEqualTo(TEST_ADDRESS);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* Copyright 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.media;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.media.AudioDeviceInfo;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.platform.test.flag.junit.SetFlagsRule;
|
||||
|
||||
import com.android.settingslib.R;
|
||||
import com.android.settingslib.media.flags.Flags;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class DeviceIconUtilTest {
|
||||
|
||||
@Rule
|
||||
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TV_MEDIA_OUTPUT_DIALOG);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromMediaRouteType_usbDevice_isHeadphone() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ false)
|
||||
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_USB_DEVICE))
|
||||
.isEqualTo(R.drawable.ic_headphone);
|
||||
assertThat(new DeviceIconUtil(/* isTv */ true)
|
||||
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_USB_DEVICE))
|
||||
.isEqualTo(R.drawable.ic_headphone);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromMediaRouteType_usbHeadset_isHeadphone() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ false)
|
||||
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_USB_HEADSET))
|
||||
.isEqualTo(R.drawable.ic_headphone);
|
||||
assertThat(new DeviceIconUtil(/* isTv */ true)
|
||||
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_USB_HEADSET))
|
||||
.isEqualTo(R.drawable.ic_headphone);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromMediaRouteType_usbAccessory_isHeadphone() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ false)
|
||||
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_USB_ACCESSORY))
|
||||
.isEqualTo(R.drawable.ic_headphone);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromMediaRouteType_tv_usbAccessory_isUsb() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ true)
|
||||
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_USB_ACCESSORY))
|
||||
.isEqualTo(R.drawable.ic_usb);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromMediaRouteType_dock_isDock() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ false)
|
||||
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_DOCK))
|
||||
.isEqualTo(R.drawable.ic_dock_device);
|
||||
assertThat(new DeviceIconUtil(/* isTv */ true)
|
||||
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_DOCK))
|
||||
.isEqualTo(R.drawable.ic_dock_device);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromMediaRouteType_hdmi() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ false)
|
||||
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_HDMI))
|
||||
.isEqualTo(R.drawable.ic_external_display);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromMediaRouteType_tv_hdmi_isTv() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ true)
|
||||
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_HDMI))
|
||||
.isEqualTo(R.drawable.ic_tv);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromMediaRouteType_hdmiArc_isExternalDisplay() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ false)
|
||||
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_HDMI_ARC))
|
||||
.isEqualTo(R.drawable.ic_external_display);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromMediaRouteType_tv_hdmiArc_isHdmi() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ true)
|
||||
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_HDMI_ARC))
|
||||
.isEqualTo(R.drawable.ic_hdmi);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromMediaRouteType_hdmiEarc_isExternalDisplay() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ false)
|
||||
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_HDMI_EARC))
|
||||
.isEqualTo(R.drawable.ic_external_display);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromMediaRouteType_tv_hdmiEarc_isHdmi() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ true)
|
||||
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_HDMI_EARC))
|
||||
.isEqualTo(R.drawable.ic_hdmi);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromMediaRouteType_wiredHeadset_isHeadphone() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ false)
|
||||
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_WIRED_HEADSET))
|
||||
.isEqualTo(R.drawable.ic_headphone);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromMediaRouteType_tv_wiredHeadset_isWiredDevice() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ true)
|
||||
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_WIRED_HEADSET))
|
||||
.isEqualTo(R.drawable.ic_wired_device);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromMediaRouteType_wiredHeadphones_isHeadphone() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ false)
|
||||
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_WIRED_HEADPHONES))
|
||||
.isEqualTo(R.drawable.ic_headphone);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromMediaRouteType_tv_wiredHeadphones_isWiredDevice() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ true)
|
||||
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_WIRED_HEADPHONES))
|
||||
.isEqualTo(R.drawable.ic_wired_device);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromMediaRouteType_builtinSpeaker_isSmartphone() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ false)
|
||||
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_BUILTIN_SPEAKER))
|
||||
.isEqualTo(R.drawable.ic_smartphone);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromMediaRouteType_tv_builtinSpeaker_isTv() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ true)
|
||||
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_BUILTIN_SPEAKER))
|
||||
.isEqualTo(R.drawable.ic_tv);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromMediaRouteType_unsupportedType_isSmartphone() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ false)
|
||||
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_UNKNOWN))
|
||||
.isEqualTo(R.drawable.ic_smartphone);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromMediaRouteType_tv_unsupportedType_isSpeaker() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ true)
|
||||
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_UNKNOWN))
|
||||
.isEqualTo(R.drawable.ic_media_speaker_device);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromAudioDeviceType_usbDevice_isHeadphone() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ false)
|
||||
.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_USB_DEVICE))
|
||||
.isEqualTo(R.drawable.ic_headphone);
|
||||
assertThat(new DeviceIconUtil(/* isTv */ true)
|
||||
.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_USB_DEVICE))
|
||||
.isEqualTo(R.drawable.ic_headphone);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromAudioDeviceType_usbHeadset_isHeadphone() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ false)
|
||||
.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_USB_HEADSET))
|
||||
.isEqualTo(R.drawable.ic_headphone);
|
||||
assertThat(new DeviceIconUtil(/* isTv */ true)
|
||||
.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_USB_HEADSET))
|
||||
.isEqualTo(R.drawable.ic_headphone);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromAudioDeviceType_usbAccessory_isHeadphone() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ false)
|
||||
.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_USB_ACCESSORY))
|
||||
.isEqualTo(R.drawable.ic_headphone);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromAudioDeviceType_tv_usbAccessory_isUsb() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ true)
|
||||
.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_USB_ACCESSORY))
|
||||
.isEqualTo(R.drawable.ic_usb);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromAudioDeviceType_dock_isDock() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ false)
|
||||
.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_DOCK))
|
||||
.isEqualTo(R.drawable.ic_dock_device);
|
||||
assertThat(new DeviceIconUtil(/* isTv */ true)
|
||||
.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_DOCK))
|
||||
.isEqualTo(R.drawable.ic_dock_device);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromAudioDeviceType_hdmi_isExternalDisplay() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ false)
|
||||
.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_HDMI))
|
||||
.isEqualTo(R.drawable.ic_external_display);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromAudioDeviceType_tv_hdmi_isTv() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ true)
|
||||
.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_HDMI))
|
||||
.isEqualTo(R.drawable.ic_tv);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromAudioDeviceType_hdmiArc_isExternalDisplay() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ false)
|
||||
.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_HDMI_ARC))
|
||||
.isEqualTo(R.drawable.ic_external_display);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromAudioDeviceType_hdmiArc_isHdmi() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ true)
|
||||
.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_HDMI_ARC))
|
||||
.isEqualTo(R.drawable.ic_hdmi);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromAudioDeviceType_hdmiEarc_isExternalDisplay() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ false)
|
||||
.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_HDMI_EARC))
|
||||
.isEqualTo(R.drawable.ic_external_display);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromAudioDeviceType_tv_hdmiEarc() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ true)
|
||||
.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_HDMI_EARC))
|
||||
.isEqualTo(R.drawable.ic_hdmi);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromAudioDeviceType_wiredHeadset_isHeadphone() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ false)
|
||||
.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_WIRED_HEADSET))
|
||||
.isEqualTo(R.drawable.ic_headphone);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromAudioDeviceType_tv_wiredHeadset_isWiredDevice() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ true)
|
||||
.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_WIRED_HEADSET))
|
||||
.isEqualTo(R.drawable.ic_wired_device);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromAudioDeviceType_wiredHeadphones_isHeadphone() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ false)
|
||||
.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_WIRED_HEADPHONES))
|
||||
.isEqualTo(R.drawable.ic_headphone);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromAudioDeviceType_tv_wiredHeadphones_isWiredDevice() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ true)
|
||||
.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_WIRED_HEADPHONES))
|
||||
.isEqualTo(R.drawable.ic_wired_device);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromAudioDeviceType_builtinSpeaker_isSmartphone() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ false)
|
||||
.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER))
|
||||
.isEqualTo(R.drawable.ic_smartphone);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromAudioDeviceType_tv_builtinSpeaker_isTv() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ true)
|
||||
.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER))
|
||||
.isEqualTo(R.drawable.ic_tv);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromAudioDeviceType_unsupportedType_isSmartphone() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ false)
|
||||
.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_UNKNOWN))
|
||||
.isEqualTo(R.drawable.ic_smartphone);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIconResIdFromAudioDeviceType_tv_unsupportedType_isSpeaker() {
|
||||
assertThat(new DeviceIconUtil(/* isTv */ true)
|
||||
.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_UNKNOWN))
|
||||
.isEqualTo(R.drawable.ic_media_speaker_device);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright 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.media;
|
||||
|
||||
import static android.media.MediaRoute2Info.TYPE_GROUP;
|
||||
import static android.media.MediaRoute2Info.TYPE_REMOTE_CAR;
|
||||
import static android.media.MediaRoute2Info.TYPE_REMOTE_COMPUTER;
|
||||
import static android.media.MediaRoute2Info.TYPE_REMOTE_GAME_CONSOLE;
|
||||
import static android.media.MediaRoute2Info.TYPE_REMOTE_SMARTPHONE;
|
||||
import static android.media.MediaRoute2Info.TYPE_REMOTE_SMARTWATCH;
|
||||
import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
|
||||
import static android.media.MediaRoute2Info.TYPE_REMOTE_TABLET;
|
||||
import static android.media.MediaRoute2Info.TYPE_REMOTE_TABLET_DOCKED;
|
||||
import static android.media.MediaRoute2Info.TYPE_REMOTE_TV;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.media.MediaRouter2Manager;
|
||||
|
||||
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 InfoMediaDeviceTest {
|
||||
|
||||
private static final String TEST_PACKAGE_NAME = "com.test.packagename";
|
||||
private static final String TEST_ID = "test_id";
|
||||
private static final String TEST_NAME = "test_name";
|
||||
|
||||
@Mock
|
||||
private MediaRouter2Manager mRouterManager;
|
||||
@Mock
|
||||
private MediaRoute2Info mRouteInfo;
|
||||
|
||||
private Context mContext;
|
||||
private InfoMediaDevice mInfoMediaDevice;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
|
||||
mInfoMediaDevice = new InfoMediaDevice(mContext, mRouteInfo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getName_shouldReturnName() {
|
||||
when(mRouteInfo.getName()).thenReturn(TEST_NAME);
|
||||
|
||||
assertThat(mInfoMediaDevice.getName()).isEqualTo(TEST_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_clientPackageNameIsNull_returnNull() {
|
||||
when(mRouteInfo.getClientPackageName()).thenReturn(null);
|
||||
|
||||
assertThat(mInfoMediaDevice.getSummary()).isEqualTo(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_clientPackageNameIsNotNull_returnActive() {
|
||||
when(mRouteInfo.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
|
||||
assertThat(mInfoMediaDevice.getSummary())
|
||||
.isEqualTo(mContext.getString(R.string.bluetooth_active_no_battery_level));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getId_shouldReturnId() {
|
||||
when(mRouteInfo.getId()).thenReturn(TEST_ID);
|
||||
|
||||
assertThat(mInfoMediaDevice.getId()).isEqualTo(TEST_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDrawableResId_returnCorrectResId() {
|
||||
when(mRouteInfo.getType()).thenReturn(TYPE_REMOTE_TV);
|
||||
|
||||
assertThat(mInfoMediaDevice.getDrawableResIdByType()).isEqualTo(
|
||||
R.drawable.ic_media_display_device);
|
||||
|
||||
when(mRouteInfo.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
|
||||
|
||||
assertThat(mInfoMediaDevice.getDrawableResIdByType()).isEqualTo(
|
||||
R.drawable.ic_media_speaker_device);
|
||||
|
||||
when(mRouteInfo.getType()).thenReturn(TYPE_GROUP);
|
||||
|
||||
assertThat(mInfoMediaDevice.getDrawableResIdByType()).isEqualTo(
|
||||
R.drawable.ic_media_group_device);
|
||||
|
||||
when(mRouteInfo.getType()).thenReturn(TYPE_REMOTE_TABLET);
|
||||
|
||||
assertThat(mInfoMediaDevice.getDrawableResIdByType()).isEqualTo(
|
||||
R.drawable.ic_media_tablet);
|
||||
|
||||
when(mRouteInfo.getType()).thenReturn(TYPE_REMOTE_TABLET_DOCKED);
|
||||
|
||||
assertThat(mInfoMediaDevice.getDrawableResIdByType()).isEqualTo(
|
||||
R.drawable.ic_dock_device);
|
||||
|
||||
when(mRouteInfo.getType()).thenReturn(TYPE_REMOTE_COMPUTER);
|
||||
|
||||
assertThat(mInfoMediaDevice.getDrawableResIdByType()).isEqualTo(
|
||||
R.drawable.ic_media_computer);
|
||||
|
||||
when(mRouteInfo.getType()).thenReturn(TYPE_REMOTE_GAME_CONSOLE);
|
||||
|
||||
assertThat(mInfoMediaDevice.getDrawableResIdByType()).isEqualTo(
|
||||
R.drawable.ic_media_game_console);
|
||||
|
||||
when(mRouteInfo.getType()).thenReturn(TYPE_REMOTE_CAR);
|
||||
|
||||
assertThat(mInfoMediaDevice.getDrawableResIdByType()).isEqualTo(
|
||||
R.drawable.ic_media_car);
|
||||
|
||||
when(mRouteInfo.getType()).thenReturn(TYPE_REMOTE_SMARTWATCH);
|
||||
|
||||
assertThat(mInfoMediaDevice.getDrawableResIdByType()).isEqualTo(
|
||||
R.drawable.ic_media_smartwatch);
|
||||
|
||||
when(mRouteInfo.getType()).thenReturn(TYPE_REMOTE_SMARTPHONE);
|
||||
|
||||
assertThat(mInfoMediaDevice.getDrawableResIdByType()).isEqualTo(R.drawable.ic_smartphone);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,880 @@
|
||||
/*
|
||||
* Copyright 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.media;
|
||||
|
||||
import static android.media.MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
|
||||
import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
|
||||
import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
|
||||
import static android.media.MediaRoute2Info.TYPE_REMOTE_TV;
|
||||
import static android.media.MediaRoute2Info.TYPE_USB_DEVICE;
|
||||
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
|
||||
import static android.media.MediaRoute2ProviderService.REASON_NETWORK_ERROR;
|
||||
import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;
|
||||
|
||||
import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_SELECTED;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
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 android.bluetooth.BluetoothDevice;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.media.MediaRouter2Manager;
|
||||
import android.media.RouteListingPreference;
|
||||
import android.media.RoutingSessionInfo;
|
||||
import android.media.session.MediaSessionManager;
|
||||
import android.os.Build;
|
||||
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.settingslib.testutils.shadow.ShadowRouter2Manager;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
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.util.ReflectionHelpers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {ShadowRouter2Manager.class})
|
||||
public class InfoMediaManagerTest {
|
||||
|
||||
private static final String TEST_PACKAGE_NAME = "com.test.packagename";
|
||||
private static final String TEST_PACKAGE_NAME_2 = "com.test.packagename2";
|
||||
private static final String TEST_ID = "test_id";
|
||||
private static final String TEST_ID_1 = "test_id_1";
|
||||
private static final String TEST_ID_2 = "test_id_2";
|
||||
private static final String TEST_ID_3 = "test_id_3";
|
||||
private static final String TEST_ID_4 = "test_id_4";
|
||||
|
||||
private static final String TEST_NAME = "test_name";
|
||||
private static final String TEST_DUPLICATED_ID_1 = "test_duplicated_id_1";
|
||||
private static final String TEST_DUPLICATED_ID_2 = "test_duplicated_id_2";
|
||||
private static final String TEST_DUPLICATED_ID_3 = "test_duplicated_id_3";
|
||||
|
||||
@Mock
|
||||
private MediaRouter2Manager mRouterManager;
|
||||
@Mock
|
||||
private LocalBluetoothManager mLocalBluetoothManager;
|
||||
@Mock
|
||||
private MediaManager.MediaDeviceCallback mCallback;
|
||||
@Mock
|
||||
private MediaSessionManager mMediaSessionManager;
|
||||
@Mock
|
||||
private ComponentName mComponentName;
|
||||
|
||||
private ManagerInfoMediaManager mInfoMediaManager;
|
||||
private Context mContext;
|
||||
private ShadowRouter2Manager mShadowRouter2Manager;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
|
||||
doReturn(mMediaSessionManager).when(mContext).getSystemService(
|
||||
Context.MEDIA_SESSION_SERVICE);
|
||||
mInfoMediaManager =
|
||||
new ManagerInfoMediaManager(
|
||||
mContext, TEST_PACKAGE_NAME, null, mLocalBluetoothManager);
|
||||
mShadowRouter2Manager = ShadowRouter2Manager.getShadow();
|
||||
mInfoMediaManager.mRouterManager = MediaRouter2Manager.getInstance(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stopScan_notStartFirst_notCallsUnregister() {
|
||||
mInfoMediaManager.mRouterManager = mRouterManager;
|
||||
mInfoMediaManager.stopScan();
|
||||
|
||||
verify(mRouterManager, never()).unregisterScanRequest();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stopScan_startFirst_callsUnregister() {
|
||||
RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
|
||||
mInfoMediaManager.mRouterManager = mRouterManager;
|
||||
// Since test is running in Robolectric, return a fake session to avoid NPE.
|
||||
when(mRouterManager.getRoutingSessions(anyString())).thenReturn(List.of(sessionInfo));
|
||||
|
||||
mInfoMediaManager.startScan();
|
||||
mInfoMediaManager.stopScan();
|
||||
|
||||
verify(mRouterManager).unregisterScanRequest();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onRouteAdded_getAvailableRoutes_shouldAddMediaDevice() {
|
||||
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
|
||||
final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
|
||||
routingSessionInfos.add(sessionInfo);
|
||||
final List<String> selectedRoutes = new ArrayList<>();
|
||||
selectedRoutes.add(TEST_ID);
|
||||
when(sessionInfo.getSelectedRoutes()).thenReturn(selectedRoutes);
|
||||
mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
|
||||
|
||||
final MediaRoute2Info info = mock(MediaRoute2Info.class);
|
||||
when(info.getId()).thenReturn(TEST_ID);
|
||||
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info.getDeduplicationIds()).thenReturn(Set.of());
|
||||
|
||||
final List<MediaRoute2Info> routes = new ArrayList<>();
|
||||
routes.add(info);
|
||||
mShadowRouter2Manager.setTransferableRoutes(routes);
|
||||
|
||||
final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
|
||||
assertThat(mediaDevice).isNull();
|
||||
|
||||
mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
|
||||
|
||||
final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
|
||||
assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
|
||||
assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(infoDevice);
|
||||
assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSessionReleased_shouldUpdateConnectedDevice() {
|
||||
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
|
||||
final RoutingSessionInfo sessionInfo1 = mock(RoutingSessionInfo.class);
|
||||
routingSessionInfos.add(sessionInfo1);
|
||||
final RoutingSessionInfo sessionInfo2 = mock(RoutingSessionInfo.class);
|
||||
routingSessionInfos.add(sessionInfo2);
|
||||
|
||||
final List<String> selectedRoutesSession1 = new ArrayList<>();
|
||||
selectedRoutesSession1.add(TEST_ID_1);
|
||||
when(sessionInfo1.getSelectedRoutes()).thenReturn(selectedRoutesSession1);
|
||||
|
||||
final List<String> selectedRoutesSession2 = new ArrayList<>();
|
||||
selectedRoutesSession2.add(TEST_ID_2);
|
||||
when(sessionInfo2.getSelectedRoutes()).thenReturn(selectedRoutesSession2);
|
||||
|
||||
mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
|
||||
|
||||
final MediaRoute2Info info1 = mock(MediaRoute2Info.class);
|
||||
when(info1.getId()).thenReturn(TEST_ID_1);
|
||||
when(info1.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
|
||||
final MediaRoute2Info info2 = mock(MediaRoute2Info.class);
|
||||
when(info2.getId()).thenReturn(TEST_ID_2);
|
||||
when(info2.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
|
||||
final List<MediaRoute2Info> routes = new ArrayList<>();
|
||||
routes.add(info1);
|
||||
routes.add(info2);
|
||||
mShadowRouter2Manager.setAllRoutes(routes);
|
||||
mShadowRouter2Manager.setTransferableRoutes(routes);
|
||||
|
||||
final MediaDevice mediaDevice1 = mInfoMediaManager.findMediaDevice(TEST_ID_1);
|
||||
assertThat(mediaDevice1).isNull();
|
||||
final MediaDevice mediaDevice2 = mInfoMediaManager.findMediaDevice(TEST_ID_2);
|
||||
assertThat(mediaDevice2).isNull();
|
||||
|
||||
mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
|
||||
final MediaDevice infoDevice1 = mInfoMediaManager.mMediaDevices.get(0);
|
||||
assertThat(infoDevice1.getId()).isEqualTo(TEST_ID_1);
|
||||
final MediaDevice infoDevice2 = mInfoMediaManager.mMediaDevices.get(1);
|
||||
assertThat(infoDevice2.getId()).isEqualTo(TEST_ID_2);
|
||||
// The active routing session is the last one in the list, which maps to infoDevice2.
|
||||
assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(infoDevice2);
|
||||
|
||||
routingSessionInfos.remove(sessionInfo2);
|
||||
mInfoMediaManager.mMediaRouterCallback.onSessionReleased(sessionInfo2);
|
||||
assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(infoDevice1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferredFeaturesChanged_samePackageName_shouldAddMediaDevice() {
|
||||
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
|
||||
final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
|
||||
routingSessionInfos.add(sessionInfo);
|
||||
final List<String> selectedRoutes = new ArrayList<>();
|
||||
selectedRoutes.add(TEST_ID);
|
||||
when(sessionInfo.getSelectedRoutes()).thenReturn(selectedRoutes);
|
||||
mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
|
||||
|
||||
final MediaRoute2Info info = mock(MediaRoute2Info.class);
|
||||
when(info.getId()).thenReturn(TEST_ID);
|
||||
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info.getDeduplicationIds()).thenReturn(Set.of());
|
||||
|
||||
final List<MediaRoute2Info> routes = new ArrayList<>();
|
||||
routes.add(info);
|
||||
mShadowRouter2Manager.setTransferableRoutes(routes);
|
||||
|
||||
final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
|
||||
assertThat(mediaDevice).isNull();
|
||||
|
||||
mInfoMediaManager.mMediaRouterCallback.onPreferredFeaturesChanged(TEST_PACKAGE_NAME, null);
|
||||
|
||||
final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
|
||||
assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
|
||||
assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(infoDevice);
|
||||
assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferredFeaturesChanged_differentPackageName_doNothing() {
|
||||
mInfoMediaManager.mMediaRouterCallback.onPreferredFeaturesChanged("com.fake.play", null);
|
||||
|
||||
assertThat(mInfoMediaManager.mMediaDevices).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onRoutesChanged_getAvailableRoutes_shouldAddMediaDevice() {
|
||||
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
|
||||
final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
|
||||
routingSessionInfos.add(sessionInfo);
|
||||
final List<String> selectedRoutes = new ArrayList<>();
|
||||
selectedRoutes.add(TEST_ID);
|
||||
when(sessionInfo.getSelectedRoutes()).thenReturn(selectedRoutes);
|
||||
mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
|
||||
|
||||
final MediaRoute2Info info = mock(MediaRoute2Info.class);
|
||||
when(info.getId()).thenReturn(TEST_ID);
|
||||
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info.getDeduplicationIds()).thenReturn(Set.of());
|
||||
|
||||
final List<MediaRoute2Info> routes = new ArrayList<>();
|
||||
routes.add(info);
|
||||
mShadowRouter2Manager.setTransferableRoutes(routes);
|
||||
|
||||
final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
|
||||
assertThat(mediaDevice).isNull();
|
||||
|
||||
mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
|
||||
|
||||
final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
|
||||
assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
|
||||
assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(infoDevice);
|
||||
assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onRoutesChanged_getAvailableRoutes_shouldFilterDevice() {
|
||||
ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT",
|
||||
Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
|
||||
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
|
||||
final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
|
||||
routingSessionInfos.add(sessionInfo);
|
||||
|
||||
final List<String> selectedRoutes = new ArrayList<>();
|
||||
selectedRoutes.add(TEST_ID);
|
||||
when(sessionInfo.getSelectedRoutes()).thenReturn(selectedRoutes);
|
||||
mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
|
||||
|
||||
mShadowRouter2Manager.setTransferableRoutes(getRoutesListWithDuplicatedIds());
|
||||
|
||||
final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
|
||||
assertThat(mediaDevice).isNull();
|
||||
|
||||
mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
|
||||
|
||||
final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
|
||||
assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
|
||||
assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(infoDevice);
|
||||
assertThat(mInfoMediaManager.mMediaDevices).hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onRouteChanged_getAvailableRoutesWithPreferenceListExit_ordersRoutes() {
|
||||
RouteListingPreference routeListingPreference = setUpPreferenceList(TEST_PACKAGE_NAME);
|
||||
setUpSelectedRoutes(TEST_PACKAGE_NAME);
|
||||
|
||||
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
|
||||
final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
|
||||
routingSessionInfos.add(sessionInfo);
|
||||
|
||||
when(mRouterManager.getRoutingSessions(TEST_PACKAGE_NAME)).thenReturn(routingSessionInfos);
|
||||
when(sessionInfo.getSelectedRoutes()).thenReturn(ImmutableList.of(TEST_ID));
|
||||
|
||||
setAvailableRoutesList(TEST_PACKAGE_NAME);
|
||||
|
||||
mInfoMediaManager.mRouterManager = mRouterManager;
|
||||
mInfoMediaManager.mMediaRouterCallback.onRouteListingPreferenceUpdated(TEST_PACKAGE_NAME,
|
||||
routeListingPreference);
|
||||
mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
|
||||
|
||||
assertThat(mInfoMediaManager.mMediaDevices).hasSize(4);
|
||||
assertThat(mInfoMediaManager.mMediaDevices.get(0).getId()).isEqualTo(TEST_ID);
|
||||
assertThat(mInfoMediaManager.mMediaDevices.get(1).getId()).isEqualTo(TEST_ID_1);
|
||||
assertThat(mInfoMediaManager.mMediaDevices.get(2).getId()).isEqualTo(TEST_ID_4);
|
||||
assertThat(mInfoMediaManager.mMediaDevices.get(2).isSuggestedDevice()).isTrue();
|
||||
assertThat(mInfoMediaManager.mMediaDevices.get(3).getId()).isEqualTo(TEST_ID_3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onRouteChanged_preferenceListUpdateWithDifferentPkg_notOrdersRoutes() {
|
||||
RouteListingPreference routeListingPreference = setUpPreferenceList(TEST_PACKAGE_NAME_2);
|
||||
setUpSelectedRoutes(TEST_PACKAGE_NAME);
|
||||
|
||||
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
|
||||
final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
|
||||
routingSessionInfos.add(sessionInfo);
|
||||
|
||||
when(mRouterManager.getRoutingSessions(TEST_PACKAGE_NAME)).thenReturn(routingSessionInfos);
|
||||
when(sessionInfo.getSelectedRoutes()).thenReturn(ImmutableList.of(TEST_ID));
|
||||
|
||||
setAvailableRoutesList(TEST_PACKAGE_NAME);
|
||||
mInfoMediaManager.mRouterManager = mRouterManager;
|
||||
mInfoMediaManager.mMediaRouterCallback.onRouteListingPreferenceUpdated(TEST_PACKAGE_NAME_2,
|
||||
routeListingPreference);
|
||||
mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
|
||||
|
||||
assertThat(mInfoMediaManager.mMediaDevices).hasSize(1);
|
||||
assertThat(mInfoMediaManager.mMediaDevices.get(0).getId()).isEqualTo(TEST_ID);
|
||||
}
|
||||
|
||||
private RouteListingPreference setUpPreferenceList(String packageName) {
|
||||
ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT",
|
||||
Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
|
||||
final List<RouteListingPreference.Item> preferenceItemList = new ArrayList<>();
|
||||
RouteListingPreference.Item item1 =
|
||||
new RouteListingPreference.Item.Builder(TEST_ID_4)
|
||||
.setFlags(RouteListingPreference.Item.FLAG_SUGGESTED)
|
||||
.build();
|
||||
RouteListingPreference.Item item2 = new RouteListingPreference.Item.Builder(
|
||||
TEST_ID_3).build();
|
||||
preferenceItemList.add(item1);
|
||||
preferenceItemList.add(item2);
|
||||
|
||||
RouteListingPreference routeListingPreference =
|
||||
new RouteListingPreference.Builder().setItems(
|
||||
preferenceItemList).setUseSystemOrdering(false).build();
|
||||
when(mRouterManager.getRouteListingPreference(packageName))
|
||||
.thenReturn(routeListingPreference);
|
||||
return routeListingPreference;
|
||||
}
|
||||
|
||||
private void setUpSelectedRoutes(String packageName) {
|
||||
final List<MediaRoute2Info> selectedRoutes = new ArrayList<>();
|
||||
final MediaRoute2Info info = mock(MediaRoute2Info.class);
|
||||
when(info.getId()).thenReturn(TEST_ID);
|
||||
when(info.getClientPackageName()).thenReturn(packageName);
|
||||
when(info.isSystemRoute()).thenReturn(true);
|
||||
selectedRoutes.add(info);
|
||||
when(mRouterManager.getSelectedRoutes(any())).thenReturn(selectedRoutes);
|
||||
}
|
||||
|
||||
private List<MediaRoute2Info> setAvailableRoutesList(String packageName) {
|
||||
final List<MediaRoute2Info> availableRoutes = new ArrayList<>();
|
||||
final MediaRoute2Info availableInfo1 = mock(MediaRoute2Info.class);
|
||||
when(availableInfo1.getId()).thenReturn(TEST_ID_2);
|
||||
when(availableInfo1.getClientPackageName()).thenReturn(packageName);
|
||||
when(availableInfo1.getType()).thenReturn(TYPE_REMOTE_TV);
|
||||
availableRoutes.add(availableInfo1);
|
||||
|
||||
final MediaRoute2Info availableInfo2 = mock(MediaRoute2Info.class);
|
||||
when(availableInfo2.getId()).thenReturn(TEST_ID_3);
|
||||
when(availableInfo2.getClientPackageName()).thenReturn(packageName);
|
||||
availableRoutes.add(availableInfo2);
|
||||
|
||||
final MediaRoute2Info availableInfo3 = mock(MediaRoute2Info.class);
|
||||
when(availableInfo3.getId()).thenReturn(TEST_ID_4);
|
||||
when(availableInfo3.getClientPackageName()).thenReturn(packageName);
|
||||
availableRoutes.add(availableInfo3);
|
||||
|
||||
final MediaRoute2Info availableInfo4 = mock(MediaRoute2Info.class);
|
||||
when(availableInfo4.getId()).thenReturn(TEST_ID_1);
|
||||
when(availableInfo4.isSystemRoute()).thenReturn(true);
|
||||
when(availableInfo4.getClientPackageName()).thenReturn(packageName);
|
||||
availableRoutes.add(availableInfo4);
|
||||
|
||||
when(mRouterManager.getAvailableRoutes(packageName)).thenReturn(availableRoutes);
|
||||
|
||||
return availableRoutes;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasPreferenceRouteListing_oldSdkVersion_returnsFalse() {
|
||||
assertThat(mInfoMediaManager.preferRouteListingOrdering()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasPreferenceRouteListing_newSdkVersionWithPreferenceExist_returnsTrue() {
|
||||
ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT",
|
||||
Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
|
||||
when(mRouterManager.getRouteListingPreference(any())).thenReturn(
|
||||
new RouteListingPreference.Builder().setItems(
|
||||
ImmutableList.of()).setUseSystemOrdering(false).build());
|
||||
mInfoMediaManager.mRouterManager = mRouterManager;
|
||||
|
||||
assertThat(mInfoMediaManager.preferRouteListingOrdering()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasPreferenceRouteListing_newSdkVersionWithPreferenceNotExist_returnsFalse() {
|
||||
ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT",
|
||||
Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
|
||||
|
||||
when(mRouterManager.getRouteListingPreference(any())).thenReturn(null);
|
||||
|
||||
assertThat(mInfoMediaManager.preferRouteListingOrdering()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getInAppOnlyItemRoutingReceiver_oldSdkVersion_returnsNull() {
|
||||
assertThat(mInfoMediaManager.getLinkedItemComponentName()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getInAppOnlyItemRoutingReceiver_newSdkVersionWithReceiverExist_returns() {
|
||||
ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT",
|
||||
Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
|
||||
when(mRouterManager.getRouteListingPreference(any())).thenReturn(
|
||||
new RouteListingPreference.Builder().setItems(
|
||||
ImmutableList.of()).setUseSystemOrdering(
|
||||
false).setLinkedItemComponentName(mComponentName).build());
|
||||
mInfoMediaManager.mRouterManager = mRouterManager;
|
||||
|
||||
assertThat(mInfoMediaManager.getLinkedItemComponentName()).isEqualTo(mComponentName);
|
||||
}
|
||||
|
||||
private List<MediaRoute2Info> getRoutesListWithDuplicatedIds() {
|
||||
final List<MediaRoute2Info> routes = new ArrayList<>();
|
||||
final MediaRoute2Info info = mock(MediaRoute2Info.class);
|
||||
when(info.getId()).thenReturn(TEST_ID);
|
||||
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info.isSystemRoute()).thenReturn(true);
|
||||
when(info.getDeduplicationIds()).thenReturn(
|
||||
Set.of(TEST_DUPLICATED_ID_1, TEST_DUPLICATED_ID_2));
|
||||
routes.add(info);
|
||||
|
||||
final MediaRoute2Info info1 = mock(MediaRoute2Info.class);
|
||||
when(info1.getId()).thenReturn(TEST_ID_1);
|
||||
when(info1.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info1.isSystemRoute()).thenReturn(true);
|
||||
when(info1.getDeduplicationIds()).thenReturn(Set.of(TEST_DUPLICATED_ID_3));
|
||||
routes.add(info1);
|
||||
|
||||
final MediaRoute2Info info2 = mock(MediaRoute2Info.class);
|
||||
when(info2.getId()).thenReturn(TEST_ID_2);
|
||||
when(info2.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info2.isSystemRoute()).thenReturn(true);
|
||||
when(info2.getDeduplicationIds()).thenReturn(Set.of(TEST_DUPLICATED_ID_3));
|
||||
routes.add(info2);
|
||||
|
||||
final MediaRoute2Info info3 = mock(MediaRoute2Info.class);
|
||||
when(info3.getId()).thenReturn(TEST_ID_3);
|
||||
when(info3.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info3.isSystemRoute()).thenReturn(true);
|
||||
when(info3.getDeduplicationIds()).thenReturn(Set.of(TEST_DUPLICATED_ID_1));
|
||||
routes.add(info3);
|
||||
|
||||
final MediaRoute2Info info4 = mock(MediaRoute2Info.class);
|
||||
when(info4.getId()).thenReturn(TEST_ID_4);
|
||||
when(info4.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info4.isSystemRoute()).thenReturn(true);
|
||||
when(info4.getDeduplicationIds()).thenReturn(Set.of(TEST_DUPLICATED_ID_2));
|
||||
routes.add(info4);
|
||||
|
||||
return routes;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onRoutesRemoved_getAvailableRoutes_shouldAddMediaDevice() {
|
||||
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
|
||||
final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
|
||||
routingSessionInfos.add(sessionInfo);
|
||||
final List<String> selectedRoutes = new ArrayList<>();
|
||||
selectedRoutes.add(TEST_ID);
|
||||
when(sessionInfo.getSelectedRoutes()).thenReturn(selectedRoutes);
|
||||
mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
|
||||
|
||||
final MediaRoute2Info info = mock(MediaRoute2Info.class);
|
||||
when(info.getId()).thenReturn(TEST_ID);
|
||||
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info.getDeduplicationIds()).thenReturn(Set.of());
|
||||
|
||||
final List<MediaRoute2Info> routes = new ArrayList<>();
|
||||
routes.add(info);
|
||||
mShadowRouter2Manager.setTransferableRoutes(routes);
|
||||
|
||||
final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
|
||||
assertThat(mediaDevice).isNull();
|
||||
|
||||
mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
|
||||
|
||||
final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
|
||||
assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
|
||||
assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(infoDevice);
|
||||
assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addDeviceToPlayMedia_containSelectableRoutes_returnTrue() {
|
||||
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
|
||||
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
|
||||
routingSessionInfos.add(info);
|
||||
|
||||
final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
|
||||
final MediaDevice device = new InfoMediaDevice(mContext, route2Info);
|
||||
|
||||
final List<String> list = new ArrayList<>();
|
||||
list.add(TEST_ID);
|
||||
|
||||
mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
|
||||
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info.getSelectableRoutes()).thenReturn(list);
|
||||
when(route2Info.getId()).thenReturn(TEST_ID);
|
||||
when(route2Info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
|
||||
assertThat(mInfoMediaManager.addDeviceToPlayMedia(device)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addDeviceToPlayMedia_notContainSelectableRoutes_returnFalse() {
|
||||
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
|
||||
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
|
||||
routingSessionInfos.add(info);
|
||||
|
||||
final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
|
||||
final MediaDevice device = new InfoMediaDevice(mContext, route2Info);
|
||||
|
||||
final List<String> list = new ArrayList<>();
|
||||
list.add("fake_id");
|
||||
|
||||
mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
|
||||
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info.getSelectableRoutes()).thenReturn(list);
|
||||
when(route2Info.getId()).thenReturn(TEST_ID);
|
||||
when(route2Info.getName()).thenReturn(TEST_NAME);
|
||||
when(route2Info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
|
||||
assertThat(mInfoMediaManager.addDeviceToPlayMedia(device)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeDeviceFromMedia_containSelectedRoutes_returnTrue() {
|
||||
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
|
||||
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
|
||||
routingSessionInfos.add(info);
|
||||
|
||||
final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
|
||||
final MediaDevice device = new InfoMediaDevice(mContext, route2Info);
|
||||
|
||||
final List<String> list = new ArrayList<>();
|
||||
list.add(TEST_ID);
|
||||
|
||||
mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
|
||||
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info.getSelectedRoutes()).thenReturn(list);
|
||||
when(route2Info.getId()).thenReturn(TEST_ID);
|
||||
when(route2Info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
|
||||
assertThat(mInfoMediaManager.removeDeviceFromPlayMedia(device)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeDeviceFromMedia_notContainSelectedRoutes_returnFalse() {
|
||||
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
|
||||
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
|
||||
routingSessionInfos.add(info);
|
||||
|
||||
final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
|
||||
final MediaDevice device = new InfoMediaDevice(mContext, route2Info);
|
||||
|
||||
final List<String> list = new ArrayList<>();
|
||||
list.add("fake_id");
|
||||
|
||||
mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
|
||||
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info.getSelectedRoutes()).thenReturn(list);
|
||||
when(route2Info.getId()).thenReturn(TEST_ID);
|
||||
when(route2Info.getName()).thenReturn(TEST_NAME);
|
||||
when(route2Info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
|
||||
assertThat(mInfoMediaManager.removeDeviceFromPlayMedia(device)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSelectableMediaDevice_notContainPackageName_returnEmpty() {
|
||||
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
|
||||
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
|
||||
routingSessionInfos.add(info);
|
||||
|
||||
mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
|
||||
when(info.getClientPackageName()).thenReturn("com.fake.packagename");
|
||||
|
||||
assertThat(mInfoMediaManager.getSelectableMediaDevices()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDeselectableMediaDevice_checkList() {
|
||||
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
|
||||
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
|
||||
routingSessionInfos.add(info);
|
||||
final List<MediaRoute2Info> mediaRoute2Infos = new ArrayList<>();
|
||||
final MediaRoute2Info mediaRoute2Info = mock(MediaRoute2Info.class);
|
||||
mediaRoute2Infos.add(mediaRoute2Info);
|
||||
mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
|
||||
mShadowRouter2Manager.setDeselectableRoutes(mediaRoute2Infos);
|
||||
when(mediaRoute2Info.getName()).thenReturn(TEST_NAME);
|
||||
when(mediaRoute2Info.getId()).thenReturn(TEST_ID);
|
||||
|
||||
final List<MediaDevice> mediaDevices = mInfoMediaManager.getDeselectableMediaDevices();
|
||||
|
||||
assertThat(mediaDevices.size()).isEqualTo(1);
|
||||
assertThat(mediaDevices.get(0).getName()).isEqualTo(TEST_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adjustSessionVolume_routingSessionInfoIsNull_noCrash() {
|
||||
mInfoMediaManager.adjustSessionVolume(null, 10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSessionVolumeMax_containPackageName_returnMaxVolume() {
|
||||
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
|
||||
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
|
||||
routingSessionInfos.add(info);
|
||||
|
||||
mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
|
||||
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
|
||||
mInfoMediaManager.getSessionVolumeMax();
|
||||
|
||||
verify(info).getVolumeMax();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSessionVolume_containPackageName_returnMaxVolume() {
|
||||
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
|
||||
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
|
||||
routingSessionInfos.add(info);
|
||||
|
||||
mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
|
||||
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
|
||||
mInfoMediaManager.getSessionVolume();
|
||||
|
||||
verify(info).getVolume();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRemoteSessions_returnsRemoteSessions() {
|
||||
final List<RoutingSessionInfo> infos = new ArrayList<>();
|
||||
infos.add(mock(RoutingSessionInfo.class));
|
||||
mShadowRouter2Manager.setRemoteSessions(infos);
|
||||
|
||||
assertThat(mInfoMediaManager.getRemoteSessions()).containsExactlyElementsIn(infos);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void releaseSession_removeSuccessfully_returnTrue() {
|
||||
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
|
||||
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
|
||||
routingSessionInfos.add(info);
|
||||
|
||||
mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
|
||||
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
|
||||
assertThat(mInfoMediaManager.releaseSession()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSessionName_containPackageName_returnName() {
|
||||
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
|
||||
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
|
||||
routingSessionInfos.add(info);
|
||||
|
||||
mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
|
||||
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info.getName()).thenReturn(TEST_NAME);
|
||||
|
||||
assertThat(mInfoMediaManager.getSessionName()).isEqualTo(TEST_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onTransferFailed_notDispatchOnRequestFailed() {
|
||||
mInfoMediaManager.registerCallback(mCallback);
|
||||
|
||||
mInfoMediaManager.mMediaRouterCallback.onTransferFailed(null, null);
|
||||
|
||||
verify(mCallback, never()).onRequestFailed(REASON_UNKNOWN_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onRequestFailed_shouldDispatchOnRequestFailed() {
|
||||
mInfoMediaManager.registerCallback(mCallback);
|
||||
|
||||
mInfoMediaManager.mMediaRouterCallback.onRequestFailed(REASON_NETWORK_ERROR);
|
||||
|
||||
verify(mCallback).onRequestFailed(REASON_NETWORK_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onTransferred_getAvailableRoutes_shouldAddMediaDevice() {
|
||||
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
|
||||
final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
|
||||
routingSessionInfos.add(sessionInfo);
|
||||
final List<String> selectedRoutes = new ArrayList<>();
|
||||
selectedRoutes.add(TEST_ID);
|
||||
when(sessionInfo.getSelectedRoutes()).thenReturn(selectedRoutes);
|
||||
mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
|
||||
|
||||
final MediaRoute2Info info = mock(MediaRoute2Info.class);
|
||||
mInfoMediaManager.registerCallback(mCallback);
|
||||
|
||||
when(info.getDeduplicationIds()).thenReturn(Set.of());
|
||||
when(info.getId()).thenReturn(TEST_ID);
|
||||
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
|
||||
final List<MediaRoute2Info> routes = new ArrayList<>();
|
||||
routes.add(info);
|
||||
mShadowRouter2Manager.setTransferableRoutes(routes);
|
||||
|
||||
final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
|
||||
assertThat(mediaDevice).isNull();
|
||||
|
||||
mInfoMediaManager.mMediaRouterCallback.onTransferred(sessionInfo, sessionInfo);
|
||||
|
||||
final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
|
||||
assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
|
||||
assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(infoDevice);
|
||||
assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
|
||||
verify(mCallback).onConnectedDeviceChanged(TEST_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSessionUpdated_shouldDispatchDeviceListAdded() {
|
||||
final MediaRoute2Info info = mock(MediaRoute2Info.class);
|
||||
when(info.getId()).thenReturn(TEST_ID);
|
||||
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
when(info.isSystemRoute()).thenReturn(true);
|
||||
|
||||
final List<MediaRoute2Info> routes = new ArrayList<>();
|
||||
routes.add(info);
|
||||
mShadowRouter2Manager.setAllRoutes(routes);
|
||||
|
||||
mInfoMediaManager.registerCallback(mCallback);
|
||||
|
||||
mInfoMediaManager.mMediaRouterCallback.onSessionUpdated(mock(RoutingSessionInfo.class));
|
||||
|
||||
verify(mCallback).onDeviceListAdded(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addMediaDevice_verifyDeviceTypeCanCorrespondToMediaDevice() {
|
||||
final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
|
||||
final CachedBluetoothDeviceManager cachedBluetoothDeviceManager =
|
||||
mock(CachedBluetoothDeviceManager.class);
|
||||
final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
|
||||
|
||||
when(route2Info.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
|
||||
when(route2Info.getId()).thenReturn(TEST_ID);
|
||||
mInfoMediaManager.addMediaDevice(route2Info);
|
||||
assertThat(mInfoMediaManager.mMediaDevices.get(0) instanceof InfoMediaDevice).isTrue();
|
||||
|
||||
when(route2Info.getType()).thenReturn(TYPE_USB_DEVICE);
|
||||
when(route2Info.getId()).thenReturn(TEST_ID);
|
||||
mInfoMediaManager.mMediaDevices.clear();
|
||||
mInfoMediaManager.addMediaDevice(route2Info);
|
||||
assertThat(mInfoMediaManager.mMediaDevices.get(0) instanceof PhoneMediaDevice).isTrue();
|
||||
|
||||
when(route2Info.getType()).thenReturn(TYPE_WIRED_HEADSET);
|
||||
when(route2Info.getId()).thenReturn(TEST_ID);
|
||||
mInfoMediaManager.mMediaDevices.clear();
|
||||
mInfoMediaManager.addMediaDevice(route2Info);
|
||||
assertThat(mInfoMediaManager.mMediaDevices.get(0) instanceof PhoneMediaDevice).isTrue();
|
||||
|
||||
when(route2Info.getType()).thenReturn(TYPE_BLUETOOTH_A2DP);
|
||||
when(route2Info.getAddress()).thenReturn("00:00:00:00:00:00");
|
||||
when(route2Info.getId()).thenReturn(TEST_ID);
|
||||
when(mLocalBluetoothManager.getCachedDeviceManager())
|
||||
.thenReturn(cachedBluetoothDeviceManager);
|
||||
when(cachedBluetoothDeviceManager.findDevice(any(BluetoothDevice.class)))
|
||||
.thenReturn(cachedDevice);
|
||||
mInfoMediaManager.mMediaDevices.clear();
|
||||
mInfoMediaManager.addMediaDevice(route2Info);
|
||||
assertThat(mInfoMediaManager.mMediaDevices.get(0) instanceof BluetoothMediaDevice).isTrue();
|
||||
|
||||
when(route2Info.getType()).thenReturn(TYPE_BUILTIN_SPEAKER);
|
||||
mInfoMediaManager.mMediaDevices.clear();
|
||||
mInfoMediaManager.addMediaDevice(route2Info);
|
||||
assertThat(mInfoMediaManager.mMediaDevices.get(0) instanceof PhoneMediaDevice).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addMediaDevice_cachedBluetoothDeviceIsNull_shouldNotAdded() {
|
||||
final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
|
||||
final CachedBluetoothDeviceManager cachedBluetoothDeviceManager =
|
||||
mock(CachedBluetoothDeviceManager.class);
|
||||
|
||||
when(route2Info.getType()).thenReturn(TYPE_BLUETOOTH_A2DP);
|
||||
when(route2Info.getAddress()).thenReturn("00:00:00:00:00:00");
|
||||
when(mLocalBluetoothManager.getCachedDeviceManager())
|
||||
.thenReturn(cachedBluetoothDeviceManager);
|
||||
when(cachedBluetoothDeviceManager.findDevice(any(BluetoothDevice.class)))
|
||||
.thenReturn(null);
|
||||
|
||||
mInfoMediaManager.mMediaDevices.clear();
|
||||
mInfoMediaManager.addMediaDevice(route2Info);
|
||||
|
||||
assertThat(mInfoMediaManager.mMediaDevices.size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addMediaDevice_deviceIncludedInSelectedDevices_shouldSetAsCurrentConnected() {
|
||||
final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
|
||||
final CachedBluetoothDeviceManager cachedBluetoothDeviceManager =
|
||||
mock(CachedBluetoothDeviceManager.class);
|
||||
final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
|
||||
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
|
||||
final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
|
||||
routingSessionInfos.add(sessionInfo);
|
||||
|
||||
when(mRouterManager.getRoutingSessions(TEST_PACKAGE_NAME)).thenReturn(routingSessionInfos);
|
||||
when(sessionInfo.getSelectedRoutes()).thenReturn(ImmutableList.of(TEST_ID));
|
||||
when(route2Info.getType()).thenReturn(TYPE_BLUETOOTH_A2DP);
|
||||
when(route2Info.getAddress()).thenReturn("00:00:00:00:00:00");
|
||||
when(route2Info.getId()).thenReturn(TEST_ID);
|
||||
when(mLocalBluetoothManager.getCachedDeviceManager())
|
||||
.thenReturn(cachedBluetoothDeviceManager);
|
||||
when(cachedBluetoothDeviceManager.findDevice(any(BluetoothDevice.class)))
|
||||
.thenReturn(cachedDevice);
|
||||
mInfoMediaManager.mRouterManager = mRouterManager;
|
||||
|
||||
mInfoMediaManager.mMediaDevices.clear();
|
||||
mInfoMediaManager.addMediaDevice(route2Info);
|
||||
|
||||
MediaDevice device = mInfoMediaManager.mMediaDevices.get(0);
|
||||
|
||||
assertThat(device instanceof BluetoothMediaDevice).isTrue();
|
||||
assertThat(device.getState()).isEqualTo(STATE_SELECTED);
|
||||
assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(device);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,608 @@
|
||||
/*
|
||||
* Copyright 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.media;
|
||||
|
||||
import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
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.BluetoothProfile;
|
||||
import android.content.Context;
|
||||
import android.media.AudioDeviceAttributes;
|
||||
import android.media.AudioManager;
|
||||
import android.media.AudioSystem;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.media.MediaRouter2Manager;
|
||||
import android.media.RoutingSessionInfo;
|
||||
|
||||
import com.android.settingslib.bluetooth.A2dpProfile;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||
import com.android.settingslib.bluetooth.HearingAidProfile;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothProfile;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
||||
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 LocalMediaManagerTest {
|
||||
|
||||
private static final String TEST_DEVICE_NAME_1 = "device_name_1";
|
||||
private static final String TEST_DEVICE_NAME_2 = "device_name_2";
|
||||
private static final String TEST_DEVICE_ID_1 = "device_id_1";
|
||||
private static final String TEST_DEVICE_ID_2 = "device_id_2";
|
||||
private static final String TEST_DEVICE_ID_3 = "device_id_3";
|
||||
private static final String TEST_CURRENT_DEVICE_ID = "currentDevice_id";
|
||||
private static final String TEST_PACKAGE_NAME = "com.test.playmusic";
|
||||
private static final String TEST_SESSION_ID = "session_id";
|
||||
private static final String TEST_ADDRESS = "00:01:02:03:04:05";
|
||||
|
||||
@Mock
|
||||
private InfoMediaManager mInfoMediaManager;
|
||||
@Mock
|
||||
private LocalBluetoothManager mLocalBluetoothManager;
|
||||
@Mock
|
||||
private LocalMediaManager.DeviceCallback mCallback;
|
||||
@Mock
|
||||
private HearingAidProfile mHapProfile;
|
||||
@Mock
|
||||
private A2dpProfile mA2dpProfile;
|
||||
@Mock
|
||||
private LocalBluetoothProfileManager mLocalProfileManager;
|
||||
@Mock
|
||||
private MediaRouter2Manager mMediaRouter2Manager;
|
||||
@Mock
|
||||
private MediaRoute2Info mRouteInfo1;
|
||||
@Mock
|
||||
private MediaRoute2Info mRouteInfo2;
|
||||
@Mock
|
||||
private AudioManager mAudioManager;
|
||||
|
||||
private Context mContext;
|
||||
private LocalMediaManager mLocalMediaManager;
|
||||
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
|
||||
private InfoMediaDevice mInfoMediaDevice1;
|
||||
private InfoMediaDevice mInfoMediaDevice2;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
final List<BluetoothDevice> bluetoothDevices = new ArrayList<>();
|
||||
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
|
||||
mShadowBluetoothAdapter.setMostRecentlyConnectedDevices(bluetoothDevices);
|
||||
when(mRouteInfo1.getName()).thenReturn(TEST_DEVICE_NAME_1);
|
||||
when(mRouteInfo1.getId()).thenReturn(TEST_DEVICE_ID_1);
|
||||
when(mRouteInfo2.getName()).thenReturn(TEST_DEVICE_NAME_2);
|
||||
when(mRouteInfo2.getId()).thenReturn(TEST_DEVICE_ID_2);
|
||||
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalProfileManager);
|
||||
when(mLocalProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
|
||||
when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHapProfile);
|
||||
|
||||
mInfoMediaDevice1 = spy(new InfoMediaDevice(mContext, mRouteInfo1));
|
||||
mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2);
|
||||
mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager,
|
||||
mInfoMediaManager, "com.test.packagename");
|
||||
mLocalMediaManager.mAudioManager = mAudioManager;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startScan_mediaDevicesListShouldBeClear() {
|
||||
final MediaDevice device = mock(MediaDevice.class);
|
||||
mLocalMediaManager.mMediaDevices.add(device);
|
||||
|
||||
assertThat(mLocalMediaManager.mMediaDevices).hasSize(1);
|
||||
mLocalMediaManager.startScan();
|
||||
assertThat(mLocalMediaManager.mMediaDevices).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void connectDevice_deviceNotEqualCurrentConnectedDevice_connectDevice() {
|
||||
final MediaDevice currentDevice = mock(MediaDevice.class);
|
||||
final MediaDevice device = mock(MediaDevice.class);
|
||||
mLocalMediaManager.mMediaDevices.add(currentDevice);
|
||||
mLocalMediaManager.mMediaDevices.add(device);
|
||||
mLocalMediaManager.mCurrentConnectedDevice = currentDevice;
|
||||
|
||||
when(device.getId()).thenReturn(TEST_DEVICE_ID_1);
|
||||
when(currentDevice.getId()).thenReturn(TEST_CURRENT_DEVICE_ID);
|
||||
|
||||
mLocalMediaManager.registerCallback(mCallback);
|
||||
assertThat(mLocalMediaManager.connectDevice(device)).isTrue();
|
||||
|
||||
verify(mInfoMediaManager).connectToDevice(device);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void connectDevice_deviceNotEqualCurrentConnectedDevice_isConnectingState() {
|
||||
mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice1);
|
||||
mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice2);
|
||||
mLocalMediaManager.mCurrentConnectedDevice = mInfoMediaDevice1;
|
||||
|
||||
mLocalMediaManager.registerCallback(mCallback);
|
||||
assertThat(mLocalMediaManager.connectDevice(mInfoMediaDevice2)).isTrue();
|
||||
|
||||
assertThat(mInfoMediaDevice2.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
|
||||
.STATE_CONNECTING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void connectDevice_deviceEqualCurrentConnectedDevice_notConnectingState() {
|
||||
mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice1);
|
||||
mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice2);
|
||||
mLocalMediaManager.mCurrentConnectedDevice = mInfoMediaDevice1;
|
||||
|
||||
mLocalMediaManager.registerCallback(mCallback);
|
||||
assertThat(mLocalMediaManager.connectDevice(mInfoMediaDevice1)).isFalse();
|
||||
|
||||
assertThat(mInfoMediaDevice1.getState()).isNotEqualTo(LocalMediaManager.MediaDeviceState
|
||||
.STATE_CONNECTING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void connectDevice_bluetoothDeviceNotConnected_connectBluetoothDevice() {
|
||||
final MediaDevice device = mock(BluetoothMediaDevice.class);
|
||||
final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
|
||||
mLocalMediaManager.mMediaDevices.add(device);
|
||||
|
||||
when(device.getId()).thenReturn(TEST_DEVICE_ID_1);
|
||||
when(((BluetoothMediaDevice) device).getCachedDevice()).thenReturn(cachedDevice);
|
||||
when(cachedDevice.isConnected()).thenReturn(false);
|
||||
when(cachedDevice.isBusy()).thenReturn(false);
|
||||
|
||||
mLocalMediaManager.registerCallback(mCallback);
|
||||
assertThat(mLocalMediaManager.connectDevice(device)).isTrue();
|
||||
|
||||
verify(cachedDevice).connect();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMediaDeviceById_idExist_shouldReturnMediaDevice() {
|
||||
final MediaDevice device1 = mock(MediaDevice.class);
|
||||
final MediaDevice device2 = mock(MediaDevice.class);
|
||||
mLocalMediaManager.mMediaDevices.add(device1);
|
||||
mLocalMediaManager.mMediaDevices.add(device2);
|
||||
|
||||
when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
|
||||
when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
|
||||
|
||||
MediaDevice device = mLocalMediaManager.getMediaDeviceById(TEST_DEVICE_ID_2);
|
||||
|
||||
assertThat(device.getId()).isEqualTo(TEST_DEVICE_ID_2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMediaDeviceById_idNotExist_shouldReturnNull() {
|
||||
final MediaDevice device1 = mock(MediaDevice.class);
|
||||
final MediaDevice device2 = mock(MediaDevice.class);
|
||||
mLocalMediaManager.mMediaDevices.add(device1);
|
||||
mLocalMediaManager.mMediaDevices.add(device2);
|
||||
|
||||
when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
|
||||
when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
|
||||
|
||||
MediaDevice device = mLocalMediaManager.getMediaDeviceById(TEST_CURRENT_DEVICE_ID);
|
||||
|
||||
assertThat(device).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMediaDeviceById_idIsNull_shouldReturnNull() {
|
||||
final MediaDevice device1 = mock(MediaDevice.class);
|
||||
final MediaDevice device2 = mock(MediaDevice.class);
|
||||
mLocalMediaManager.mMediaDevices.add(device1);
|
||||
mLocalMediaManager.mMediaDevices.add(device2);
|
||||
|
||||
when(device1.getId()).thenReturn(null);
|
||||
when(device2.getId()).thenReturn(null);
|
||||
|
||||
MediaDevice device = mLocalMediaManager.getMediaDeviceById(TEST_CURRENT_DEVICE_ID);
|
||||
|
||||
assertThat(device).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeviceListAdded_addDevicesList() {
|
||||
final List<MediaDevice> devices = new ArrayList<>();
|
||||
final MediaDevice device1 = mock(MediaDevice.class);
|
||||
final MediaDevice device2 = mock(MediaDevice.class);
|
||||
devices.add(device1);
|
||||
devices.add(device2);
|
||||
|
||||
when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
|
||||
when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
|
||||
|
||||
assertThat(mLocalMediaManager.mMediaDevices).isEmpty();
|
||||
mLocalMediaManager.registerCallback(mCallback);
|
||||
mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
|
||||
|
||||
assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
|
||||
verify(mCallback).onDeviceListUpdate(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeviceListAdded_addDeviceList() {
|
||||
final List<MediaDevice> devices = new ArrayList<>();
|
||||
final MediaDevice device1 = mock(MediaDevice.class);
|
||||
final MediaDevice device2 = mock(MediaDevice.class);
|
||||
final MediaDevice device3 = mock(MediaDevice.class);
|
||||
devices.add(device1);
|
||||
devices.add(device2);
|
||||
mLocalMediaManager.mMediaDevices.add(device3);
|
||||
|
||||
when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
|
||||
when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
|
||||
when(device3.getId()).thenReturn(TEST_DEVICE_ID_3);
|
||||
|
||||
assertThat(mLocalMediaManager.mMediaDevices).hasSize(1);
|
||||
mLocalMediaManager.registerCallback(mCallback);
|
||||
mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
|
||||
|
||||
assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
|
||||
verify(mCallback).onDeviceListUpdate(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeviceListRemoved_removeAll() {
|
||||
final List<MediaDevice> devices = new ArrayList<>();
|
||||
final MediaDevice device1 = mock(MediaDevice.class);
|
||||
final MediaDevice device2 = mock(MediaDevice.class);
|
||||
devices.add(device1);
|
||||
devices.add(device2);
|
||||
mLocalMediaManager.mMediaDevices.add(device1);
|
||||
mLocalMediaManager.mMediaDevices.add(device2);
|
||||
|
||||
assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
|
||||
mLocalMediaManager.registerCallback(mCallback);
|
||||
mLocalMediaManager.mMediaDeviceCallback
|
||||
.onDeviceListRemoved(mLocalMediaManager.mMediaDevices);
|
||||
|
||||
assertThat(mLocalMediaManager.mMediaDevices).isEmpty();
|
||||
verify(mCallback).onDeviceListUpdate(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeviceListRemoved_phoneDeviceNotLastDeviceAfterRemoveDeviceList_removeList() {
|
||||
final List<MediaDevice> devices = new ArrayList<>();
|
||||
final MediaDevice device1 = mock(MediaDevice.class);
|
||||
final MediaDevice device2 = mock(MediaDevice.class);
|
||||
final MediaDevice device3 = mock(MediaDevice.class);
|
||||
devices.add(device1);
|
||||
devices.add(device3);
|
||||
mLocalMediaManager.mMediaDevices.add(device1);
|
||||
mLocalMediaManager.mMediaDevices.add(device2);
|
||||
mLocalMediaManager.mMediaDevices.add(device3);
|
||||
|
||||
assertThat(mLocalMediaManager.mMediaDevices).hasSize(3);
|
||||
mLocalMediaManager.registerCallback(mCallback);
|
||||
mLocalMediaManager.mMediaDeviceCallback.onDeviceListRemoved(devices);
|
||||
|
||||
assertThat(mLocalMediaManager.mMediaDevices).hasSize(1);
|
||||
verify(mCallback).onDeviceListUpdate(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onConnectedDeviceChanged_connectedAndCurrentDeviceAreDifferent_notifyThemChanged() {
|
||||
final MediaDevice device1 = mock(MediaDevice.class);
|
||||
final MediaDevice device2 = mock(MediaDevice.class);
|
||||
|
||||
mLocalMediaManager.mMediaDevices.add(device1);
|
||||
mLocalMediaManager.mMediaDevices.add(device2);
|
||||
mLocalMediaManager.mCurrentConnectedDevice = device1;
|
||||
|
||||
when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
|
||||
when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
|
||||
|
||||
mLocalMediaManager.registerCallback(mCallback);
|
||||
mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_2);
|
||||
|
||||
assertThat(mLocalMediaManager.getCurrentConnectedDevice()).isEqualTo(device2);
|
||||
verify(mCallback).onSelectedDeviceStateChanged(device2,
|
||||
LocalMediaManager.MediaDeviceState.STATE_CONNECTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onConnectedDeviceChanged_connectedAndCurrentDeviceAreSame_doNothing() {
|
||||
final MediaDevice device1 = mock(MediaDevice.class);
|
||||
final MediaDevice device2 = mock(MediaDevice.class);
|
||||
|
||||
mLocalMediaManager.mMediaDevices.add(device1);
|
||||
mLocalMediaManager.mMediaDevices.add(device2);
|
||||
mLocalMediaManager.mCurrentConnectedDevice = device1;
|
||||
|
||||
when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
|
||||
when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
|
||||
|
||||
mLocalMediaManager.registerCallback(mCallback);
|
||||
mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_1);
|
||||
|
||||
verify(mCallback, never()).onDeviceAttributesChanged();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onConnectedDeviceChanged_isConnectedState() {
|
||||
mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice1);
|
||||
mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice2);
|
||||
mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
|
||||
|
||||
assertThat(mInfoMediaDevice1.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
|
||||
.STATE_DISCONNECTED);
|
||||
mLocalMediaManager.registerCallback(mCallback);
|
||||
mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_1);
|
||||
|
||||
assertThat(mInfoMediaDevice1.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
|
||||
.STATE_CONNECTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onConnectedDeviceChanged_nullConnectedDevice_noException() {
|
||||
mLocalMediaManager.registerCallback(mCallback);
|
||||
mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeviceAttributesChanged_failingTransferring_shouldResetState() {
|
||||
final MediaDevice currentDevice = mock(MediaDevice.class);
|
||||
final MediaDevice device = mock(BluetoothMediaDevice.class);
|
||||
final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
|
||||
mLocalMediaManager.mMediaDevices.add(device);
|
||||
mLocalMediaManager.mMediaDevices.add(currentDevice);
|
||||
when(device.getId()).thenReturn(TEST_DEVICE_ID_1);
|
||||
when(currentDevice.getId()).thenReturn(TEST_CURRENT_DEVICE_ID);
|
||||
when(((BluetoothMediaDevice) device).getCachedDevice()).thenReturn(cachedDevice);
|
||||
when(cachedDevice.isConnected()).thenReturn(false);
|
||||
when(cachedDevice.isBusy()).thenReturn(false);
|
||||
|
||||
mLocalMediaManager.registerCallback(mCallback);
|
||||
mLocalMediaManager.connectDevice(device);
|
||||
|
||||
mLocalMediaManager.mDeviceAttributeChangeCallback.onDeviceAttributesChanged();
|
||||
verify(device).setState(LocalMediaManager.MediaDeviceState.STATE_CONNECTING_FAILED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onRequestFailed_checkDevicesState() {
|
||||
mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice1);
|
||||
mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice2);
|
||||
mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
|
||||
mInfoMediaDevice2.setState(LocalMediaManager.MediaDeviceState.STATE_CONNECTED);
|
||||
|
||||
assertThat(mInfoMediaDevice1.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
|
||||
.STATE_CONNECTING);
|
||||
assertThat(mInfoMediaDevice2.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
|
||||
.STATE_CONNECTED);
|
||||
mLocalMediaManager.registerCallback(mCallback);
|
||||
mLocalMediaManager.mMediaDeviceCallback.onRequestFailed(REASON_UNKNOWN_ERROR);
|
||||
|
||||
assertThat(mInfoMediaDevice1.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
|
||||
.STATE_CONNECTING_FAILED);
|
||||
assertThat(mInfoMediaDevice2.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
|
||||
.STATE_CONNECTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeviceAttributesChanged_shouldBeCalled() {
|
||||
mLocalMediaManager.registerCallback(mCallback);
|
||||
|
||||
mLocalMediaManager.mDeviceAttributeChangeCallback.onDeviceAttributesChanged();
|
||||
|
||||
verify(mCallback).onDeviceAttributesChanged();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getActiveMediaSession_verifyCorrectSession() {
|
||||
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
|
||||
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
|
||||
when(info.getId()).thenReturn(TEST_SESSION_ID);
|
||||
routingSessionInfos.add(info);
|
||||
when(mInfoMediaManager.getRemoteSessions()).thenReturn(routingSessionInfos);
|
||||
|
||||
assertThat(mLocalMediaManager.getRemoteRoutingSessions().get(0).getId())
|
||||
.matches(TEST_SESSION_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeviceListAdded_haveMutingExpectedDevice_addMutingExpectedDevice() {
|
||||
final List<MediaDevice> devices = new ArrayList<>();
|
||||
final MediaDevice device1 = mock(MediaDevice.class);
|
||||
devices.add(device1);
|
||||
|
||||
final List<LocalBluetoothProfile> profiles = new ArrayList<>();
|
||||
final A2dpProfile a2dpProfile = mock(A2dpProfile.class);
|
||||
profiles.add(a2dpProfile);
|
||||
|
||||
final List<BluetoothDevice> bluetoothDevices = new ArrayList<>();
|
||||
final BluetoothDevice bluetoothDevice = mock(BluetoothDevice.class);
|
||||
final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
|
||||
final CachedBluetoothDeviceManager cachedManager = mock(CachedBluetoothDeviceManager.class);
|
||||
bluetoothDevices.add(bluetoothDevice);
|
||||
mShadowBluetoothAdapter.setMostRecentlyConnectedDevices(bluetoothDevices);
|
||||
|
||||
AudioDeviceAttributes audioDeviceAttributes = new AudioDeviceAttributes(
|
||||
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, TEST_ADDRESS);
|
||||
|
||||
when(mAudioManager.getMutingExpectedDevice()).thenReturn(audioDeviceAttributes);
|
||||
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(cachedManager);
|
||||
when(cachedManager.findDevice(bluetoothDevice)).thenReturn(cachedDevice);
|
||||
when(cachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
|
||||
when(cachedDevice.isConnected()).thenReturn(false);
|
||||
when(cachedDevice.getConnectableProfiles()).thenReturn(profiles);
|
||||
when(cachedDevice.getDevice()).thenReturn(bluetoothDevice);
|
||||
when(cachedDevice.getAddress()).thenReturn(TEST_ADDRESS);
|
||||
when(mA2dpProfile.getActiveDevice()).thenReturn(bluetoothDevice);
|
||||
when(mHapProfile.getActiveDevices()).thenReturn(new ArrayList<>());
|
||||
|
||||
when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
|
||||
when(device1.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE);
|
||||
|
||||
assertThat(mLocalMediaManager.mMediaDevices).hasSize(0);
|
||||
mLocalMediaManager.registerCallback(mCallback);
|
||||
mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
|
||||
|
||||
assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
|
||||
verify(mCallback).onDeviceListUpdate(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeviceListAdded_transferToDisconnectedBluetooth_verifyConnectDevice() {
|
||||
final List<MediaDevice> devices = new ArrayList<>();
|
||||
final MediaDevice currentDevice = mock(MediaDevice.class);
|
||||
final MediaDevice device = mock(BluetoothMediaDevice.class);
|
||||
final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
|
||||
mLocalMediaManager.mMediaDevices.add(device);
|
||||
mLocalMediaManager.mMediaDevices.add(currentDevice);
|
||||
|
||||
when(device.getId()).thenReturn(TEST_DEVICE_ID_1);
|
||||
when(currentDevice.getId()).thenReturn(TEST_CURRENT_DEVICE_ID);
|
||||
when(((BluetoothMediaDevice) device).getCachedDevice()).thenReturn(cachedDevice);
|
||||
when(cachedDevice.isConnected()).thenReturn(false);
|
||||
when(cachedDevice.isBusy()).thenReturn(false);
|
||||
|
||||
mLocalMediaManager.registerCallback(mCallback);
|
||||
mLocalMediaManager.connectDevice(device);
|
||||
|
||||
verify(cachedDevice).connect();
|
||||
when(device.isConnected()).thenReturn(true);
|
||||
mLocalMediaManager.mCurrentConnectedDevice = currentDevice;
|
||||
devices.add(mInfoMediaDevice1);
|
||||
devices.add(currentDevice);
|
||||
mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
|
||||
|
||||
verify(mInfoMediaManager).connectToDevice(mInfoMediaDevice1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onRequestFailed_shouldDispatchOnRequestFailed() {
|
||||
mLocalMediaManager.registerCallback(mCallback);
|
||||
|
||||
mLocalMediaManager.mMediaDeviceCallback.onRequestFailed(1);
|
||||
|
||||
verify(mCallback).onRequestFailed(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeviceListAdded_bluetoothAdapterIsNull_noDisconnectedDeviceAdded() {
|
||||
final List<MediaDevice> devices = new ArrayList<>();
|
||||
final MediaDevice device1 = mock(MediaDevice.class);
|
||||
final MediaDevice device2 = mock(MediaDevice.class);
|
||||
final MediaDevice device3 = mock(MediaDevice.class);
|
||||
devices.add(device1);
|
||||
devices.add(device2);
|
||||
mLocalMediaManager.mMediaDevices.add(device3);
|
||||
|
||||
mShadowBluetoothAdapter = null;
|
||||
|
||||
assertThat(mLocalMediaManager.mMediaDevices).hasSize(1);
|
||||
mLocalMediaManager.registerCallback(mCallback);
|
||||
mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
|
||||
|
||||
assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
|
||||
verify(mCallback).onDeviceListUpdate(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adjustSessionVolume_verifyCorrectSessionVolume() {
|
||||
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
|
||||
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
|
||||
when(info.getId()).thenReturn(TEST_SESSION_ID);
|
||||
routingSessionInfos.add(info);
|
||||
when(mInfoMediaManager.getRoutingSessionById(TEST_SESSION_ID)).thenReturn(info);
|
||||
|
||||
mLocalMediaManager.adjustSessionVolume(TEST_SESSION_ID, 10);
|
||||
|
||||
verify(mInfoMediaManager).adjustSessionVolume(info, 10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateCurrentConnectedDevice_bluetoothDeviceIsActive_returnBluetoothDevice() {
|
||||
final BluetoothMediaDevice device1 = mock(BluetoothMediaDevice.class);
|
||||
final BluetoothMediaDevice device2 = mock(BluetoothMediaDevice.class);
|
||||
final PhoneMediaDevice phoneDevice = mock(PhoneMediaDevice.class);
|
||||
final CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class);
|
||||
final CachedBluetoothDevice cachedDevice2 = mock(CachedBluetoothDevice.class);
|
||||
final BluetoothDevice bluetoothDevice1 = mock(BluetoothDevice.class);
|
||||
final BluetoothDevice bluetoothDevice2 = mock(BluetoothDevice.class);
|
||||
|
||||
when(mA2dpProfile.getActiveDevice()).thenReturn(bluetoothDevice2);
|
||||
when(mHapProfile.getActiveDevices()).thenReturn(new ArrayList<>());
|
||||
when(device1.getCachedDevice()).thenReturn(cachedDevice1);
|
||||
when(device2.getCachedDevice()).thenReturn(cachedDevice2);
|
||||
when(cachedDevice1.getDevice()).thenReturn(bluetoothDevice1);
|
||||
when(cachedDevice2.getDevice()).thenReturn(bluetoothDevice2);
|
||||
when(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).thenReturn(false);
|
||||
when(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).thenReturn(true);
|
||||
when(device1.isConnected()).thenReturn(true);
|
||||
when(device2.isConnected()).thenReturn(true);
|
||||
|
||||
mLocalMediaManager.mMediaDevices.add(device1);
|
||||
mLocalMediaManager.mMediaDevices.add(phoneDevice);
|
||||
mLocalMediaManager.mMediaDevices.add(device2);
|
||||
|
||||
assertThat(mLocalMediaManager.updateCurrentConnectedDevice()).isEqualTo(device2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateCurrentConnectedDevice_phoneDeviceIsActive_returnPhoneDevice() {
|
||||
final BluetoothMediaDevice device1 = mock(BluetoothMediaDevice.class);
|
||||
final BluetoothMediaDevice device2 = mock(BluetoothMediaDevice.class);
|
||||
final PhoneMediaDevice phoneDevice = mock(PhoneMediaDevice.class);
|
||||
final CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class);
|
||||
final CachedBluetoothDevice cachedDevice2 = mock(CachedBluetoothDevice.class);
|
||||
final BluetoothDevice bluetoothDevice1 = mock(BluetoothDevice.class);
|
||||
final BluetoothDevice bluetoothDevice2 = mock(BluetoothDevice.class);
|
||||
|
||||
when(mA2dpProfile.getActiveDevice()).thenReturn(null);
|
||||
when(mHapProfile.getActiveDevices()).thenReturn(new ArrayList<>());
|
||||
when(device1.getCachedDevice()).thenReturn(cachedDevice1);
|
||||
when(device2.getCachedDevice()).thenReturn(cachedDevice2);
|
||||
when(cachedDevice1.getDevice()).thenReturn(bluetoothDevice1);
|
||||
when(cachedDevice2.getDevice()).thenReturn(bluetoothDevice2);
|
||||
when(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).thenReturn(false);
|
||||
when(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).thenReturn(false);
|
||||
when(device1.isConnected()).thenReturn(true);
|
||||
when(device2.isConnected()).thenReturn(true);
|
||||
|
||||
mLocalMediaManager.mMediaDevices.add(device1);
|
||||
mLocalMediaManager.mMediaDevices.add(phoneDevice);
|
||||
mLocalMediaManager.mMediaDevices.add(device2);
|
||||
|
||||
assertThat(mLocalMediaManager.updateCurrentConnectedDevice()).isEqualTo(phoneDevice);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,510 @@
|
||||
/*
|
||||
* 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.media;
|
||||
|
||||
import static android.media.MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
|
||||
import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
|
||||
import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
|
||||
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
|
||||
import static android.media.RouteListingPreference.Item.SELECTION_BEHAVIOR_GO_TO_APP;
|
||||
|
||||
import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothClass;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Context;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.media.NearbyDevice;
|
||||
import android.media.RouteListingPreference;
|
||||
import android.os.Parcel;
|
||||
|
||||
import com.android.settingslib.bluetooth.A2dpProfile;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.HearingAidProfile;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
||||
|
||||
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.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class MediaDeviceTest {
|
||||
private static final Comparator<MediaDevice> COMPARATOR = Comparator.naturalOrder();
|
||||
private static final String DEVICE_ADDRESS_1 = "AA:BB:CC:DD:EE:11";
|
||||
private static final String DEVICE_ADDRESS_2 = "AA:BB:CC:DD:EE:22";
|
||||
private static final String DEVICE_ADDRESS_3 = "AA:BB:CC:DD:EE:33";
|
||||
private static final String DEVICE_NAME_1 = "TestName_1";
|
||||
private static final String DEVICE_NAME_2 = "TestName_2";
|
||||
private static final String DEVICE_NAME_3 = "TestName_3";
|
||||
private static final String ROUTER_ID_1 = "RouterId_1";
|
||||
private static final String ROUTER_ID_2 = "RouterId_2";
|
||||
private static final String ROUTER_ID_3 = "RouterId_3";
|
||||
private static final String TEST_PACKAGE_NAME = "com.test.playmusic";
|
||||
private final BluetoothClass mHeadreeClass =
|
||||
createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
|
||||
private final BluetoothClass mCarkitClass =
|
||||
createBtClass(BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO);
|
||||
|
||||
@Mock
|
||||
private BluetoothDevice mDevice1;
|
||||
@Mock
|
||||
private BluetoothDevice mDevice2;
|
||||
@Mock
|
||||
private BluetoothDevice mDevice3;
|
||||
@Mock
|
||||
private CachedBluetoothDevice mCachedDevice1;
|
||||
@Mock
|
||||
private CachedBluetoothDevice mCachedDevice2;
|
||||
@Mock
|
||||
private CachedBluetoothDevice mCachedDevice3;
|
||||
@Mock
|
||||
private LocalBluetoothManager mLocalBluetoothManager;
|
||||
@Mock
|
||||
private MediaRoute2Info mRouteInfo1;
|
||||
@Mock
|
||||
private MediaRoute2Info mRouteInfo2;
|
||||
@Mock
|
||||
private MediaRoute2Info mRouteInfo3;
|
||||
@Mock
|
||||
private MediaRoute2Info mBluetoothRouteInfo1;
|
||||
@Mock
|
||||
private MediaRoute2Info mBluetoothRouteInfo2;
|
||||
@Mock
|
||||
private MediaRoute2Info mBluetoothRouteInfo3;
|
||||
@Mock
|
||||
private MediaRoute2Info mPhoneRouteInfo;
|
||||
@Mock
|
||||
private LocalBluetoothProfileManager mProfileManager;
|
||||
@Mock
|
||||
private HearingAidProfile mHapProfile;
|
||||
@Mock
|
||||
private A2dpProfile mA2dpProfile;
|
||||
@Mock
|
||||
private BluetoothDevice mDevice;
|
||||
private RouteListingPreference.Item mItem;
|
||||
|
||||
private BluetoothMediaDevice mBluetoothMediaDevice1;
|
||||
private BluetoothMediaDevice mBluetoothMediaDevice2;
|
||||
private BluetoothMediaDevice mBluetoothMediaDevice3;
|
||||
private Context mContext;
|
||||
private InfoMediaDevice mInfoMediaDevice1;
|
||||
private InfoMediaDevice mInfoMediaDevice2;
|
||||
private InfoMediaDevice mInfoMediaDevice3;
|
||||
private List<MediaDevice> mMediaDevices = new ArrayList<>();
|
||||
private PhoneMediaDevice mPhoneMediaDevice;
|
||||
|
||||
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(mCachedDevice1.getAddress()).thenReturn(DEVICE_ADDRESS_1);
|
||||
when(mCachedDevice2.getAddress()).thenReturn(DEVICE_ADDRESS_2);
|
||||
when(mCachedDevice3.getAddress()).thenReturn(DEVICE_ADDRESS_3);
|
||||
when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME_1);
|
||||
when(mCachedDevice2.getName()).thenReturn(DEVICE_NAME_2);
|
||||
when(mCachedDevice3.getName()).thenReturn(DEVICE_NAME_3);
|
||||
when(mCachedDevice1.getDevice()).thenReturn(mDevice1);
|
||||
when(mCachedDevice2.getDevice()).thenReturn(mDevice2);
|
||||
when(mCachedDevice3.getDevice()).thenReturn(mDevice3);
|
||||
when(mCachedDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
|
||||
when(mCachedDevice1.isConnected()).thenReturn(true);
|
||||
when(mCachedDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
|
||||
when(mCachedDevice2.isConnected()).thenReturn(true);
|
||||
when(mCachedDevice3.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
|
||||
when(mCachedDevice3.isConnected()).thenReturn(true);
|
||||
when(mBluetoothRouteInfo1.getType()).thenReturn(TYPE_BLUETOOTH_A2DP);
|
||||
when(mBluetoothRouteInfo2.getType()).thenReturn(TYPE_BLUETOOTH_A2DP);
|
||||
when(mBluetoothRouteInfo3.getType()).thenReturn(TYPE_BLUETOOTH_A2DP);
|
||||
when(mRouteInfo1.getId()).thenReturn(ROUTER_ID_1);
|
||||
when(mRouteInfo2.getId()).thenReturn(ROUTER_ID_2);
|
||||
when(mRouteInfo3.getId()).thenReturn(ROUTER_ID_3);
|
||||
when(mRouteInfo1.getName()).thenReturn(DEVICE_NAME_1);
|
||||
when(mRouteInfo2.getName()).thenReturn(DEVICE_NAME_2);
|
||||
when(mRouteInfo3.getName()).thenReturn(DEVICE_NAME_3);
|
||||
when(mRouteInfo1.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
|
||||
when(mRouteInfo2.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
|
||||
when(mRouteInfo3.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
|
||||
when(mPhoneRouteInfo.getType()).thenReturn(TYPE_BUILTIN_SPEAKER);
|
||||
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mProfileManager);
|
||||
when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
|
||||
when(mProfileManager.getHearingAidProfile()).thenReturn(mHapProfile);
|
||||
when(mA2dpProfile.getActiveDevice()).thenReturn(mDevice);
|
||||
|
||||
mBluetoothMediaDevice1 =
|
||||
new BluetoothMediaDevice(
|
||||
mContext, mCachedDevice1, mBluetoothRouteInfo1);
|
||||
mBluetoothMediaDevice2 =
|
||||
new BluetoothMediaDevice(
|
||||
mContext, mCachedDevice2, mBluetoothRouteInfo2);
|
||||
mBluetoothMediaDevice3 =
|
||||
new BluetoothMediaDevice(
|
||||
mContext, mCachedDevice3, mBluetoothRouteInfo3);
|
||||
mInfoMediaDevice1 = new InfoMediaDevice(mContext, mRouteInfo1);
|
||||
mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2);
|
||||
mInfoMediaDevice3 = new InfoMediaDevice(mContext, mRouteInfo3);
|
||||
mPhoneMediaDevice = new PhoneMediaDevice(mContext, mPhoneRouteInfo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareTo_carKit_nonCarKitBluetooth_carKitFirst() {
|
||||
when(mDevice1.getBluetoothClass()).thenReturn(mHeadreeClass);
|
||||
when(mDevice2.getBluetoothClass()).thenReturn(mCarkitClass);
|
||||
mMediaDevices.add(mBluetoothMediaDevice1);
|
||||
mMediaDevices.add(mBluetoothMediaDevice2);
|
||||
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
|
||||
Collections.sort(mMediaDevices, COMPARATOR);
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareTo_differentRange_sortWithRange() {
|
||||
mBluetoothMediaDevice1.setRangeZone(NearbyDevice.RANGE_FAR);
|
||||
mBluetoothMediaDevice2.setRangeZone(NearbyDevice.RANGE_CLOSE);
|
||||
mMediaDevices.add(mBluetoothMediaDevice1);
|
||||
mMediaDevices.add(mBluetoothMediaDevice2);
|
||||
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
|
||||
Collections.sort(mMediaDevices, COMPARATOR);
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareTo_carKit_info_carKitFirst() {
|
||||
when(mDevice1.getBluetoothClass()).thenReturn(mCarkitClass);
|
||||
mMediaDevices.add(mInfoMediaDevice1);
|
||||
mMediaDevices.add(mBluetoothMediaDevice1);
|
||||
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mInfoMediaDevice1);
|
||||
Collections.sort(mMediaDevices, COMPARATOR);
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareTo_carKit_phone_phoneFirst() {
|
||||
when(mDevice1.getBluetoothClass()).thenReturn(mCarkitClass);
|
||||
mMediaDevices.add(mBluetoothMediaDevice1);
|
||||
mMediaDevices.add(mPhoneMediaDevice);
|
||||
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
|
||||
Collections.sort(mMediaDevices, COMPARATOR);
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mPhoneMediaDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareTo_carKitIsDisConnected_nonCarKitBluetooth_nonCarKitBluetoothFirst() {
|
||||
when(mDevice1.getBluetoothClass()).thenReturn(mHeadreeClass);
|
||||
when(mDevice2.getBluetoothClass()).thenReturn(mCarkitClass);
|
||||
when(mCachedDevice2.isConnected()).thenReturn(false);
|
||||
mMediaDevices.add(mBluetoothMediaDevice1);
|
||||
mMediaDevices.add(mBluetoothMediaDevice2);
|
||||
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
|
||||
Collections.sort(mMediaDevices, COMPARATOR);
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareTo_lastSelected_others_lastSelectedFirst() {
|
||||
mMediaDevices.add(mBluetoothMediaDevice1);
|
||||
mMediaDevices.add(mBluetoothMediaDevice2);
|
||||
mBluetoothMediaDevice2.setConnectedRecord();
|
||||
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
|
||||
Collections.sort(mMediaDevices, COMPARATOR);
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareTo_connectionRecord_sortByRecord() {
|
||||
mMediaDevices.add(mBluetoothMediaDevice1);
|
||||
mMediaDevices.add(mBluetoothMediaDevice2);
|
||||
mBluetoothMediaDevice1.setConnectedRecord();
|
||||
mBluetoothMediaDevice2.setConnectedRecord();
|
||||
mBluetoothMediaDevice2.setConnectedRecord();
|
||||
// Reset last selected record
|
||||
ConnectionRecordManager.getInstance().setConnectionRecord(mContext, null, 0);
|
||||
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
|
||||
assertThat(mMediaDevices.get(1)).isEqualTo(mBluetoothMediaDevice2);
|
||||
Collections.sort(mMediaDevices, COMPARATOR);
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice2);
|
||||
assertThat(mMediaDevices.get(1)).isEqualTo(mBluetoothMediaDevice1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareTo_sortByRecord_connectedDeviceFirst() {
|
||||
mMediaDevices.add(mBluetoothMediaDevice1);
|
||||
mMediaDevices.add(mBluetoothMediaDevice2);
|
||||
when(mCachedDevice2.isConnected()).thenReturn(false);
|
||||
|
||||
mBluetoothMediaDevice1.setConnectedRecord();
|
||||
mBluetoothMediaDevice2.setConnectedRecord();
|
||||
mBluetoothMediaDevice2.setConnectedRecord();
|
||||
// Reset last selected record
|
||||
ConnectionRecordManager.getInstance().setConnectionRecord(mContext, null, 0);
|
||||
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
|
||||
assertThat(mMediaDevices.get(1)).isEqualTo(mBluetoothMediaDevice2);
|
||||
Collections.sort(mMediaDevices, COMPARATOR);
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
|
||||
assertThat(mMediaDevices.get(1)).isEqualTo(mBluetoothMediaDevice2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareTo_info_bluetooth_bluetoothFirst() {
|
||||
mMediaDevices.add(mInfoMediaDevice1);
|
||||
mMediaDevices.add(mBluetoothMediaDevice1);
|
||||
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mInfoMediaDevice1);
|
||||
Collections.sort(mMediaDevices, COMPARATOR);
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareTo_bluetooth_phone_phoneFirst() {
|
||||
mMediaDevices.add(mBluetoothMediaDevice1);
|
||||
mMediaDevices.add(mPhoneMediaDevice);
|
||||
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
|
||||
Collections.sort(mMediaDevices, COMPARATOR);
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mPhoneMediaDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareTo_bluetooth_wiredHeadset_wiredHeadsetFirst() {
|
||||
final MediaRoute2Info phoneRouteInfo = mock(MediaRoute2Info.class);
|
||||
when(phoneRouteInfo.getType()).thenReturn(TYPE_WIRED_HEADPHONES);
|
||||
|
||||
final PhoneMediaDevice phoneMediaDevice =
|
||||
new PhoneMediaDevice(mContext, phoneRouteInfo);
|
||||
|
||||
mMediaDevices.add(mBluetoothMediaDevice1);
|
||||
mMediaDevices.add(phoneMediaDevice);
|
||||
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
|
||||
Collections.sort(mMediaDevices, COMPARATOR);
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(phoneMediaDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareTo_info_wiredHeadset_wiredHeadsetFirst() {
|
||||
final MediaRoute2Info phoneRouteInfo = mock(MediaRoute2Info.class);
|
||||
when(phoneRouteInfo.getType()).thenReturn(TYPE_WIRED_HEADPHONES);
|
||||
|
||||
final PhoneMediaDevice phoneMediaDevice =
|
||||
new PhoneMediaDevice(mContext, phoneRouteInfo);
|
||||
|
||||
mMediaDevices.add(mInfoMediaDevice1);
|
||||
mMediaDevices.add(phoneMediaDevice);
|
||||
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mInfoMediaDevice1);
|
||||
Collections.sort(mMediaDevices, COMPARATOR);
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(phoneMediaDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareTo_twoInfo_sortByAlphabet() {
|
||||
mMediaDevices.add(mInfoMediaDevice2);
|
||||
mMediaDevices.add(mInfoMediaDevice1);
|
||||
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mInfoMediaDevice2);
|
||||
Collections.sort(mMediaDevices, COMPARATOR);
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mInfoMediaDevice1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareTo_twoBluetooth_sortByAlphabet() {
|
||||
mMediaDevices.add(mBluetoothMediaDevice2);
|
||||
mMediaDevices.add(mBluetoothMediaDevice1);
|
||||
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice2);
|
||||
Collections.sort(mMediaDevices, COMPARATOR);
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareTo_sortByAlphabet_connectDeviceFirst() {
|
||||
mMediaDevices.add(mBluetoothMediaDevice2);
|
||||
mMediaDevices.add(mBluetoothMediaDevice1);
|
||||
when(mCachedDevice1.isConnected()).thenReturn(false);
|
||||
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice2);
|
||||
Collections.sort(mMediaDevices, COMPARATOR);
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mBluetoothMediaDevice2);
|
||||
}
|
||||
|
||||
// 1.mInfoMediaDevice1: Last Selected device
|
||||
// 2.mBluetoothMediaDevice1: CarKit device
|
||||
// 3.mInfoMediaDevice2: * 2 times usage
|
||||
// 4.mInfoMediaDevice3: * 1 time usage
|
||||
// 5.mBluetoothMediaDevice2: * 2 times usage
|
||||
// 6.mBluetoothMediaDevice3: * 1 time usage
|
||||
// 7.mPhoneMediaDevice: * 0 time usage
|
||||
// Order: 7 -> 2 -> 5 -> 6 -> 1 -> 3 -> 4
|
||||
@Test
|
||||
public void compareTo_mixedDevices_carKitFirst() {
|
||||
when(mDevice1.getBluetoothClass()).thenReturn(mCarkitClass);
|
||||
when(mDevice2.getBluetoothClass()).thenReturn(mHeadreeClass);
|
||||
when(mDevice3.getBluetoothClass()).thenReturn(mHeadreeClass);
|
||||
mMediaDevices.add(mBluetoothMediaDevice1);
|
||||
mMediaDevices.add(mBluetoothMediaDevice2);
|
||||
mMediaDevices.add(mBluetoothMediaDevice3);
|
||||
mMediaDevices.add(mInfoMediaDevice1);
|
||||
mMediaDevices.add(mInfoMediaDevice2);
|
||||
mMediaDevices.add(mInfoMediaDevice3);
|
||||
mMediaDevices.add(mPhoneMediaDevice);
|
||||
mBluetoothMediaDevice3.setConnectedRecord();
|
||||
mBluetoothMediaDevice2.setConnectedRecord();
|
||||
mBluetoothMediaDevice2.setConnectedRecord();
|
||||
mInfoMediaDevice3.setConnectedRecord();
|
||||
mInfoMediaDevice2.setConnectedRecord();
|
||||
mInfoMediaDevice2.setConnectedRecord();
|
||||
mInfoMediaDevice1.setConnectedRecord();
|
||||
|
||||
Collections.sort(mMediaDevices, COMPARATOR);
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mPhoneMediaDevice);
|
||||
assertThat(mMediaDevices.get(1)).isEqualTo(mBluetoothMediaDevice1);
|
||||
assertThat(mMediaDevices.get(2)).isEqualTo(mBluetoothMediaDevice2);
|
||||
assertThat(mMediaDevices.get(3)).isEqualTo(mBluetoothMediaDevice3);
|
||||
assertThat(mMediaDevices.get(4)).isEqualTo(mInfoMediaDevice1);
|
||||
assertThat(mMediaDevices.get(5)).isEqualTo(mInfoMediaDevice2);
|
||||
assertThat(mMediaDevices.get(6)).isEqualTo(mInfoMediaDevice3);
|
||||
}
|
||||
|
||||
// 1.mInfoMediaDevice1: Last Selected device
|
||||
// 2.mBluetoothMediaDevice1: CarKit device not connected
|
||||
// 3.mInfoMediaDevice2: * 2 times usage
|
||||
// 4.mInfoMediaDevice3: * 1 time usage
|
||||
// 5.mBluetoothMediaDevice2: * 4 times usage not connected
|
||||
// 6.mBluetoothMediaDevice3: * 1 time usage
|
||||
// 7.mPhoneMediaDevice: * 0 time usage
|
||||
// Order: 7 -> 6 -> 1 -> 3 -> 4 -> 2 -> 5
|
||||
@Test
|
||||
public void compareTo_mixedDevices_connectDeviceFirst() {
|
||||
when(mDevice1.getBluetoothClass()).thenReturn(mCarkitClass);
|
||||
when(mDevice2.getBluetoothClass()).thenReturn(mHeadreeClass);
|
||||
when(mDevice3.getBluetoothClass()).thenReturn(mHeadreeClass);
|
||||
when(mCachedDevice1.isConnected()).thenReturn(false);
|
||||
when(mCachedDevice2.isConnected()).thenReturn(false);
|
||||
mMediaDevices.add(mBluetoothMediaDevice1);
|
||||
mMediaDevices.add(mBluetoothMediaDevice2);
|
||||
mMediaDevices.add(mBluetoothMediaDevice3);
|
||||
mMediaDevices.add(mInfoMediaDevice1);
|
||||
mMediaDevices.add(mInfoMediaDevice2);
|
||||
mMediaDevices.add(mInfoMediaDevice3);
|
||||
mMediaDevices.add(mPhoneMediaDevice);
|
||||
mBluetoothMediaDevice3.setConnectedRecord();
|
||||
mBluetoothMediaDevice2.setConnectedRecord();
|
||||
mBluetoothMediaDevice2.setConnectedRecord();
|
||||
mBluetoothMediaDevice2.setConnectedRecord();
|
||||
mBluetoothMediaDevice2.setConnectedRecord();
|
||||
mInfoMediaDevice3.setConnectedRecord();
|
||||
mInfoMediaDevice2.setConnectedRecord();
|
||||
mInfoMediaDevice2.setConnectedRecord();
|
||||
mInfoMediaDevice1.setConnectedRecord();
|
||||
|
||||
Collections.sort(mMediaDevices, COMPARATOR);
|
||||
assertThat(mMediaDevices.get(0)).isEqualTo(mPhoneMediaDevice);
|
||||
assertThat(mMediaDevices.get(1)).isEqualTo(mBluetoothMediaDevice3);
|
||||
assertThat(mMediaDevices.get(2)).isEqualTo(mInfoMediaDevice1);
|
||||
assertThat(mMediaDevices.get(3)).isEqualTo(mInfoMediaDevice2);
|
||||
assertThat(mMediaDevices.get(4)).isEqualTo(mInfoMediaDevice3);
|
||||
assertThat(mMediaDevices.get(5)).isEqualTo(mBluetoothMediaDevice1);
|
||||
assertThat(mMediaDevices.get(6)).isEqualTo(mBluetoothMediaDevice2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getClientPackageName_returnPackageName() {
|
||||
when(mRouteInfo1.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
|
||||
assertThat(mInfoMediaDevice1.getClientPackageName()).isEqualTo(TEST_PACKAGE_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setState_verifyGetState() {
|
||||
mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_CONNECTED);
|
||||
assertThat(mInfoMediaDevice1.getState()).isEqualTo(
|
||||
LocalMediaManager.MediaDeviceState.STATE_CONNECTED);
|
||||
|
||||
mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
|
||||
assertThat(mInfoMediaDevice1.getState()).isEqualTo(
|
||||
LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
|
||||
|
||||
mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
|
||||
assertThat(mInfoMediaDevice1.getState()).isEqualTo(
|
||||
LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
|
||||
|
||||
mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_CONNECTING_FAILED);
|
||||
assertThat(mInfoMediaDevice1.getState()).isEqualTo(
|
||||
LocalMediaManager.MediaDeviceState.STATE_CONNECTING_FAILED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFeatures_noRouteInfo_returnEmptyList() {
|
||||
mBluetoothMediaDevice1 =
|
||||
new BluetoothMediaDevice(
|
||||
mContext, mCachedDevice1, /* MediaRoute2Info */ null);
|
||||
|
||||
assertThat(mBluetoothMediaDevice1.getFeatures().size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSelectionBehavior_setItemWithSelectionBehaviorOnSystemRoute_returnTransfer() {
|
||||
mItem = new RouteListingPreference.Item.Builder(DEVICE_ADDRESS_1)
|
||||
.setSelectionBehavior(SELECTION_BEHAVIOR_GO_TO_APP)
|
||||
.build();
|
||||
mBluetoothMediaDevice1 =
|
||||
new BluetoothMediaDevice(
|
||||
mContext,
|
||||
mCachedDevice1,
|
||||
null /* MediaRoute2Info */,
|
||||
mItem);
|
||||
mPhoneMediaDevice =
|
||||
new PhoneMediaDevice(mContext, mPhoneRouteInfo, mItem);
|
||||
|
||||
assertThat(mBluetoothMediaDevice1.getSelectionBehavior()).isEqualTo(
|
||||
SELECTION_BEHAVIOR_TRANSFER);
|
||||
assertThat(mPhoneMediaDevice.getSelectionBehavior()).isEqualTo(
|
||||
SELECTION_BEHAVIOR_TRANSFER);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 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.media;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
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.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class MediaManagerTest {
|
||||
|
||||
private static final String TEST_ID = "test_id";
|
||||
|
||||
@Mock
|
||||
private MediaManager.MediaDeviceCallback mCallback;
|
||||
@Mock
|
||||
private MediaDevice mDevice;
|
||||
|
||||
private MediaManager mMediaManager;
|
||||
private Context mContext;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
|
||||
when(mDevice.getId()).thenReturn(TEST_ID);
|
||||
|
||||
mMediaManager = new MediaManager(mContext, null) {};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dispatchDeviceListAdded_registerCallback_shouldDispatchCallback() {
|
||||
mMediaManager.registerCallback(mCallback);
|
||||
|
||||
mMediaManager.dispatchDeviceListAdded();
|
||||
|
||||
verify(mCallback).onDeviceListAdded(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dispatchDeviceListRemoved_registerCallback_shouldDispatchCallback() {
|
||||
mMediaManager.registerCallback(mCallback);
|
||||
|
||||
mMediaManager.dispatchDeviceListRemoved(mMediaManager.mMediaDevices);
|
||||
|
||||
verify(mCallback).onDeviceListRemoved(mMediaManager.mMediaDevices);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dispatchActiveDeviceChanged_registerCallback_shouldDispatchCallback() {
|
||||
mMediaManager.registerCallback(mCallback);
|
||||
|
||||
mMediaManager.dispatchConnectedDeviceChanged(TEST_ID);
|
||||
|
||||
verify(mCallback).onConnectedDeviceChanged(TEST_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findMediaDevice_idExist_shouldReturnMediaDevice() {
|
||||
mMediaManager.mMediaDevices.add(mDevice);
|
||||
|
||||
final MediaDevice device = mMediaManager.findMediaDevice(TEST_ID);
|
||||
|
||||
assertThat(device.getId()).isEqualTo(mDevice.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findMediaDevice_idNotExist_shouldReturnNull() {
|
||||
mMediaManager.mMediaDevices.add(mDevice);
|
||||
|
||||
final MediaDevice device = mMediaManager.findMediaDevice("123");
|
||||
|
||||
assertThat(device).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dispatchOnRequestFailed_registerCallback_shouldDispatchCallback() {
|
||||
mMediaManager.registerCallback(mCallback);
|
||||
|
||||
mMediaManager.dispatchOnRequestFailed(1);
|
||||
|
||||
verify(mCallback).onRequestFailed(1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright 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.media;
|
||||
|
||||
import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
|
||||
import static android.media.MediaRoute2Info.TYPE_USB_DEVICE;
|
||||
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
|
||||
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
|
||||
|
||||
import static com.android.settingslib.media.PhoneMediaDevice.PHONE_ID;
|
||||
import static com.android.settingslib.media.PhoneMediaDevice.USB_HEADSET_ID;
|
||||
import static com.android.settingslib.media.PhoneMediaDevice.WIRED_HEADSET_ID;
|
||||
import static com.android.settingslib.media.PhoneMediaDevice.getMediaTransferThisDeviceName;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.platform.test.annotations.DisableFlags;
|
||||
import android.platform.test.annotations.EnableFlags;
|
||||
import android.platform.test.flag.junit.SetFlagsRule;
|
||||
|
||||
import com.android.media.flags.Flags;
|
||||
import com.android.settingslib.R;
|
||||
|
||||
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;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class PhoneMediaDeviceTest {
|
||||
|
||||
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||
|
||||
@Mock
|
||||
private MediaRoute2Info mInfo;
|
||||
|
||||
private Context mContext;
|
||||
private PhoneMediaDevice mPhoneMediaDevice;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
|
||||
mPhoneMediaDevice = new PhoneMediaDevice(mContext, mInfo, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateSummary_isActiveIsTrue_returnActiveString() {
|
||||
mPhoneMediaDevice.updateSummary(true);
|
||||
|
||||
assertThat(mPhoneMediaDevice.getSummary())
|
||||
.isEqualTo(mContext.getString(R.string.bluetooth_active_no_battery_level));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateSummary_notActive_returnEmpty() {
|
||||
mPhoneMediaDevice.updateSummary(false);
|
||||
|
||||
assertThat(mPhoneMediaDevice.getSummary()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDrawableResId_returnCorrectResId() {
|
||||
when(mInfo.getType()).thenReturn(TYPE_WIRED_HEADPHONES);
|
||||
|
||||
assertThat(mPhoneMediaDevice.getDrawableResId()).isEqualTo(R.drawable.ic_headphone);
|
||||
|
||||
when(mInfo.getType()).thenReturn(TYPE_WIRED_HEADSET);
|
||||
|
||||
assertThat(mPhoneMediaDevice.getDrawableResId()).isEqualTo(R.drawable.ic_headphone);
|
||||
|
||||
when(mInfo.getType()).thenReturn(TYPE_BUILTIN_SPEAKER);
|
||||
|
||||
assertThat(mPhoneMediaDevice.getDrawableResId()).isEqualTo(R.drawable.ic_smartphone);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getName_returnCorrectName() {
|
||||
final String deviceName = "test_name";
|
||||
|
||||
when(mInfo.getType()).thenReturn(TYPE_WIRED_HEADPHONES);
|
||||
when(mInfo.getName()).thenReturn(deviceName);
|
||||
|
||||
assertThat(mPhoneMediaDevice.getName())
|
||||
.isEqualTo(mContext.getString(R.string.media_transfer_wired_usb_device_name));
|
||||
|
||||
when(mInfo.getType()).thenReturn(TYPE_USB_DEVICE);
|
||||
|
||||
assertThat(mPhoneMediaDevice.getName())
|
||||
.isEqualTo(mContext.getString(R.string.media_transfer_wired_usb_device_name));
|
||||
|
||||
when(mInfo.getType()).thenReturn(TYPE_BUILTIN_SPEAKER);
|
||||
|
||||
assertThat(mPhoneMediaDevice.getName())
|
||||
.isEqualTo(getMediaTransferThisDeviceName(mContext));
|
||||
}
|
||||
|
||||
@EnableFlags(Flags.FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER)
|
||||
@Test
|
||||
public void getId_whenAdvancedWiredRoutingEnabled_returnCorrectId() {
|
||||
String fakeId = "foo";
|
||||
when(mInfo.getId()).thenReturn(fakeId);
|
||||
|
||||
assertThat(mPhoneMediaDevice.getId()).isEqualTo(fakeId);
|
||||
}
|
||||
|
||||
@DisableFlags(Flags.FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER)
|
||||
@Test
|
||||
public void getId_whenAdvancedWiredRoutingDisabled_returnCorrectId() {
|
||||
when(mInfo.getType()).thenReturn(TYPE_WIRED_HEADPHONES);
|
||||
|
||||
assertThat(mPhoneMediaDevice.getId())
|
||||
.isEqualTo(WIRED_HEADSET_ID);
|
||||
|
||||
when(mInfo.getType()).thenReturn(TYPE_USB_DEVICE);
|
||||
|
||||
assertThat(mPhoneMediaDevice.getId())
|
||||
.isEqualTo(USB_HEADSET_ID);
|
||||
|
||||
when(mInfo.getType()).thenReturn(TYPE_BUILTIN_SPEAKER);
|
||||
|
||||
assertThat(mPhoneMediaDevice.getId())
|
||||
.isEqualTo(PHONE_ID);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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.mobile;
|
||||
|
||||
import static com.android.settingslib.mobile.MobileIconCarrierIdOverridesImpl.parseNetworkIconOverrideTypedArray;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.res.TypedArray;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public final class MobileIconCarrierIdOverridesTest {
|
||||
private static final String OVERRIDE_ICON_1_NAME = "name_1";
|
||||
private static final int OVERRIDE_ICON_1_RES = 1;
|
||||
|
||||
private static final String OVERRIDE_ICON_2_NAME = "name_2";
|
||||
private static final int OVERRIDE_ICON_2_RES = 2;
|
||||
|
||||
NetworkOverrideTypedArrayMock mResourceMock;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mResourceMock = new NetworkOverrideTypedArrayMock(
|
||||
new String[] { OVERRIDE_ICON_1_NAME, OVERRIDE_ICON_2_NAME },
|
||||
new int[] { OVERRIDE_ICON_1_RES, OVERRIDE_ICON_2_RES }
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParse_singleOverride() {
|
||||
mResourceMock.setOverrides(
|
||||
new String[] { OVERRIDE_ICON_1_NAME },
|
||||
new int[] { OVERRIDE_ICON_1_RES }
|
||||
);
|
||||
|
||||
Map<String, Integer> parsed = parseNetworkIconOverrideTypedArray(mResourceMock.getMock());
|
||||
|
||||
assertThat(parsed.get(OVERRIDE_ICON_1_NAME)).isEqualTo(OVERRIDE_ICON_1_RES);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParse_multipleOverrides() {
|
||||
mResourceMock.setOverrides(
|
||||
new String[] { OVERRIDE_ICON_1_NAME, OVERRIDE_ICON_2_NAME },
|
||||
new int[] { OVERRIDE_ICON_1_RES, OVERRIDE_ICON_2_RES }
|
||||
);
|
||||
|
||||
Map<String, Integer> parsed = parseNetworkIconOverrideTypedArray(mResourceMock.getMock());
|
||||
|
||||
assertThat(parsed.get(OVERRIDE_ICON_2_NAME)).isEqualTo(OVERRIDE_ICON_2_RES);
|
||||
assertThat(parsed.get(OVERRIDE_ICON_1_NAME)).isEqualTo(OVERRIDE_ICON_1_RES);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParse_nonexistentKey_isNull() {
|
||||
mResourceMock.setOverrides(
|
||||
new String[] { OVERRIDE_ICON_1_NAME },
|
||||
new int[] { OVERRIDE_ICON_1_RES }
|
||||
);
|
||||
|
||||
Map<String, Integer> parsed = parseNetworkIconOverrideTypedArray(mResourceMock.getMock());
|
||||
|
||||
assertThat(parsed.get(OVERRIDE_ICON_2_NAME)).isNull();
|
||||
}
|
||||
|
||||
static class NetworkOverrideTypedArrayMock {
|
||||
private Object[] mInterleaved;
|
||||
|
||||
private final TypedArray mMockTypedArray = mock(TypedArray.class);
|
||||
|
||||
NetworkOverrideTypedArrayMock(
|
||||
String[] networkTypes,
|
||||
int[] iconOverrides) {
|
||||
|
||||
mInterleaved = interleaveTypes(networkTypes, iconOverrides);
|
||||
|
||||
doAnswer(invocation -> {
|
||||
return mInterleaved[(int) invocation.getArgument(0)];
|
||||
}).when(mMockTypedArray).getString(/* index */ anyInt());
|
||||
|
||||
doAnswer(invocation -> {
|
||||
return mInterleaved[(int) invocation.getArgument(0)];
|
||||
}).when(mMockTypedArray).getResourceId(/* index */ anyInt(), /* default */ anyInt());
|
||||
|
||||
when(mMockTypedArray.length()).thenAnswer(invocation -> {
|
||||
return mInterleaved.length;
|
||||
});
|
||||
}
|
||||
|
||||
TypedArray getMock() {
|
||||
return mMockTypedArray;
|
||||
}
|
||||
|
||||
void setOverrides(String[] types, int[] resIds) {
|
||||
mInterleaved = interleaveTypes(types, resIds);
|
||||
}
|
||||
|
||||
private Object[] interleaveTypes(String[] strs, int[] ints) {
|
||||
assertThat(strs.length).isEqualTo(ints.length);
|
||||
|
||||
Object[] ret = new Object[strs.length * 2];
|
||||
|
||||
// Keep track of where we are in the interleaved array, but iterate the overrides
|
||||
int interleavedIndex = 0;
|
||||
for (int i = 0; i < strs.length; i++) {
|
||||
ret[interleavedIndex] = strs[i];
|
||||
interleavedIndex += 1;
|
||||
ret[interleavedIndex] = ints[i];
|
||||
interleavedIndex += 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* 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.net;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.anyInt;
|
||||
import static org.mockito.Mockito.anyLong;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.usage.NetworkStats;
|
||||
import android.app.usage.NetworkStatsManager;
|
||||
import android.content.Context;
|
||||
import android.net.NetworkTemplate;
|
||||
import android.os.RemoteException;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
|
||||
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;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class DataUsageControllerTest {
|
||||
|
||||
private static final String SUB_ID = "Test Subscriber";
|
||||
private static final String SUB_ID_2 = "Test Subscriber 2";
|
||||
|
||||
@Mock
|
||||
private TelephonyManager mTelephonyManager;
|
||||
@Mock
|
||||
private SubscriptionManager mSubscriptionManager;
|
||||
@Mock
|
||||
private NetworkStatsManager mNetworkStatsManager;
|
||||
@Mock
|
||||
private Context mContext;
|
||||
private NetworkTemplate mNetworkTemplate;
|
||||
private NetworkTemplate mNetworkTemplate2;
|
||||
private NetworkTemplate mWifiNetworkTemplate;
|
||||
|
||||
private DataUsageController mController;
|
||||
private final int mDefaultSubscriptionId = 1234;
|
||||
|
||||
@Before
|
||||
public void setUp() throws RemoteException {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
|
||||
when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
|
||||
when(mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE))
|
||||
.thenReturn(mSubscriptionManager);
|
||||
when(mContext.getSystemService(NetworkStatsManager.class)).thenReturn(mNetworkStatsManager);
|
||||
mController = new DataUsageController(mContext);
|
||||
ShadowSubscriptionManager.setDefaultDataSubscriptionId(mDefaultSubscriptionId);
|
||||
doReturn(SUB_ID).when(mTelephonyManager).getSubscriberId();
|
||||
|
||||
mNetworkTemplate = new NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER)
|
||||
.setMeteredness(android.net.NetworkStats.METERED_YES)
|
||||
.setSubscriberIds(Set.of(SUB_ID)).build();
|
||||
mNetworkTemplate2 = new NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER)
|
||||
.setMeteredness(android.net.NetworkStats.METERED_YES)
|
||||
.setSubscriberIds(Set.of(SUB_ID_2)).build();
|
||||
mWifiNetworkTemplate = new NetworkTemplate.Builder(NetworkTemplate.MATCH_WIFI).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHistoricalUsageLevel_shouldQuerySummaryForDevice() throws Exception {
|
||||
mController.getHistoricalUsageLevel(mWifiNetworkTemplate);
|
||||
|
||||
verify(mNetworkStatsManager).querySummaryForDevice(eq(mWifiNetworkTemplate),
|
||||
eq(0L) /* startTime */, anyLong() /* endTime */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHistoricalUsageLevel_noUsageData_shouldReturn0() throws Exception {
|
||||
when(mNetworkStatsManager.querySummaryForDevice(eq(mWifiNetworkTemplate),
|
||||
eq(0L) /* startTime */, anyLong() /* endTime */))
|
||||
.thenReturn(mock(NetworkStats.Bucket.class));
|
||||
assertThat(mController.getHistoricalUsageLevel(mWifiNetworkTemplate))
|
||||
.isEqualTo(0L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHistoricalUsageLevel_hasUsageData_shouldReturnTotalUsage() throws Exception {
|
||||
final long receivedBytes = 743823454L;
|
||||
final long transmittedBytes = 16574289L;
|
||||
final NetworkStats.Bucket bucket = mock(NetworkStats.Bucket.class);
|
||||
when(bucket.getRxBytes()).thenReturn(receivedBytes);
|
||||
when(bucket.getTxBytes()).thenReturn(transmittedBytes);
|
||||
when(mNetworkStatsManager.querySummaryForDevice(eq(mWifiNetworkTemplate),
|
||||
eq(0L) /* startTime */, anyLong() /* endTime */)).thenReturn(bucket);
|
||||
|
||||
assertThat(mController.getHistoricalUsageLevel(mWifiNetworkTemplate))
|
||||
.isEqualTo(receivedBytes + transmittedBytes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDataUsageInfo_hasUsageData_shouldReturnCorrectUsageForExplicitSubId()
|
||||
throws Exception {
|
||||
// First setup a stats bucket for the default subscription / subscriber ID.
|
||||
final long defaultSubRx = 1234567L;
|
||||
final long defaultSubTx = 123456L;
|
||||
final NetworkStats.Bucket defaultSubscriberBucket = mock(NetworkStats.Bucket.class);
|
||||
when(defaultSubscriberBucket.getRxBytes()).thenReturn(defaultSubRx);
|
||||
when(defaultSubscriberBucket.getTxBytes()).thenReturn(defaultSubTx);
|
||||
when(mNetworkStatsManager.querySummaryForDevice(eq(mNetworkTemplate), eq(0L)/* startTime */,
|
||||
anyLong() /* endTime */)).thenReturn(defaultSubscriberBucket);
|
||||
|
||||
// Now setup a stats bucket for a different, non-default subscription / subscriber ID.
|
||||
final long nonDefaultSubRx = 7654321L;
|
||||
final long nonDefaultSubTx = 654321L;
|
||||
final NetworkStats.Bucket nonDefaultSubscriberBucket = mock(NetworkStats.Bucket.class);
|
||||
when(nonDefaultSubscriberBucket.getRxBytes()).thenReturn(nonDefaultSubRx);
|
||||
when(nonDefaultSubscriberBucket.getTxBytes()).thenReturn(nonDefaultSubTx);
|
||||
final int explicitSubscriptionId = 55;
|
||||
when(mNetworkStatsManager.querySummaryForDevice(eq(mNetworkTemplate2),
|
||||
eq(0L)/* startTime */, anyLong() /* endTime */)).thenReturn(
|
||||
nonDefaultSubscriberBucket);
|
||||
doReturn(SUB_ID_2).when(mTelephonyManager).getSubscriberId();
|
||||
|
||||
// Now verify that when we're asking for stats on the non-default subscription, we get
|
||||
// the data back for that subscription and *not* the default one.
|
||||
mController.setSubscriptionId(explicitSubscriptionId);
|
||||
|
||||
assertThat(mController.getHistoricalUsageLevel(mNetworkTemplate2)).isEqualTo(
|
||||
nonDefaultSubRx + nonDefaultSubTx);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTelephonyManager_shouldCreateWithExplicitSubId() {
|
||||
int explicitSubId = 1;
|
||||
TelephonyManager tmForSub1 = new TelephonyManager(mContext, explicitSubId);
|
||||
when(mTelephonyManager.createForSubscriptionId(eq(explicitSubId))).thenReturn(tmForSub1);
|
||||
|
||||
// Set a specific subId.
|
||||
mController.setSubscriptionId(explicitSubId);
|
||||
|
||||
assertThat(mController.getTelephonyManager()).isEqualTo(tmForSub1);
|
||||
verify(mTelephonyManager).createForSubscriptionId(eq(explicitSubId));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTelephonyManager_noExplicitSubId_shouldCreateWithDefaultDataSubId()
|
||||
throws Exception {
|
||||
TelephonyManager tmForDefaultSub = new TelephonyManager(mContext, mDefaultSubscriptionId);
|
||||
when(mTelephonyManager.createForSubscriptionId(mDefaultSubscriptionId))
|
||||
.thenReturn(tmForDefaultSub);
|
||||
|
||||
// No subId is set. It should use default data sub.
|
||||
assertThat(mController.getTelephonyManager()).isEqualTo(tmForDefaultSub);
|
||||
verify(mTelephonyManager).createForSubscriptionId(mDefaultSubscriptionId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTelephonyManager_noExplicitSubIdOrDefaultSub_shouldCreateWithActiveSub()
|
||||
throws Exception {
|
||||
int activeSubId = 2;
|
||||
ShadowSubscriptionManager.setDefaultDataSubscriptionId(
|
||||
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
|
||||
when(mSubscriptionManager.getActiveSubscriptionIdList())
|
||||
.thenReturn(new int[] {activeSubId});
|
||||
TelephonyManager tmForActiveSub = new TelephonyManager(mContext, activeSubId);
|
||||
when(mTelephonyManager.createForSubscriptionId(activeSubId))
|
||||
.thenReturn(tmForActiveSub);
|
||||
|
||||
// No subId is set, default data subId is also not set. So should use the only active subId.
|
||||
assertThat(mController.getTelephonyManager()).isEqualTo(tmForActiveSub);
|
||||
verify(mTelephonyManager).createForSubscriptionId(activeSubId);
|
||||
}
|
||||
}
|
||||
@@ -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.net;
|
||||
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.usage.NetworkStatsManager;
|
||||
import android.content.Context;
|
||||
import android.net.NetworkPolicy;
|
||||
import android.net.NetworkPolicyManager;
|
||||
import android.net.NetworkTemplate;
|
||||
import android.os.RemoteException;
|
||||
import android.text.format.DateUtils;
|
||||
|
||||
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 NetworkCycleChartDataLoaderTest {
|
||||
|
||||
@Mock
|
||||
private NetworkStatsManager mNetworkStatsManager;
|
||||
@Mock
|
||||
private NetworkPolicyManager mNetworkPolicyManager;
|
||||
@Mock
|
||||
private Context mContext;
|
||||
@Mock
|
||||
private NetworkTemplate mNetworkTemplate;
|
||||
|
||||
private NetworkCycleChartDataLoader mLoader;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(mContext.getSystemService(Context.NETWORK_STATS_SERVICE))
|
||||
.thenReturn(mNetworkStatsManager);
|
||||
when(mContext.getSystemService(Context.NETWORK_POLICY_SERVICE))
|
||||
.thenReturn(mNetworkPolicyManager);
|
||||
when(mNetworkPolicyManager.getNetworkPolicies()).thenReturn(new NetworkPolicy[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void recordUsage_shouldQueryNetworkSummaryForDevice() throws RemoteException {
|
||||
final long end = System.currentTimeMillis();
|
||||
final long start = end - (DateUtils.WEEK_IN_MILLIS * 4);
|
||||
mLoader = NetworkCycleChartDataLoader.builder(mContext)
|
||||
.setNetworkTemplate(mNetworkTemplate)
|
||||
.build();
|
||||
|
||||
mLoader.recordUsage(start, end);
|
||||
|
||||
verify(mNetworkStatsManager).querySummaryForDevice(mNetworkTemplate, start, end);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user