Snap for 10157673 from 263049e89d to udc-qpr1-release

Change-Id: I252624612c9e166a995dda93bc1d233ddfb21447
This commit is contained in:
Android Build Coastguard Worker
2023-05-18 03:26:54 +00:00
26 changed files with 1194 additions and 179 deletions

View File

@@ -60,6 +60,8 @@
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:textAlignment="viewStart"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceListItem"
android:hyphenationFrequency="normalFast"

View File

@@ -258,8 +258,10 @@
<!-- Title for stylus device details page [CHAR LIMIT=50] -->
<string name="stylus_device_details_title">Stylus</string>
<!-- Preference title for setting the default note taking app [CHAR LIMIT=none] -->
<string name="stylus_default_notes_app">Default notes app</string>
<!-- Preference title for setting the app that opens when stylus button is pressed [CHAR LIMIT=none] -->
<string name="stylus_default_notes_app">Tail button press</string>
<!-- Summary for the app that opens when tail button is pressed, if set to a work profile app [CHAR LIMIT=none] -->
<string name="stylus_default_notes_summary_work"><xliff:g id="app_name" example="WhatsApp">%s</xliff:g> (Work profile)</string>
<!-- Preference title for toggling whether handwriting in textfields is enabled [CHAR LIMIT=none] -->
<string name="stylus_textfield_handwriting">Write in text fields</string>
<!-- Preference title for toggling whether stylus button presses are ignored [CHAR LIMIT=none] -->
@@ -8164,6 +8166,12 @@
<!-- [CHAR LIMIT=NONE] Channel notification settings: Block option description-->
<string name="notification_content_block_summary">Never show notifications in the shade or on peripheral devices</string>
<!-- [CHAR LIMIT=NONE] App notification settings: Full screen intent permission option title -->
<string name="app_notification_fsi_permission_title">Allow full screen notifications</string>
<!-- [CHAR LIMIT=NONE] App notification settings: Full screen intent permission option description -->
<string name="app_notification_fsi_permission_summary">Allow notifications to take up the full screen when the device is locked</string>
<!-- [CHAR LIMIT=NONE BACKUP_MESSAGE_ID:7166470350070693657] App notification settings: Badging option title -->
<string name="notification_badge_title">Allow notification dot</string>
@@ -10158,7 +10166,9 @@
<string name="financed_privacy_restrictions_removed">All restrictions are removed from the device</string>
<!-- Label explaining that the app installed by credit provider can be uninstalled. [CHAR LIMIT=60]-->
<string name="financed_privacy_uninstall_creditor_app">You can uninstall the creditor app</string>
<!-- Title of setting on security settings screen on a device provisioned by Device Lock. This will take the user to a screen in Device Lock with information about what a device provider can control and their impact on the user's privacy. Shown on Device Lock provisioned devices only. [CHAR LIMIT=NONE] -->
<!-- TODO(b/282040794): Update the title -->
<string name="device_lock_info">Device Lock</string>
<!-- Strings for displaying which applications were set as default for specific actions. -->
<!-- Title for the apps that have been set as default handlers of camera-related intents. [CHAR LIMIT=30] -->
<string name="default_camera_app_title">{count, plural,

View File

@@ -193,6 +193,8 @@
android:key="long_background_tasks"
android:title="@string/long_background_tasks_title"
android:summary="@string/summary_placeholder"
settings:isPreferenceVisible="false"
settings:searchable="false"
settings:controller="com.android.settings.applications.appinfo.LongBackgroundTasksDetailsPreferenceController" />
</PreferenceCategory>

View File

@@ -59,38 +59,57 @@
<com.android.settingslib.RestrictedSwitchPreference
android:key="allow_sound"
android:title="@string/allow_interruption"
android:summary="@string/allow_interruption_summary" />
android:summary="@string/allow_interruption_summary"
settings:allowDividerAbove="true"
settings:allowDividerBelow="false" />
<!-- Visibility Override -->
<com.android.settings.RestrictedListPreference
android:key="visibility_override"
android:title="@string/app_notification_visibility_override_title"/>
android:title="@string/app_notification_visibility_override_title"
settings:allowDividerAbove="true"
settings:allowDividerBelow="false" />
<!-- Bypass DND -->
<com.android.settingslib.RestrictedSwitchPreference
android:key="bypass_dnd"
android:title="@string/app_notification_override_dnd_title"
android:summary="@string/app_notification_override_dnd_summary"/>
android:summary="@string/app_notification_override_dnd_summary"
settings:allowDividerAbove="true"
settings:allowDividerBelow="false" />
<!-- Allow full-screen intents -->
<com.android.settingslib.RestrictedSwitchPreference
android:key="fsi_permission"
android:title="@string/app_notification_fsi_permission_title"
android:summary="@string/app_notification_fsi_permission_summary"
settings:allowDividerAbove="true"
settings:allowDividerBelow="false" />
<!-- Show badge -->
<com.android.settingslib.RestrictedSwitchPreference
android:key="badge"
android:title="@string/notification_badge_title"
settings:useAdditionalSummary="true"
android:order="1001"
android:icon="@drawable/ic_notification_dot"
settings:useAdditionalSummary="true"
settings:restrictedSwitchSummary="@string/enabled_by_admin"
android:order="1001"
settings:allowDividerAbove="true"
settings:restrictedSwitchSummary="@string/enabled_by_admin" />
settings:allowDividerBelow="false" />
<Preference
android:key="app_link"
android:order="1003"
android:icon="@drawable/ic_settings_24dp"
android:title="@string/app_settings_link" />
android:title="@string/app_settings_link"
android:order="1003"
settings:allowDividerAbove="true"
settings:allowDividerBelow="false" />
<com.android.settingslib.widget.FooterPreference
android:key="deleted"
android:icon="@drawable/ic_trash_can"
android:order="8000" />
android:order="8000"
settings:allowDividerAbove="true"
settings:allowDividerBelow="false" />
</PreferenceScreen>

View File

@@ -146,6 +146,13 @@
android:fragment="com.android.settings.enterprise.EnterprisePrivacySettings"
settings:controller="com.android.settings.enterprise.FinancedPrivacyPreferenceController"/>
<Preference
android:key="device_lock_info"
android:title="@string/device_lock_info"
android:summary="@string/summary_placeholder"
settings:controller="com.android.settings.devicelock.DeviceLockPreferenceController">
<intent android:action="com.android.devicelockcontroller.action.DEVICE_INFO_SETTINGS"/>
</Preference>
</PreferenceCategory>
<Preference

View File

@@ -58,6 +58,13 @@
android:fragment="com.android.settings.enterprise.EnterprisePrivacySettings"
settings:controller="com.android.settings.enterprise.FinancedPrivacyPreferenceController"/>
<Preference
android:key="device_lock_info"
android:title="@string/device_lock_info"
android:summary="@string/summary_placeholder"
settings:controller="com.android.settings.devicelock.DeviceLockPreferenceController">
<intent android:action="com.android.devicelockcontroller.action.DEVICE_INFO_SETTINGS"/>
</Preference>
</PreferenceCategory>
<Preference

View File

@@ -128,6 +128,8 @@
android:title="@string/long_background_tasks_title"
android:order="-800"
android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
settings:isPreferenceVisible="false"
settings:searchable="false"
settings:keywords="@string/keywords_long_background_tasks"
settings:controller="com.android.settings.applications.specialaccess.applications.LongBackgroundTaskController">
<extra

View File

@@ -16,10 +16,6 @@
package com.android.settings.applications;
import static android.Manifest.permission.RUN_USER_INITIATED_JOBS;
import static android.app.AppOpsManager.OP_RUN_USER_INITIATED_JOBS;
import static android.app.AppOpsManager.opToPermission;
import android.Manifest;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
@@ -345,7 +341,9 @@ public class ApplicationFeatureProviderImpl implements ApplicationFeatureProvide
@Override
public boolean isLongBackgroundTaskPermissionToggleSupported() {
return TextUtils.equals(RUN_USER_INITIATED_JOBS,
opToPermission(OP_RUN_USER_INITIATED_JOBS));
// Since the RUN_USER_INITIATED_JOBS permission related to this controller is a normal
// app-op permission allowed by default, this should always return false - if it is ever
// converted to a special app-op permission, this should be updated.
return false;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 The Android Open Source Project
* 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.
@@ -37,6 +37,6 @@ public class LongBackgroundTaskController extends BasePreferenceController {
@Override
public int getAvailabilityStatus() {
return mAppFeatureProvider.isLongBackgroundTaskPermissionToggleSupported()
? AVAILABLE : UNSUPPORTED_ON_DEVICE;
? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
}

View File

@@ -69,6 +69,7 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
private static final String ENABLE_DUAL_MODE_AUDIO =
"persist.bluetooth.enable_dual_mode_audio";
private static final String CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT = "le_audio_enabled_by_default";
private LocalBluetoothManager mManager;
private LocalBluetoothProfileManager mProfileManager;
@@ -99,7 +100,9 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
mIsLeContactSharingEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_LE_AUDIO_CONTACT_SHARING_ENABLED, true);
mIsLeAudioToggleEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED, false);
SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED, false)
|| DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH,
CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT, false);
// Call refresh here even though it will get called later in onResume, to avoid the
// list of switches appearing to "pop" into the page.
refresh();

View File

@@ -16,12 +16,17 @@
package com.android.settings.connecteddevice.stylus;
import android.app.Dialog;
import android.app.role.RoleManager;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.text.TextUtils;
@@ -38,6 +43,8 @@ import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
import com.android.settings.R;
import com.android.settings.dashboard.profileselector.ProfileSelectDialog;
import com.android.settings.dashboard.profileselector.UserAdapter;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -45,6 +52,7 @@ import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnResume;
import java.util.ArrayList;
import java.util.List;
/**
@@ -73,6 +81,9 @@ public class StylusDevicesController extends AbstractPreferenceController implem
@VisibleForTesting
PreferenceCategory mPreferencesContainer;
@VisibleForTesting
Dialog mDialog;
public StylusDevicesController(Context context, InputDevice inputDevice,
CachedBluetoothDevice cachedBluetoothDevice, Lifecycle lifecycle) {
super(context);
@@ -100,8 +111,8 @@ public class StylusDevicesController extends AbstractPreferenceController implem
pref.setOnPreferenceClickListener(this);
pref.setEnabled(true);
List<String> roleHolders = rm.getRoleHoldersAsUser(RoleManager.ROLE_NOTES,
mContext.getUser());
UserHandle user = getDefaultNoteTaskProfile();
List<String> roleHolders = rm.getRoleHoldersAsUser(RoleManager.ROLE_NOTES, user);
if (roleHolders.isEmpty()) {
pref.setSummary(R.string.default_app_none);
return pref;
@@ -117,7 +128,13 @@ public class StylusDevicesController extends AbstractPreferenceController implem
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Notes role package not found.");
}
pref.setSummary(appName);
if (mContext.getSystemService(UserManager.class).isManagedProfile(user.getIdentifier())) {
pref.setSummary(
mContext.getString(R.string.stylus_default_notes_summary_work, appName));
} else {
pref.setSummary(appName);
}
return pref;
}
@@ -155,7 +172,13 @@ public class StylusDevicesController extends AbstractPreferenceController implem
String packageName = pm.getPermissionControllerPackageName();
Intent intent = new Intent(Intent.ACTION_MANAGE_DEFAULT_APP).setPackage(
packageName).putExtra(Intent.EXTRA_ROLE_NAME, RoleManager.ROLE_NOTES);
mContext.startActivity(intent);
List<UserHandle> users = getUserAndManagedProfiles();
if (users.size() <= 1) {
mContext.startActivity(intent);
} else {
createAndShowProfileSelectDialog(intent, users);
}
break;
case KEY_HANDWRITING:
Settings.Secure.putInt(mContext.getContentResolver(),
@@ -229,6 +252,55 @@ public class StylusDevicesController extends AbstractPreferenceController implem
return inputMethod != null && inputMethod.supportsStylusHandwriting();
}
private List<UserHandle> getUserAndManagedProfiles() {
UserManager um = mContext.getSystemService(UserManager.class);
final ArrayList<UserHandle> userManagedProfiles = new ArrayList<>();
// Add the current user, then add all the associated managed profiles.
final UserHandle currentUser = Process.myUserHandle();
userManagedProfiles.add(currentUser);
final List<UserInfo> userInfos = um.getUsers();
for (UserInfo info : userInfos) {
if (um.isManagedProfile(info.id)
&& um.getProfileParent(info.id).id == currentUser.getIdentifier()) {
userManagedProfiles.add(UserHandle.of(info.id));
}
}
return userManagedProfiles;
}
private UserHandle getDefaultNoteTaskProfile() {
final int userId = Secure.getInt(
mContext.getContentResolver(),
Secure.DEFAULT_NOTE_TASK_PROFILE,
UserHandle.myUserId());
return UserHandle.of(userId);
}
@VisibleForTesting
UserAdapter.OnClickListener createProfileDialogClickCallback(
Intent intent, List<UserHandle> users) {
// TODO(b/281659827): improve UX flow for when activity is cancelled
return (int position) -> {
intent.putExtra(Intent.EXTRA_USER, users.get(position));
Secure.putInt(mContext.getContentResolver(),
Secure.DEFAULT_NOTE_TASK_PROFILE,
users.get(position).getIdentifier());
mContext.startActivity(intent);
mDialog.dismiss();
};
}
private void createAndShowProfileSelectDialog(Intent intent, List<UserHandle> users) {
mDialog = ProfileSelectDialog.createDialog(
mContext,
users,
createProfileDialogClickCallback(intent, users));
mDialog.show();
}
/**
* Identifies whether a device is a stylus using the associated {@link InputDevice} or
* {@link CachedBluetoothDevice}.

View File

@@ -39,6 +39,7 @@ public class BluetoothLeAudioDeviceDetailsPreferenceController
implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
private static final String PREFERENCE_KEY = "bluetooth_show_leaudio_device_details";
private static final String CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT = "le_audio_enabled_by_default";
static int sLeAudioSupportedStateCache = BluetoothStatusCodes.ERROR_UNKNOWN;
@VisibleForTesting
@@ -87,8 +88,12 @@ public class BluetoothLeAudioDeviceDetailsPreferenceController
final boolean leAudioDeviceDetailEnabled = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED, false);
final boolean leAudioEnabledByDefault = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_BLUETOOTH, CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT, false);
((SwitchPreference) mPreference).setChecked(leAudioDeviceDetailEnabled);
mPreference.setEnabled(!leAudioEnabledByDefault);
((SwitchPreference) mPreference).setChecked(leAudioDeviceDetailEnabled
|| leAudioEnabledByDefault);
}
@Override

View File

@@ -28,11 +28,9 @@ import androidx.fragment.app.FragmentManager;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
/**
* Dialog fragment for reboot confirmation when enabling certain features.
*/
/** Dialog fragment for reboot confirmation when enabling certain features. */
public class RebootConfirmationDialogFragment extends InstrumentedDialogFragment
implements DialogInterface.OnClickListener {
implements DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
private static final String TAG = "FreeformPrefRebootDlg";
@@ -40,18 +38,17 @@ public class RebootConfirmationDialogFragment extends InstrumentedDialogFragment
private final int mCancelButtonId;
private final RebootConfirmationDialogHost mHost;
/**
* Show an instance of this dialog.
*/
/** Show an instance of this dialog. */
public static void show(Fragment fragment, int messageId, RebootConfirmationDialogHost host) {
show(fragment, messageId, R.string.reboot_dialog_reboot_later, host);
}
/**
* Show an instance of this dialog with cancel button string set as cancelButtonId
*/
public static void show(Fragment fragment, int messageId,
int cancelButtonId, RebootConfirmationDialogHost host) {
/** Show an instance of this dialog with cancel button string set as cancelButtonId */
public static void show(
Fragment fragment,
int messageId,
int cancelButtonId,
RebootConfirmationDialogHost host) {
final FragmentManager manager = fragment.getActivity().getSupportFragmentManager();
if (manager.findFragmentByTag(TAG) == null) {
final RebootConfirmationDialogFragment dialog =
@@ -60,8 +57,8 @@ public class RebootConfirmationDialogFragment extends InstrumentedDialogFragment
}
}
private RebootConfirmationDialogFragment(int messageId,
int cancelButtonId, RebootConfirmationDialogHost host) {
private RebootConfirmationDialogFragment(
int messageId, int cancelButtonId, RebootConfirmationDialogHost host) {
mMessageId = messageId;
mCancelButtonId = cancelButtonId;
mHost = host;
@@ -89,4 +86,10 @@ public class RebootConfirmationDialogFragment extends InstrumentedDialogFragment
mHost.onRebootCancelled();
}
}
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
mHost.onRebootDialogDismissed();
}
}

View File

@@ -20,22 +20,20 @@ import android.content.Context;
import android.content.Intent;
/**
* Host of {@link RebootConfirmationDialogFragment} that provides callback when user
* interacts with the UI.
* Host of {@link RebootConfirmationDialogFragment} that provides callback when user interacts with
* the UI.
*/
public interface RebootConfirmationDialogHost {
/**
* Called when user made a decision to reboot the device.
*/
/** Called when user made a decision to reboot the device. */
default void onRebootConfirmed(Context context) {
// user presses button "Reboot now", reboot the device
final Intent intent = new Intent(Intent.ACTION_REBOOT);
context.startActivity(intent);
}
/**
* Called when user made a decision to cancel the reboot
* Default to do nothing
*/
/** Called when user made a decision to cancel the reboot Default to do nothing */
default void onRebootCancelled() {}
/** Called when reboot dialog is dismissed Default to do nothing */
default void onRebootDialogDismissed() {}
}

View File

@@ -17,6 +17,7 @@
package com.android.settings.development.graphicsdriver;
import android.content.Context;
import android.content.Intent;
import android.os.GraphicsEnvironment;
import android.os.SystemProperties;
import android.text.TextUtils;
@@ -33,9 +34,7 @@ import com.android.settings.development.RebootConfirmationDialogFragment;
import com.android.settings.development.RebootConfirmationDialogHost;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;
/**
* Controller to handle the events when user toggles this developer option switch: Enable ANGLE
*/
/** Controller to handle the events when user toggles this developer option switch: Enable ANGLE */
public class GraphicsDriverEnableAngleAsSystemDriverController
extends DeveloperOptionsPreferenceController
implements Preference.OnPreferenceChangeListener,
@@ -50,14 +49,15 @@ public class GraphicsDriverEnableAngleAsSystemDriverController
private final GraphicsDriverSystemPropertiesWrapper mSystemProperties;
private boolean mShouldToggleSwitchBackOnRebootDialogDismiss;
@VisibleForTesting
static final String PROPERTY_RO_GFX_ANGLE_SUPPORTED = "ro.gfx.angle.supported";
@VisibleForTesting
static final String PROPERTY_PERSISTENT_GRAPHICS_EGL = "persist.graphics.egl";
@VisibleForTesting
static final String ANGLE_DRIVER_SUFFIX = "angle";
@VisibleForTesting static final String ANGLE_DRIVER_SUFFIX = "angle";
@VisibleForTesting
static class Injector {
@@ -87,6 +87,10 @@ public class GraphicsDriverEnableAngleAsSystemDriverController
super(context);
mFragment = fragment;
mSystemProperties = injector.createSystemPropertiesWrapper();
// By default, when the reboot dialog is dismissed we want to toggle the switch back.
// Exception is when user chooses to reboot now, the switch should keep its current value
// and persist its' state over reboot.
mShouldToggleSwitchBackOnRebootDialogDismiss = true;
}
@Override
@@ -108,11 +112,12 @@ public class GraphicsDriverEnableAngleAsSystemDriverController
@VisibleForTesting
void showRebootDialog() {
RebootConfirmationDialogFragment.show(
mFragment, R.string.reboot_dialog_enable_angle_as_system_driver,
R.string.cancel, this);
mFragment,
R.string.reboot_dialog_enable_angle_as_system_driver,
R.string.cancel,
this);
}
@Override
public void updateState(Preference preference) {
// set switch on if "persist.graphics.egl" is "angle" and angle is built in /vendor
@@ -120,8 +125,9 @@ public class GraphicsDriverEnableAngleAsSystemDriverController
final String currentGlesDriver =
mSystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL, "");
final boolean isAngle = TextUtils.equals(ANGLE_DRIVER_SUFFIX, currentGlesDriver);
final boolean isAngleSupported = TextUtils
.equals(mSystemProperties.get(PROPERTY_RO_GFX_ANGLE_SUPPORTED, ""), "true");
final boolean isAngleSupported =
TextUtils.equals(
mSystemProperties.get(PROPERTY_RO_GFX_ANGLE_SUPPORTED, ""), "true");
((SwitchPreference) mPreference).setChecked(isAngle && isAngleSupported);
((SwitchPreference) mPreference).setEnabled(isAngleSupported);
}
@@ -130,8 +136,9 @@ public class GraphicsDriverEnableAngleAsSystemDriverController
protected void onDeveloperOptionsSwitchEnabled() {
// only enable the switch if ro.gfx.angle.supported is true
// we use ro.gfx.angle.supported to indicate if ANGLE libs are installed under /vendor
final boolean isAngleSupported = TextUtils
.equals(mSystemProperties.get(PROPERTY_RO_GFX_ANGLE_SUPPORTED, ""), "true");
final boolean isAngleSupported =
TextUtils.equals(
mSystemProperties.get(PROPERTY_RO_GFX_ANGLE_SUPPORTED, ""), "true");
((SwitchPreference) mPreference).setEnabled(isAngleSupported);
}
@@ -145,9 +152,7 @@ public class GraphicsDriverEnableAngleAsSystemDriverController
((SwitchPreference) mPreference).setEnabled(false);
}
@Override
public void onRebootCancelled() {
// if user presses button "Cancel", do not reboot the device, and toggles switch back
void toggleSwitchBack() {
final String currentGlesDriver =
mSystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL, "");
if (TextUtils.equals(ANGLE_DRIVER_SUFFIX, currentGlesDriver)) {
@@ -169,4 +174,40 @@ public class GraphicsDriverEnableAngleAsSystemDriverController
// if persist.graphics.egl holds values other than the above two, log error message
Log.e(TAG, "Invalid persist.graphics.egl property value");
}
@VisibleForTesting
void rebootDevice(Context context) {
final Intent intent = new Intent(Intent.ACTION_REBOOT);
context.startActivity(intent);
}
@Override
public void onRebootConfirmed(Context context) {
// User chooses to reboot now, do not toggle switch back
mShouldToggleSwitchBackOnRebootDialogDismiss = false;
// Reboot the device
rebootDevice(context);
}
@Override
public void onRebootCancelled() {
// User chooses to cancel reboot, toggle switch back
mShouldToggleSwitchBackOnRebootDialogDismiss = true;
}
@Override
public void onRebootDialogDismissed() {
// If reboot dialog is dismissed either from
// 1) User clicks cancel
// 2) User taps phone screen area outside of reboot dialog
// do not reboot the device, and toggles switch back.
if (mShouldToggleSwitchBackOnRebootDialogDismiss) {
toggleSwitchBack();
}
// Reset the flag so that the default option is to toggle switch back
// on reboot dialog dismissed.
mShouldToggleSwitchBackOnRebootDialogDismiss = true;
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.devicelock;
import android.content.Context;
import android.devicelock.DeviceLockManager;
import android.util.Log;
import androidx.preference.Preference;
import com.android.settings.core.BasePreferenceController;
/**
* The PreferenceController that manages the Device Lock entry preference.
*/
public final class DeviceLockPreferenceController extends BasePreferenceController {
private static final String TAG = "DeviceLockPreferenceController";
private final DeviceLockManager mDeviceLockManager;
public DeviceLockPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
mDeviceLockManager = mContext.getSystemService(DeviceLockManager.class);
}
@Override
public int getAvailabilityStatus() {
// TODO(b/282856378): make this entry searchable
return AVAILABLE_UNSEARCHABLE;
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
mDeviceLockManager.getKioskApps(mContext.getMainExecutor(),
result -> {
// if kiosk apps present on the device, the device is provisioned by Device Lock
boolean isDeviceProvisionedByDeviceLock = result != null && !result.isEmpty();
Log.d(TAG, "Set preference visibility to " + isDeviceProvisionedByDeviceLock);
// TODO(b/282179089): find alternatives instead of calling setVisible
preference.setVisible(isDeviceProvisionedByDeviceLock);
});
}
}

View File

@@ -18,7 +18,9 @@ package com.android.settings.fuelgauge.batterytip;
import android.annotation.Nullable;
import android.content.Context;
import android.os.BadParcelableException;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
@@ -142,14 +144,26 @@ public class BatteryTipPreferenceController extends BasePreferenceController {
}
public void restoreInstanceState(Bundle bundle) {
if (bundle != null) {
if (bundle == null) {
return;
}
try {
List<BatteryTip> batteryTips = bundle.getParcelableArrayList(KEY_BATTERY_TIPS);
updateBatteryTips(batteryTips);
} catch (BadParcelableException e) {
Log.e(TAG, "failed to invoke restoreInstanceState()", e);
}
}
public void saveInstanceState(Bundle outState) {
outState.putParcelableList(KEY_BATTERY_TIPS, mBatteryTips);
public void saveInstanceState(Bundle bundle) {
if (bundle == null) {
return;
}
try {
bundle.putParcelableList(KEY_BATTERY_TIPS, mBatteryTips);
} catch (BadParcelableException e) {
Log.e(TAG, "failed to invoke saveInstanceState()", e);
}
}
public boolean needUpdate() {

View File

@@ -79,10 +79,13 @@ public class SpinnerPreference extends Preference {
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state == null || !state.getClass().equals(SavedState.class)) {
if (state == null || state == BaseSavedState.EMPTY_STATE) {
super.onRestoreInstanceState(state);
return;
}
if (!(state instanceof SavedState)) {
return;
}
SavedState savedState = (SavedState) state;
super.onRestoreInstanceState(savedState.getSuperState());
mSavedSpinnerPosition = savedState.getSpinnerPosition();

View File

@@ -74,6 +74,7 @@ public class AppNotificationSettings extends NotificationSettings {
mControllers = new ArrayList<>();
mControllers.add(new HeaderPreferenceController(context, this));
mControllers.add(new BlockPreferenceController(context, mDependentFieldListener, mBackend));
mControllers.add(new FullScreenIntentPermissionPreferenceController(context, mBackend));
mControllers.add(new BadgePreferenceController(context, mBackend));
mControllers.add(new AllowSoundPreferenceController(
context, mDependentFieldListener, mBackend));

View File

@@ -0,0 +1,118 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.notification.app
import android.Manifest.permission.USE_FULL_SCREEN_INTENT
import android.app.AppOpsManager
import android.app.AppOpsManager.OP_USE_FULL_SCREEN_INTENT
import android.content.AttributionSource
import android.content.Context
import android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET
import android.content.pm.PackageManager.GET_PERMISSIONS
import android.os.UserHandle
import android.permission.PermissionManager
import android.util.Log
import androidx.preference.Preference
import androidx.preference.Preference.OnPreferenceChangeListener
import com.android.settings.notification.NotificationBackend
import com.android.settingslib.RestrictedSwitchPreference
class FullScreenIntentPermissionPreferenceController(
context: Context,
backend: NotificationBackend
) : NotificationPreferenceController(context, backend), OnPreferenceChangeListener {
private val packageManager = mPm!!
private val permissionManager = context.getSystemService(PermissionManager::class.java)!!
private val appOpsManager = context.getSystemService(AppOpsManager::class.java)!!
private val packageName get() = mAppRow.pkg
private val uid get() = mAppRow.uid
private val userHandle get() = UserHandle.getUserHandleForUid(uid)
override fun getPreferenceKey() = KEY_FSI_PERMISSION
override fun isAvailable(): Boolean {
val inAppWidePreferences = mChannelGroup == null && mChannel == null
if (!inAppWidePreferences) {
Log.wtf(TAG, "Belongs only in app-wide notification preferences!")
}
return super.isAvailable() && inAppWidePreferences && isPermissionRequested()
}
override fun isIncludedInFilter() = false
override fun updateState(preference: Preference) {
check(KEY_FSI_PERMISSION.equals(preference.key))
check(preference is RestrictedSwitchPreference)
preference.setDisabledByAdmin(mAdmin)
preference.isEnabled = !preference.isDisabledByAdmin
preference.isChecked = isPermissionGranted()
}
override fun onPreferenceChange(preference: Preference, value: Any): Boolean {
check(KEY_FSI_PERMISSION.equals(preference.key))
check(preference is RestrictedSwitchPreference)
check(value is Boolean)
if (isPermissionGranted() != value) {
setPermissionGranted(value)
}
return true
}
private fun isPermissionRequested(): Boolean {
val packageInfo = packageManager.getPackageInfo(packageName, GET_PERMISSIONS)
for (requestedPermission in packageInfo.requestedPermissions) {
if (USE_FULL_SCREEN_INTENT.equals(requestedPermission)) {
return true
}
}
return false
}
private fun isPermissionGranted(): Boolean {
val attributionSource = AttributionSource.Builder(uid).setPackageName(packageName).build()
val permissionResult =
permissionManager.checkPermissionForPreflight(USE_FULL_SCREEN_INTENT, attributionSource)
return (permissionResult == PermissionManager.PERMISSION_GRANTED)
}
private fun setPermissionGranted(allowed: Boolean) {
val mode = if (allowed) AppOpsManager.MODE_ALLOWED else AppOpsManager.MODE_ERRORED
appOpsManager.setUidMode(OP_USE_FULL_SCREEN_INTENT, uid, mode)
packageManager.updatePermissionFlags(
USE_FULL_SCREEN_INTENT,
packageName,
FLAG_PERMISSION_USER_SET,
FLAG_PERMISSION_USER_SET,
userHandle
)
}
companion object {
const val KEY_FSI_PERMISSION = "fsi_permission"
const val TAG = "FsiPermPrefController"
}
}

View File

@@ -183,7 +183,8 @@ public class NumberingSystemItemController extends BasePreferenceController {
Bundle bundle = new Bundle();
bundle.putString(RegionalPreferencesEntriesFragment.ARG_KEY_REGIONAL_PREFERENCE,
ARG_VALUE_NUMBERING_SYSTEM_SELECT);
bundle.putString(KEY_SELECTED_LANGUAGE, updatedLocale.toLanguageTag());
bundle.putString(KEY_SELECTED_LANGUAGE,
updatedLocale != null ? updatedLocale.toLanguageTag() : "");
mParentFragment.setArguments(bundle);
continue;
}
@@ -194,13 +195,14 @@ public class NumberingSystemItemController extends BasePreferenceController {
private Locale saveNumberingSystemToLocale(Locale targetLocale, String value) {
LocaleList localeList = LocalePicker.getLocales();
Locale[] locales = new Locale[localeList.size()];
Locale updatedLocale = null;
for (int i = 0; i < localeList.size(); i++) {
Locale locale = localeList.get(i);
if (targetLocale.equals(locale)) {
if (value.equals(RegionalPreferencesDataUtils.DEFAULT_VALUE)) {
if (RegionalPreferencesDataUtils.DEFAULT_VALUE.equals(value)) {
value = null;
}
Locale updatedLocale = new Locale.Builder()
updatedLocale = new Locale.Builder()
.setLocale(locale)
.setUnicodeLocaleKeyword(ExtensionTypes.NUMBERING_SYSTEM, value)
.build();
@@ -210,7 +212,7 @@ public class NumberingSystemItemController extends BasePreferenceController {
locales[i] = localeList.get(i);
}
LocalePicker.updateLocales(new LocaleList(locales));
return targetLocale;
return updatedLocale;
}
private static String getNumberingSystem(Locale locale) {

View File

@@ -18,6 +18,10 @@ package com.android.settings.connecteddevice.stylus;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doNothing;
@@ -27,13 +31,17 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Dialog;
import android.app.role.RoleManager;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.view.InputDevice;
@@ -48,6 +56,7 @@ import androidx.preference.SwitchPreference;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.dashboard.profileselector.UserAdapter;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.core.lifecycle.Lifecycle;
@@ -59,7 +68,9 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class StylusDevicesControllerTest {
@@ -79,6 +90,8 @@ public class StylusDevicesControllerTest {
@Mock
private PackageManager mPm;
@Mock
private UserManager mUserManager;
@Mock
private RoleManager mRm;
@Mock
private Lifecycle mLifecycle;
@@ -87,7 +100,6 @@ public class StylusDevicesControllerTest {
@Mock
private BluetoothDevice mBluetoothDevice;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -101,6 +113,7 @@ public class StylusDevicesControllerTest {
when(mContext.getSystemService(InputMethodManager.class)).thenReturn(mImm);
when(mContext.getSystemService(RoleManager.class)).thenReturn(mRm);
when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
doNothing().when(mContext).startActivity(any());
when(mImm.getCurrentInputMethodInfo()).thenReturn(mInputMethodInfo);
@@ -115,6 +128,8 @@ public class StylusDevicesControllerTest {
when(mPm.getApplicationInfo(eq(NOTES_PACKAGE_NAME),
any(PackageManager.ApplicationInfoFlags.class))).thenReturn(new ApplicationInfo());
when(mPm.getApplicationLabel(any(ApplicationInfo.class))).thenReturn(NOTES_APP_LABEL);
when(mUserManager.getUsers()).thenReturn(Arrays.asList(new UserInfo(0, "default", 0)));
when(mUserManager.isManagedProfile(anyInt())).thenReturn(false);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
@@ -228,21 +243,35 @@ public class StylusDevicesControllerTest {
when(mInputMethodInfo.supportsStylusHandwriting()).thenReturn(false);
showScreen(mController);
Preference handwritingPref = mPreferenceContainer.getPreference(1);
Preference handwritingPref = mPreferenceContainer.getPreference(1);
assertThat(handwritingPref.isVisible()).isFalse();
}
@Test
public void defaultNotesPreference_showsNotesRoleApp() {
public void defaultNotesPreference_singleUser_showsNotesRoleApp() {
showScreen(mController);
Preference defaultNotesPref = mPreferenceContainer.getPreference(0);
Preference defaultNotesPref = mPreferenceContainer.getPreference(0);
assertThat(defaultNotesPref.getTitle().toString()).isEqualTo(
mContext.getString(R.string.stylus_default_notes_app));
assertThat(defaultNotesPref.getSummary().toString()).isEqualTo(NOTES_APP_LABEL.toString());
}
@Test
public void defaultNotesPreference_workProfileUser_showsWorkNotesRoleApp() {
when(mUserManager.isManagedProfile(0)).thenReturn(true);
showScreen(mController);
Preference defaultNotesPref = mPreferenceContainer.getPreference(0);
assertThat(defaultNotesPref.getTitle().toString()).isEqualTo(
mContext.getString(R.string.stylus_default_notes_app));
assertThat(defaultNotesPref.getSummary().toString()).isEqualTo(
mContext.getString(R.string.stylus_default_notes_summary_work,
NOTES_APP_LABEL.toString()));
}
@Test
public void defaultNotesPreference_roleHolderChanges_updatesPreference() {
showScreen(mController);
@@ -267,7 +296,7 @@ public class StylusDevicesControllerTest {
}
@Test
public void defaultNotesPreferenceClick_sendsManageDefaultRoleIntent() {
public void defaultNotesPreferenceClick_singleUser_sendsManageDefaultRoleIntent() {
final String permissionPackageName = "permissions.package";
when(mPm.getPermissionControllerPackageName()).thenReturn(permissionPackageName);
final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
@@ -282,6 +311,46 @@ public class StylusDevicesControllerTest {
assertThat(intent.getPackage()).isEqualTo(permissionPackageName);
assertThat(intent.getStringExtra(Intent.EXTRA_ROLE_NAME)).isEqualTo(
RoleManager.ROLE_NOTES);
assertNull(mController.mDialog);
}
@Test
public void defaultNotesPreferenceClick_multiUser_showsProfileSelectorDialog() {
mContext.setTheme(R.style.Theme_AppCompat);
final String permissionPackageName = "permissions.package";
final UserHandle currentUser = Process.myUserHandle();
List<UserInfo> userInfos = Arrays.asList(
new UserInfo(currentUser.getIdentifier(), "current", 0),
new UserInfo(1, "profile", UserInfo.FLAG_PROFILE)
);
when(mUserManager.getUsers()).thenReturn(userInfos);
when(mUserManager.isManagedProfile(1)).thenReturn(true);
when(mUserManager.getUserInfo(currentUser.getIdentifier())).thenReturn(userInfos.get(0));
when(mUserManager.getUserInfo(1)).thenReturn(userInfos.get(1));
when(mUserManager.getProfileParent(1)).thenReturn(userInfos.get(0));
when(mPm.getPermissionControllerPackageName()).thenReturn(permissionPackageName);
showScreen(mController);
Preference defaultNotesPref = mPreferenceContainer.getPreference(0);
mController.onPreferenceClick(defaultNotesPref);
assertTrue(mController.mDialog.isShowing());
}
@Test
public void profileSelectDialogClickCallback_onClick_sendsIntent() {
Intent intent = new Intent();
UserHandle user1 = mock(UserHandle.class);
UserHandle user2 = mock(UserHandle.class);
List<UserHandle> users = Arrays.asList(new UserHandle[] {user1, user2});
mController.mDialog = mock(Dialog.class);
UserAdapter.OnClickListener callback = mController
.createProfileDialogClickCallback(intent, users);
callback.onClick(1);
assertEquals(intent.getExtra(Intent.EXTRA_USER), user2);
verify(mContext).startActivity(intent);
}
@Test

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.devicelock;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.devicelock.DeviceLockManager;
import android.os.OutcomeReceiver;
import androidx.preference.Preference;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Before;
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.robolectric.RobolectricTestRunner;
import java.util.Collections;
import java.util.Map;
@RunWith(RobolectricTestRunner.class)
public final class DeviceLockPreferenceControllerTest {
private static final String TEST_PREFERENCE_KEY = "KEY";
private static final Map<Integer, String> TEST_KIOSK_APPS = Map.of(0, "test");
@Mock
private DeviceLockManager mDeviceLockManager;
@Captor
private ArgumentCaptor<OutcomeReceiver<Map<Integer, String>, Exception>>
mOutcomeReceiverArgumentCaptor;
private DeviceLockPreferenceController mController;
private Context mContext;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = ApplicationProvider.getApplicationContext();
Context context = spy(mContext);
when(context.getSystemService(DeviceLockManager.class)).thenReturn(mDeviceLockManager);
mController = new DeviceLockPreferenceController(context, TEST_PREFERENCE_KEY);
}
@Test
public void testUpdateState_preferenceBecomesInvisibleIfNoKioskAppsPresent() {
Preference preference = new Preference(mContext, null, 0, 0);
preference.setVisible(true);
mController.updateState(preference);
verify(mDeviceLockManager).getKioskApps(any(), mOutcomeReceiverArgumentCaptor.capture());
OutcomeReceiver<Map<Integer, String>, Exception> outcomeReceiver =
mOutcomeReceiverArgumentCaptor.getValue();
outcomeReceiver.onResult(Collections.emptyMap());
assertThat(preference.isVisible()).isFalse();
}
@Test
public void testUpdateState_preferenceBecomesVisibleIfKioskAppsPresent() {
Preference preference = new Preference(mContext, null, 0, 0);
preference.setVisible(false);
mController.updateState(preference);
verify(mDeviceLockManager).getKioskApps(any(), mOutcomeReceiverArgumentCaptor.capture());
OutcomeReceiver<Map<Integer, String>, Exception> outcomeReceiver =
mOutcomeReceiverArgumentCaptor.getValue();
outcomeReceiver.onResult(TEST_KIOSK_APPS);
assertThat(preference.isVisible()).isTrue();
}
}

View File

@@ -0,0 +1,299 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.notification.app
import android.Manifest.permission.USE_FULL_SCREEN_INTENT
import android.app.AppOpsManager
import android.app.AppOpsManager.MODE_ALLOWED
import android.app.AppOpsManager.MODE_ERRORED
import android.app.AppOpsManager.OP_USE_FULL_SCREEN_INTENT
import android.app.NotificationChannel
import android.app.NotificationChannelGroup
import android.app.NotificationManager.IMPORTANCE_UNSPECIFIED
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET
import android.content.pm.PackageManager.GET_PERMISSIONS
import android.os.UserHandle
import android.permission.PermissionManager.PERMISSION_GRANTED
import android.permission.PermissionManager.PERMISSION_HARD_DENIED
import android.permission.PermissionManager.PERMISSION_SOFT_DENIED
import android.permission.PermissionManager.PermissionResult
import androidx.preference.Preference
import androidx.preference.PreferenceScreen
import androidx.test.core.app.ApplicationProvider
import com.android.settings.notification.NotificationBackend
import com.android.settings.notification.NotificationBackend.AppRow
import com.android.settings.notification.app.FullScreenIntentPermissionPreferenceController.Companion.KEY_FSI_PERMISSION
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
import com.android.settingslib.RestrictedSwitchPreference
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Answers
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.robolectric.shadows.ShadowApplicationPackageManager
import org.robolectric.shadows.ShadowPermissionChecker
import org.mockito.Mockito.`when` as whenever
@RunWith(RobolectricTestRunner::class)
@Config(shadows = [ShadowApplicationPackageManager::class])
class FullScreenIntentPermissionPreferenceControllerTest {
@JvmField
@Rule
val mockitoRule = MockitoJUnit.rule()!!
@Mock
private lateinit var packageManager: PackageManager
@Mock
private lateinit var appOpsManager: AppOpsManager
private lateinit var preference: RestrictedSwitchPreference
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private lateinit var screen: PreferenceScreen
private lateinit var controller: FullScreenIntentPermissionPreferenceController
@Before
fun setUp() {
val context = spy(ApplicationProvider.getApplicationContext<Context>())
whenever(context.packageManager).thenReturn(packageManager)
whenever(context.getSystemService(AppOpsManager::class.java)).thenReturn(appOpsManager)
preference = RestrictedSwitchPreference(context).apply { key = KEY_FSI_PERMISSION }
whenever(screen.findPreference<Preference>(KEY_FSI_PERMISSION)).thenReturn(preference)
controller = FullScreenIntentPermissionPreferenceController(
context,
mock(NotificationBackend::class.java)
)
}
@Test
fun testIsAvailable_notWhenPermissionNotRequested() {
setPermissionRequestedInManifest(requested = false)
initController()
assertFalse(controller.isAvailable)
}
@Test
fun testIsAvailable_notWhenOnChannelScreen() {
setPermissionRequestedInManifest()
initController(channel = makeTestChannel())
assertFalse(controller.isAvailable)
}
@Test
fun testIsAvailable_notWhenOnGroupScreen() {
setPermissionRequestedInManifest()
initController(group = makeTestGroup())
assertFalse(controller.isAvailable)
}
@Test
fun testIsAvailable_onAppScreenWhenRequested() {
setPermissionRequestedInManifest()
initController()
assertTrue(controller.isAvailable)
}
@Test
fun testIsEnabled_notWhenDisabledByAdmin() {
setPermissionRequestedInManifest()
initController(admin = makeTestAdmin())
controller.updateState(preference)
assertFalse(preference.isEnabled)
}
@Test
fun testIsEnabled_whenNotDisabledByAdmin() {
setPermissionRequestedInManifest()
initController()
controller.updateState(preference)
assertTrue(preference.isEnabled)
}
@Test
fun testIsChecked_notWhenHardDenied() {
setPermissionRequestedInManifest()
setPermissionResult(PERMISSION_HARD_DENIED)
initController()
controller.updateState(preference)
assertFalse(preference.isChecked)
}
@Test
fun testIsChecked_notWhenSoftDenied() {
setPermissionRequestedInManifest()
setPermissionResult(PERMISSION_SOFT_DENIED)
initController()
controller.updateState(preference)
assertFalse(preference.isChecked)
}
@Test
fun testIsChecked_whenGranted() {
setPermissionRequestedInManifest()
setPermissionResult(PERMISSION_GRANTED)
initController()
controller.updateState(preference)
assertTrue(preference.isChecked)
}
@Test
fun testOnPreferenceChange_whenHardDenied() {
setPermissionRequestedInManifest()
setPermissionResult(PERMISSION_HARD_DENIED)
initController()
controller.displayPreference(screen)
controller.updateState(preference)
assertFalse(preference.isChecked)
setPreferenceChecked(true)
verifySetAppOpMode(MODE_ALLOWED)
verifySetPermissionUserSetFlag()
}
@Test
fun testOnPreferenceChange_whenSoftDenied() {
setPermissionRequestedInManifest()
setPermissionResult(PERMISSION_SOFT_DENIED)
initController()
controller.displayPreference(screen)
controller.updateState(preference)
assertFalse(preference.isChecked)
setPreferenceChecked(true)
verifySetAppOpMode(MODE_ALLOWED)
verifySetPermissionUserSetFlag()
}
@Test
fun testOnPreferenceChange_whenGranted() {
setPermissionRequestedInManifest()
setPermissionResult(PERMISSION_GRANTED)
initController()
controller.displayPreference(screen)
controller.updateState(preference)
assertTrue(preference.isChecked)
setPreferenceChecked(false)
verifySetAppOpMode(MODE_ERRORED)
verifySetPermissionUserSetFlag()
}
private fun setPermissionRequestedInManifest(
requested: Boolean = true
) {
whenever(packageManager.getPackageInfo(TEST_PACKAGE, GET_PERMISSIONS)).thenReturn(
PackageInfo().apply {
packageName = TEST_PACKAGE
applicationInfo = ApplicationInfo().apply { packageName = TEST_PACKAGE }
requestedPermissions = if (requested) arrayOf(USE_FULL_SCREEN_INTENT) else arrayOf()
})
}
private fun setPermissionResult(@PermissionResult result: Int) {
ShadowPermissionChecker.setResult(TEST_PACKAGE, USE_FULL_SCREEN_INTENT, result)
}
private fun setPreferenceChecked(checked: Boolean) {
preference.isChecked = checked
/* This shouldn't be necessary, but for some reason it's not called automatically when
isChecked is changed. */
controller.onPreferenceChange(preference, checked)
}
private fun verifySetAppOpMode(@AppOpsManager.Mode expectedMode: Int) {
verify(appOpsManager).setUidMode(OP_USE_FULL_SCREEN_INTENT, TEST_UID, expectedMode)
}
private fun verifySetPermissionUserSetFlag() {
verify(packageManager).updatePermissionFlags(
USE_FULL_SCREEN_INTENT,
TEST_PACKAGE,
FLAG_PERMISSION_USER_SET,
FLAG_PERMISSION_USER_SET,
makeTestUserHandle()
)
}
private fun initController(
channel: NotificationChannel? = null,
group: NotificationChannelGroup? = null,
admin: EnforcedAdmin? = null
) {
controller.onResume(
makeTestAppRow(),
channel,
group,
/* conversationDrawable = */null,
/* conversationInfo = */null,
admin,
/* preferenceFilter = */null
)
}
private fun makeTestChannel() =
NotificationChannel("test_channel_id", "Test Channel Name", IMPORTANCE_UNSPECIFIED)
private fun makeTestGroup() = NotificationChannelGroup("test_group_id", "Test Group Name")
private fun makeTestAppRow() = AppRow().apply { pkg = TEST_PACKAGE; uid = TEST_UID }
private fun makeTestUserHandle() = UserHandle.getUserHandleForUid(TEST_UID)
private fun makeTestAdmin() = mock(EnforcedAdmin::class.java)
private companion object {
const val TEST_PACKAGE = "test.package.name"
const val TEST_UID = 12345
}
}

View File

@@ -56,11 +56,39 @@ public class GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest {
private GraphicsDriverEnableAngleAsSystemDriverController mController;
@Mock
private DevelopmentSettingsDashboardFragment mFragment;
// Signal to wait for SystemProperty values changed
private class PropertyChangeSignal {
private CountDownLatch mCountDownLatch;
@Mock
private GraphicsDriverSystemPropertiesWrapper mSystemPropertiesMock;
private Runnable mCountDownJob;
PropertyChangeSignal() {
mCountDownLatch = new CountDownLatch(1);
mCountDownJob =
new Runnable() {
@Override
public void run() {
mCountDownLatch.countDown();
}
};
}
public Runnable getCountDownJob() {
return mCountDownJob;
}
public void wait(int timeoutInMilliSeconds) {
try {
mCountDownLatch.await(timeoutInMilliSeconds, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Assert.fail(e.getMessage());
}
}
}
@Mock private DevelopmentSettingsDashboardFragment mFragment;
@Mock private GraphicsDriverSystemPropertiesWrapper mSystemPropertiesMock;
@Before
public void setUp() {
@@ -76,18 +104,27 @@ public class GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest {
// so we can force the SystemProperties with values we need to run tests.
// 2) Override the showRebootDialog() to do nothing.
// We do not need to pop up the reboot dialog in the test.
// 3) Override the rebootDevice() to do nothing.
mController = new GraphicsDriverEnableAngleAsSystemDriverController(
mContext, mFragment, new Injector(){
@Override
public GraphicsDriverSystemPropertiesWrapper createSystemPropertiesWrapper() {
return mSystemPropertiesMock;
}
}) {
@Override
void showRebootDialog() {
// do nothing
}
};
mContext,
mFragment,
new Injector() {
@Override
public GraphicsDriverSystemPropertiesWrapper
createSystemPropertiesWrapper() {
return mSystemPropertiesMock;
}
}) {
@Override
void showRebootDialog() {
// do nothing
}
@Override
void rebootDevice(Context context) {
// do nothing
}
};
final PreferenceManager preferenceManager = new PreferenceManager(mContext);
final PreferenceScreen screen = preferenceManager.createPreferenceScreen(mContext);
@@ -99,58 +136,42 @@ public class GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest {
@Test
public void onPreferenceChange_switchOn_shouldEnableAngleAsSystemDriver() {
// Step 1: toggle the switch "Enable ANGLE" on
// Add a callback when SystemProperty changes.
// This allows the thread to wait until
// GpuService::toggleAngleAsSystemDriver() updates the persist.graphics.egl.
final CountDownLatch countDownLatch = new CountDownLatch(1);
Runnable countDown = new Runnable() {
@Override
public void run() {
countDownLatch.countDown();
}
};
SystemProperties.addChangeCallback(countDown);
// Test onPreferenceChange(true) updates the persist.graphics.egl to "angle"
PropertyChangeSignal propertyChangeSignal = new PropertyChangeSignal();
SystemProperties.addChangeCallback(propertyChangeSignal.getCountDownJob());
mController.onPreferenceChange(mPreference, true);
try {
countDownLatch.await(100, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Assert.fail(e.getMessage());
}
propertyChangeSignal.wait(100);
// Step 2: verify results
final String systemEGLDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL);
assertThat(systemEGLDriver).isEqualTo(ANGLE_DRIVER_SUFFIX);
// Step 3: clean up
// Done with the test, remove the callback
SystemProperties.removeChangeCallback(countDown);
SystemProperties.removeChangeCallback(propertyChangeSignal.getCountDownJob());
}
@Test
public void onPreferenceChange_switchOff_shouldDisableAngleAsSystemDriver() {
// Step 1: toggle the switch "Enable ANGLE" off
// Add a callback when SystemProperty changes.
// This allows the thread to wait until
// GpuService::toggleAngleAsSystemDriver() updates the persist.graphics.egl.
final CountDownLatch countDownLatch = new CountDownLatch(1);
Runnable countDown = new Runnable() {
@Override
public void run() {
countDownLatch.countDown();
}
};
SystemProperties.addChangeCallback(countDown);
// Test onPreferenceChange(false) updates the persist.graphics.egl to ""
PropertyChangeSignal propertyChangeSignal = new PropertyChangeSignal();
SystemProperties.addChangeCallback(propertyChangeSignal.getCountDownJob());
mController.onPreferenceChange(mPreference, false);
try {
countDownLatch.await(100, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Assert.fail(e.getMessage());
}
propertyChangeSignal.wait(100);
// Step 2: verify results
final String systemEGLDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL);
assertThat(systemEGLDriver).isEqualTo("");
// Step 3: clean up
// Done with the test, remove the callback
SystemProperties.removeChangeCallback(countDown);
SystemProperties.removeChangeCallback(propertyChangeSignal.getCountDownJob());
}
@Test
@@ -162,8 +183,7 @@ public class GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest {
@Test
public void updateState_angleNotSupported_PreferenceShouldNotBeChecked() {
when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any()))
.thenReturn("");
when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any())).thenReturn("");
mController.updateState(mPreference);
assertThat(mPreference.isChecked()).isFalse();
}
@@ -191,8 +211,7 @@ public class GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest {
updateState_angleSupported_angleIsNotSystemGLESDriver_PreferenceShouldNotBeChecked() {
when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any()))
.thenReturn("true");
when(mSystemPropertiesMock.get(eq(PROPERTY_PERSISTENT_GRAPHICS_EGL), any()))
.thenReturn("");
when(mSystemPropertiesMock.get(eq(PROPERTY_PERSISTENT_GRAPHICS_EGL), any())).thenReturn("");
mController.updateState(mPreference);
assertThat(mPreference.isChecked()).isFalse();
}
@@ -218,28 +237,18 @@ public class GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest {
// Add a callback when SystemProperty changes.
// This allows the thread to wait until
// GpuService::toggleAngleAsSystemDriver() updates the persist.graphics.egl.
final CountDownLatch countDownLatch = new CountDownLatch(1);
Runnable countDown = new Runnable() {
@Override
public void run() {
countDownLatch.countDown();
}
};
SystemProperties.addChangeCallback(countDown);
PropertyChangeSignal propertyChangeSignal1 = new PropertyChangeSignal();
SystemProperties.addChangeCallback(propertyChangeSignal1.getCountDownJob());
// Test that onDeveloperOptionSwitchDisabled,
// persist.graphics.egl updates to ""
mController.onDeveloperOptionsSwitchDisabled();
try {
countDownLatch.await(100, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Assert.fail(e.getMessage());
}
propertyChangeSignal1.wait(100);
final String systemEGLDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL);
assertThat(systemEGLDriver).isEqualTo("");
// Done with the test, remove the callback
SystemProperties.removeChangeCallback(countDown);
SystemProperties.removeChangeCallback(propertyChangeSignal1.getCountDownJob());
}
@Test
@@ -256,70 +265,217 @@ public class GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest {
@Test
public void onRebootCancelled_ToggleSwitchFromOnToOff() {
// Step 1: Toggle the "Enable ANGLE" switch on
// Add a callback when SystemProperty changes.
// This allows the thread to wait until
// GpuService::toggleAngleAsSystemDriver() updates the persist.graphics.egl.
final CountDownLatch countDownLatch = new CountDownLatch(1);
Runnable countDown = new Runnable() {
@Override
public void run() {
countDownLatch.countDown();
}
};
SystemProperties.addChangeCallback(countDown);
PropertyChangeSignal propertyChangeSignal1 = new PropertyChangeSignal();
SystemProperties.addChangeCallback(propertyChangeSignal1.getCountDownJob());
mController.onPreferenceChange(mPreference, true);
// Block the following code execution until the "persist.graphics.egl" property value is
// changed.
propertyChangeSignal1.wait(100);
// Test that if the current persist.graphics.egl is "angle",
// when reboot is cancelled, persist.graphics.egl is changed back to "",
// and switch is set to unchecked.
// Step 2: Cancel reboot
PropertyChangeSignal propertyChangeSignal2 = new PropertyChangeSignal();
SystemProperties.addChangeCallback(propertyChangeSignal2.getCountDownJob());
when(mSystemPropertiesMock.get(eq(PROPERTY_PERSISTENT_GRAPHICS_EGL), any()))
.thenReturn(ANGLE_DRIVER_SUFFIX);
.thenReturn(SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL));
mController.onRebootCancelled();
try {
countDownLatch.await(100, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Assert.fail(e.getMessage());
}
mController.onRebootDialogDismissed();
// Block the following code execution until the "persist.graphics.egl" property valye is
// changed.
propertyChangeSignal2.wait(100);
// Step 3: Verify results
// 1) Test that persist.graphics.egl is changed back to "".
final String systemEGLDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL);
assertThat(systemEGLDriver).isEqualTo("");
// 2) Test that the switch is set to unchecked.
assertThat(mPreference.isChecked()).isFalse();
// Done with the test, remove the callback.
SystemProperties.removeChangeCallback(countDown);
// Step 4: Clean up
// Done with the test, remove the callback
SystemProperties.removeChangeCallback(propertyChangeSignal1.getCountDownJob());
SystemProperties.removeChangeCallback(propertyChangeSignal2.getCountDownJob());
}
@Test
public void onRebootCancelled_ToggleSwitchFromOffToOn() {
// Step 1: Toggle off the switch "Enable ANGLE"
// Add a callback when SystemProperty changes.
// This allows the thread to wait until
// GpuService::toggleAngleAsSystemDriver() updates the persist.graphics.egl.
final CountDownLatch countDownLatch = new CountDownLatch(1);
Runnable countDown = new Runnable() {
@Override
public void run() {
countDownLatch.countDown();
}
};
SystemProperties.addChangeCallback(countDown);
PropertyChangeSignal propertyChangeSignal1 = new PropertyChangeSignal();
SystemProperties.addChangeCallback(propertyChangeSignal1.getCountDownJob());
mController.onPreferenceChange(mPreference, false);
// Block the following code execution until the "persist.graphics.egl" property value is
// changed.
propertyChangeSignal1.wait(100);
// Test that if the current persist.graphics.egl is "",
// when reboot is cancelled, persist.graphics.egl is changed back to "angle",
// and switch is set to checked.
// Step 2: Cancel reboot
PropertyChangeSignal propertyChangeSignal2 = new PropertyChangeSignal();
SystemProperties.addChangeCallback(propertyChangeSignal2.getCountDownJob());
when(mSystemPropertiesMock.get(eq(PROPERTY_PERSISTENT_GRAPHICS_EGL), any()))
.thenReturn("");
.thenReturn(SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL));
mController.onRebootCancelled();
try {
countDownLatch.await(100, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Assert.fail(e.getMessage());
}
mController.onRebootDialogDismissed();
// Block the following code execution until the "persist.graphics.egl" property valye is
// changed.
propertyChangeSignal2.wait(100);
// Step 3: Verify results
// 1) Test that persist.graphics.egl is changed back to "ANGLE"
final String systemEGLDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL);
assertThat(systemEGLDriver).isEqualTo(ANGLE_DRIVER_SUFFIX);
// 2) Test that the switch is set to checked
assertThat(mPreference.isChecked()).isTrue();
// Step 4: Clean up
// Done with the test, remove the callback.
SystemProperties.removeChangeCallback(countDown);
SystemProperties.removeChangeCallback(propertyChangeSignal1.getCountDownJob());
SystemProperties.removeChangeCallback(propertyChangeSignal2.getCountDownJob());
}
@Test
public void onRebootDialogDismissed_ToggleSwitchFromOnToOff() {
// Step 1: Toggle on the switch "Enable ANGLE"
// Add a callback when SystemProperty changes.
// This allows the thread to wait until
// GpuService::toggleAngleAsSystemDriver() updates the persist.graphics.egl.
PropertyChangeSignal propertyChangeSignal1 = new PropertyChangeSignal();
SystemProperties.addChangeCallback(propertyChangeSignal1.getCountDownJob());
mController.onPreferenceChange(mPreference, true);
// Block the following code execution until the "persist.graphics.egl" property value is
// changed.
propertyChangeSignal1.wait(100);
// Step 2: Dismiss the reboot dialog
PropertyChangeSignal propertyChangeSignal2 = new PropertyChangeSignal();
SystemProperties.addChangeCallback(propertyChangeSignal2.getCountDownJob());
when(mSystemPropertiesMock.get(eq(PROPERTY_PERSISTENT_GRAPHICS_EGL), any()))
.thenReturn(SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL));
mController.onRebootDialogDismissed();
// Block the following code execution until the "persist.graphics.egl" property valye is
// changed.
propertyChangeSignal2.wait(100);
// Step 3: Verify results
// 1) Test that persist.graphics.egl is changed back to "".
final String systemEGLDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL);
assertThat(systemEGLDriver).isEqualTo("");
// 2) Test that the switch is set to unchecked.
assertThat(mPreference.isChecked()).isFalse();
// Step 4: Clean up
// Done with the test, remove the callback
SystemProperties.removeChangeCallback(propertyChangeSignal1.getCountDownJob());
SystemProperties.removeChangeCallback(propertyChangeSignal2.getCountDownJob());
}
@Test
public void onRebootDialogDismissed_ToggleSwitchFromOffToOn() {
// Step 1: Toggle on the switch "Enable ANGLE"
// Add a callback when SystemProperty changes.
// This allows the thread to wait until
// GpuService::toggleAngleAsSystemDriver() updates the persist.graphics.egl.
PropertyChangeSignal propertyChangeSignal1 = new PropertyChangeSignal();
SystemProperties.addChangeCallback(propertyChangeSignal1.getCountDownJob());
mController.onPreferenceChange(mPreference, false);
// Block the following code execution until the "persist.graphics.egl" property value is
// changed.
propertyChangeSignal1.wait(100);
// Step 2: Dismiss the reboot dialog
PropertyChangeSignal propertyChangeSignal2 = new PropertyChangeSignal();
SystemProperties.addChangeCallback(propertyChangeSignal2.getCountDownJob());
when(mSystemPropertiesMock.get(eq(PROPERTY_PERSISTENT_GRAPHICS_EGL), any()))
.thenReturn(SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL));
mController.onRebootDialogDismissed();
// Block the following code execution until the "persist.graphics.egl" property valye is
// changed.
propertyChangeSignal2.wait(100);
// Step 3: Verify results
// 1) Test that persist.graphics.egl is changed back to "ANGLE"
final String systemEGLDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL);
assertThat(systemEGLDriver).isEqualTo(ANGLE_DRIVER_SUFFIX);
// 2) Test that the switch is set to checked
assertThat(mPreference.isChecked()).isTrue();
// Step 4: Clean up
// Done with the test, remove the callback
SystemProperties.removeChangeCallback(propertyChangeSignal1.getCountDownJob());
SystemProperties.removeChangeCallback(propertyChangeSignal2.getCountDownJob());
}
@Test
public void onRebootDialogConfirmed_ToggleSwitchOnRemainsOn() {
// Step 1: Toggle on the switch "Enable ANGLE"
// Add a callback when SystemProperty changes.
// This allows the thread to wait until
// GpuService::toggleAngleAsSystemDriver() updates the persist.graphics.egl.
PropertyChangeSignal propertyChangeSignal1 = new PropertyChangeSignal();
SystemProperties.addChangeCallback(propertyChangeSignal1.getCountDownJob());
mController.onPreferenceChange(mPreference, true);
// Block the following code execution until the "persist.graphics.egl" property value is
// changed.
propertyChangeSignal1.wait(100);
// Step 2: Confirm reboot
PropertyChangeSignal propertyChangeSignal2 = new PropertyChangeSignal();
SystemProperties.addChangeCallback(propertyChangeSignal2.getCountDownJob());
when(mSystemPropertiesMock.get(eq(PROPERTY_PERSISTENT_GRAPHICS_EGL), any()))
.thenReturn(SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL));
mController.onRebootConfirmed(mContext);
mController.onRebootDialogDismissed();
// Block the following code execution until the "persist.graphics.egl" property valye is
// changed.
propertyChangeSignal2.wait(100);
// Step 3: Verify Results
// Test that persist.graphics.egl remains to be "ANGLE"
final String systemEGLDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL);
assertThat(systemEGLDriver).isEqualTo(ANGLE_DRIVER_SUFFIX);
// Step 4: Clean up
// Done with the test, remove the callback
SystemProperties.removeChangeCallback(propertyChangeSignal1.getCountDownJob());
SystemProperties.removeChangeCallback(propertyChangeSignal2.getCountDownJob());
}
@Test
public void onRebootDialogConfirmed_ToggleSwitchOffRemainsOff() {
// Step 1: Toggle off the switch "Enable ANGLE"
// Add a callback when SystemProperty changes.
// This allows the thread to wait until
// GpuService::toggleAngleAsSystemDriver() updates the persist.graphics.egl.
PropertyChangeSignal propertyChangeSignal1 = new PropertyChangeSignal();
SystemProperties.addChangeCallback(propertyChangeSignal1.getCountDownJob());
mController.onPreferenceChange(mPreference, false);
// Block the following code execution until the "persist.graphics.egl" property value is
// changed.
propertyChangeSignal1.wait(100);
// Step 2: Confirm reboot
PropertyChangeSignal propertyChangeSignal2 = new PropertyChangeSignal();
SystemProperties.addChangeCallback(propertyChangeSignal2.getCountDownJob());
when(mSystemPropertiesMock.get(eq(PROPERTY_PERSISTENT_GRAPHICS_EGL), any()))
.thenReturn(SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL));
mController.onRebootConfirmed(mContext);
mController.onRebootDialogDismissed();
// Block the following code execution until the "persist.graphics.egl" property valye is
// changed.
propertyChangeSignal2.wait(100);
// Step 3: Verify Results
// Test that persist.graphics.egl remains to be ""
final String systemEGLDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL);
assertThat(systemEGLDriver).isEqualTo("");
// Step 4: Clean up
// Done with the test, remove the callback
SystemProperties.removeChangeCallback(propertyChangeSignal1.getCountDownJob());
SystemProperties.removeChangeCallback(propertyChangeSignal2.getCountDownJob());
}
}

View File

@@ -16,6 +16,8 @@
package com.android.settings.regionalpreferences;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -34,6 +36,7 @@ import androidx.preference.PreferenceScreen;
import androidx.test.annotation.UiThreadTest;
import androidx.test.core.app.ApplicationProvider;
import com.android.internal.app.LocalePicker;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.widget.TickButtonPreference;
@@ -68,6 +71,7 @@ public class NumberingSystemItemControllerTest {
@After
public void tearDown() {
LocaleList.setDefault(mCacheLocale);
LocalePicker.updateLocales(mCacheLocale);
}
@Test
@@ -122,6 +126,31 @@ public class NumberingSystemItemControllerTest {
"test_key");
}
@Test
@UiThreadTest
public void handlePreferenceTreeClick_numbersSelect_numberingSystemIsUpdated() {
LocalePicker.updateLocales(LocaleList.forLanguageTags("en-US,zh-TW,ar-BH"));
Bundle bundle = new Bundle();
bundle.putString(RegionalPreferencesEntriesFragment.ARG_KEY_REGIONAL_PREFERENCE,
NumberingSystemItemController.ARG_VALUE_NUMBERING_SYSTEM_SELECT);
bundle.putString(
NumberingSystemItemController.KEY_SELECTED_LANGUAGE, "ar-BH");
TickButtonPreference defaultPreference = new TickButtonPreference(mApplicationContext);
TickButtonPreference numberPreference = new TickButtonPreference(mApplicationContext);
defaultPreference.setKey("default");
numberPreference.setKey("latn");
mPreferenceScreen.addPreference(defaultPreference);
mPreferenceScreen.addPreference(numberPreference);
mController = new NumberingSystemItemController(mApplicationContext, bundle);
mController.setParentFragment(mFragment);
mController.displayPreference(mPreferenceScreen);
mController.handlePreferenceTreeClick(numberPreference);
assertThat(LocalePicker.getLocales().toLanguageTags()).contains(
"en-US,zh-TW,ar-BH-u-nu-latn");
}
@Test
@UiThreadTest
public void displayPreference_languageOptAndHas2LocaleWithSingleNu_showNothing() {