From c8a53a9391649ab216ee03f597225d8a091462a9 Mon Sep 17 00:00:00 2001 From: jasonwshsu Date: Wed, 5 Apr 2023 23:58:33 +0800 Subject: [PATCH 1/9] Fix pair another dialog doesn't show up after pairing in all bluetooth device page. Solution: Finish the page immediately to back to previous page when already BONDED the device in all bluetooth device page. Bug: 270096758 Test: make RunSettingsRoboTests ROBOTEST_FILTER=ViewAllBluetoothDevicesPreferenceControllerTest Change-Id: I13a88c3fbe0c6851f9446a9f574a1c18f934cd2e --- res/xml/hearing_device_pairing_detail.xml | 6 +- .../HearingDevicePairingDetail.java | 7 ++ ...lBluetoothDevicesPreferenceController.java | 88 +++++++++++++++++++ .../BluetoothDevicePairingDetailBase.java | 3 + ...etoothDevicesPreferenceControllerTest.java | 82 +++++++++++++++++ 5 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 src/com/android/settings/accessibility/ViewAllBluetoothDevicesPreferenceController.java create mode 100644 tests/robotests/src/com/android/settings/accessibility/ViewAllBluetoothDevicesPreferenceControllerTest.java diff --git a/res/xml/hearing_device_pairing_detail.xml b/res/xml/hearing_device_pairing_detail.xml index 29a611879a7..0ccd000a4a3 100644 --- a/res/xml/hearing_device_pairing_detail.xml +++ b/res/xml/hearing_device_pairing_detail.xml @@ -26,11 +26,11 @@ android:key="device_control_category" android:title="@string/accessibility_found_all_devices"> + settings:useAdminDisabledSummary="true" + settings:controller="com.android.settings.accessibility.ViewAllBluetoothDevicesPreferenceController"/> Date: Wed, 12 Apr 2023 14:41:35 +0800 Subject: [PATCH 2/9] [BiometricsV2] fix enrolling rotate failed Clean help message during progress changed, and reload help message from ViewModel in onStart of enrolling page. Bug: 275513362 Test: atest FingerprintEnrollProgressViewModelTest Test: enable biometrics v2, and manully rotate screen when help msg is shown, and check help msg is kept after screen rotation. Change-Id: I44220050806038ee62750f8945b8ba8f8bb9c282 --- ...ingerprintEnrollEnrollingSfpsFragment.java | 10 ++++- ...ngerprintEnrollEnrollingUdfpsFragment.java | 17 ++++++--- .../FingerprintEnrollProgressViewModel.java | 1 + ...ingerprintEnrollProgressViewModelTest.java | 38 +++++++++++++++++++ 4 files changed, 58 insertions(+), 8 deletions(-) diff --git a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingSfpsFragment.java b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingSfpsFragment.java index 720857c32b0..f2dad238b72 100644 --- a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingSfpsFragment.java +++ b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingSfpsFragment.java @@ -219,7 +219,13 @@ public class FingerprintEnrollEnrollingSfpsFragment extends Fragment { super.onStart(); startEnrollment(); updateProgress(false /* animate */, mProgressViewModel.getProgressLiveData().getValue()); - updateTitleAndDescription(); + final EnrollmentStatusMessage msg = mProgressViewModel.getHelpMessageLiveData().getValue(); + if (msg != null) { + onEnrollmentHelp(msg); + } else { + clearError(); + updateTitleAndDescription(); + } } @Override @@ -373,7 +379,7 @@ public class FingerprintEnrollEnrollingSfpsFragment extends Fragment { mView.setHeaderText(error); mView.getHeaderTextView().setContentDescription(error); new GlifLayoutHelper(getActivity(), mView).setDescriptionText(""); - if (!mHelpAnimation.isRunning()) { + if (isResumed() && !mHelpAnimation.isRunning()) { mHelpAnimation.start(); } applySfpsErrorDynamicColors(true); diff --git a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingUdfpsFragment.java b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingUdfpsFragment.java index 0fdfd2ee63e..40d571330d6 100644 --- a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingUdfpsFragment.java +++ b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingUdfpsFragment.java @@ -105,7 +105,7 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment { }; private final Observer mHelpMessageObserver = helpMessage -> { if (helpMessage != null) { - onEnrollmentHelp(helpMessage.getStr()); + onEnrollmentHelp(helpMessage); } }; private final Observer mErrorMessageObserver = errorMessage -> { @@ -198,7 +198,12 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment { super.onStart(); startEnrollment(); updateProgress(false /* animate */, mProgressViewModel.getProgressLiveData().getValue()); - updateTitleAndDescription(); + final EnrollmentStatusMessage msg = mProgressViewModel.getHelpMessageLiveData().getValue(); + if (msg != null) { + onEnrollmentHelp(msg); + } else { + updateTitleAndDescription(); + } } @Override @@ -484,13 +489,13 @@ public class FingerprintEnrollEnrollingUdfpsFragment extends Fragment { } - private void onEnrollmentHelp(CharSequence helpString) { - if (!TextUtils.isEmpty(helpString)) { - showError(helpString); + private void onEnrollmentHelp(@NonNull EnrollmentStatusMessage helpMessage) { + final CharSequence helpStr = helpMessage.getStr(); + if (!TextUtils.isEmpty(helpStr)) { + showError(helpStr); mUdfpsEnrollView.onEnrollmentHelp(); } } - private void onEnrollmentError(@NonNull EnrollmentStatusMessage errorMessage) { removeEnrollmentObservers(); diff --git a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java index 695ea0d0c0f..d77d9d3f7e8 100644 --- a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java +++ b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java @@ -77,6 +77,7 @@ public class FingerprintEnrollProgressViewModel extends AndroidViewModel { Log.d(TAG, "onEnrollmentProgress(" + remaining + "), steps: " + currentSteps + ", post progress as " + progress); } + mHelpMessageLiveData.setValue(null); mProgressLiveData.postValue(progress); } diff --git a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java index 6190c5e3f8f..bdb45b0300f 100644 --- a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java +++ b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java @@ -224,6 +224,44 @@ public class FingerprintEnrollProgressViewModelTest { assertThat(progress.getRemaining()).isEqualTo(0); } + @Test + public void testProgressUpdateClearHelpMessage() { + // Start enrollment + mViewModel.setToken(new byte[] { 1, 2, 3 }); + final boolean ret = mViewModel.startEnrollment(ENROLL_ENROLL); + assertThat(ret).isTrue(); + assertThat(mCallbackWrapper.mValue).isNotNull(); + final LiveData progressLiveData = mViewModel.getProgressLiveData(); + final LiveData helpMsgLiveData = + mViewModel.getHelpMessageLiveData(); + + // Update first progress + mCallbackWrapper.mValue.onEnrollmentProgress(25); + EnrollmentProgress progress = progressLiveData.getValue(); + assertThat(progress).isNotNull(); + assertThat(progress.getSteps()).isEqualTo(25); + assertThat(progress.getRemaining()).isEqualTo(25); + + // Update help message + final int testHelpMsgId = 3; + final String testHelpString = "Test Help String"; + mCallbackWrapper.mValue.onEnrollmentHelp(testHelpMsgId, testHelpString); + final EnrollmentStatusMessage helpMsg = helpMsgLiveData.getValue(); + assertThat(helpMsg).isNotNull(); + assertThat(helpMsg.getMsgId()).isEqualTo(testHelpMsgId); + assertThat(helpMsg.getStr().toString()).isEqualTo(testHelpString); + + // Update second progress + mCallbackWrapper.mValue.onEnrollmentProgress(20); + progress = progressLiveData.getValue(); + assertThat(progress).isNotNull(); + assertThat(progress.getSteps()).isEqualTo(25); + assertThat(progress.getRemaining()).isEqualTo(20); + + // Help message shall be set to null + assertThat(helpMsgLiveData.getValue()).isNull(); + } + @Test public void testProgressUpdateWithMessageDisplayController() { // Enable MessageDisplayController and mock handler for it From 742e506d13e4f28e4334d1344816f1df45c0c772 Mon Sep 17 00:00:00 2001 From: Wesley Wang Date: Fri, 14 Apr 2023 18:32:25 +0800 Subject: [PATCH 3/9] Cleanup smart battery entries (1/3) - Remove smart battery entry from battery settings page since restriction app info already move to app list page and smart battery feature will keep as enabled, smart battery page will be empty - Enable battery usage list page for AOSP Bug: 277175992 Test: make SettingsRoboTests Change-Id: I9221cecbfc8445b8f470975551f600e94e3128d5 --- res/values/config.xml | 2 +- res/xml/power_usage_summary.xml | 9 +- .../fuelgauge/PowerUsageFeatureProvider.java | 10 -- .../PowerUsageFeatureProviderImpl.java | 10 -- .../BatteryManagerPreferenceController.java | 85 ------------ .../PowerUsageFeatureProviderImplTest.java | 5 - ...atteryManagerPreferenceControllerTest.java | 130 ------------------ 7 files changed, 2 insertions(+), 249 deletions(-) delete mode 100644 src/com/android/settings/fuelgauge/batterytip/BatteryManagerPreferenceController.java delete mode 100644 tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryManagerPreferenceControllerTest.java diff --git a/res/values/config.xml b/res/values/config.xml index 28bf7232aca..3221c063cf4 100755 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -658,7 +658,7 @@ true - false + true true diff --git a/res/xml/power_usage_summary.xml b/res/xml/power_usage_summary.xml index 9e86bf52b3d..121e1fe9628 100644 --- a/res/xml/power_usage_summary.xml +++ b/res/xml/power_usage_summary.xml @@ -1,5 +1,5 @@ - - - diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java index 50a52aa4d1f..7511093c1c2 100644 --- a/src/com/android/settings/Settings.java +++ b/src/com/android/settings/Settings.java @@ -322,6 +322,7 @@ public class Settings extends SettingsActivity { public static class PremiumSmsAccessActivity extends SettingsActivity { /* empty */ } public static class PictureInPictureSettingsActivity extends SettingsActivity { /* empty */ } public static class TurnScreenOnSettingsActivity extends SettingsActivity { /* empty */ } + public static class AppTurnScreenOnSettingsActivity extends SettingsActivity { /* empty */ } public static class AppPictureInPictureSettingsActivity extends SettingsActivity { /* empty */ } public static class ZenAccessSettingsActivity extends SettingsActivity { /* empty */ } public static class ZenAccessDetailSettingsActivity extends SettingsActivity {} diff --git a/src/com/android/settings/applications/AppStateTurnScreenOnBridge.java b/src/com/android/settings/applications/AppStateTurnScreenOnBridge.java new file mode 100644 index 00000000000..8a75c0bf3c1 --- /dev/null +++ b/src/com/android/settings/applications/AppStateTurnScreenOnBridge.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.applications; + +import android.Manifest; +import android.app.AppOpsManager; +import android.content.Context; + +import com.android.settingslib.applications.ApplicationsState; +import com.android.settingslib.applications.ApplicationsState.AppEntry; +import com.android.settingslib.applications.ApplicationsState.AppFilter; + +import java.util.List; + +/** + * Connects app op info to the ApplicationsState. Extends {@link AppStateAppOpsBridge} to tailor + * to the semantics of {@link Manifest.permission#TURN_SCREEN_ON}. + * Also provides app filters that can use the info. + */ +public class AppStateTurnScreenOnBridge extends AppStateAppOpsBridge { + private static final String APP_OP_STR = AppOpsManager.OPSTR_TURN_SCREEN_ON; + private static final String[] PERMISSIONS = { + Manifest.permission.TURN_SCREEN_ON + }; + + private AppOpsManager mAppOpsManager; + + public AppStateTurnScreenOnBridge(Context context, ApplicationsState appState, + Callback callback) { + super(context, appState, callback, AppOpsManager.strOpToOp(APP_OP_STR), PERMISSIONS); + mAppOpsManager = context.getSystemService(AppOpsManager.class); + } + + @Override + protected void updateExtraInfo(AppEntry app, String pkg, int uid) { + app.extraInfo = getPermissionInfo(pkg, uid); + } + + @Override + protected void loadAllExtraInfo() { + super.loadAllExtraInfo(); + final List apps = mAppSession.getAllApps(); + for (AppEntry app : apps) { + if (app.extraInfo instanceof PermissionState) { + ((PermissionState) app.extraInfo).appOpMode = mAppOpsManager.unsafeCheckOpNoThrow( + APP_OP_STR, app.info.uid, app.info.packageName); + } + } + } + + @Override + public PermissionState getPermissionInfo(String pkg, int uid) { + PermissionState ps = super.getPermissionInfo(pkg, uid); + ps.appOpMode = mAppOpsManager.unsafeCheckOpNoThrow(APP_OP_STR, uid, pkg); + return ps; + } + + public static final AppFilter FILTER_TURN_SCREEN_ON_APPS = new AppFilter() { + + @Override + public void init() { + } + + @Override + public boolean filterApp(AppEntry info) { + // If extraInfo != null, it means that the app has declared + // Manifest.permission.TURN_SCREEN_ON and therefore it should appear on our + // list + return info.extraInfo != null; + } + }; +} diff --git a/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnDetails.java b/src/com/android/settings/applications/appinfo/TurnScreenOnDetails.java similarity index 50% rename from src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnDetails.java rename to src/com/android/settings/applications/appinfo/TurnScreenOnDetails.java index 4540ab9863a..39e1a5fa2ad 100644 --- a/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnDetails.java +++ b/src/com/android/settings/applications/appinfo/TurnScreenOnDetails.java @@ -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. @@ -13,14 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.settings.applications.specialaccess.turnscreenon; +package com.android.settings.applications.appinfo; +import static android.app.Activity.RESULT_CANCELED; +import static android.app.Activity.RESULT_OK; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.MODE_ERRORED; -import static android.app.AppOpsManager.OP_TURN_SCREEN_ON; import android.app.AppOpsManager; import android.app.settings.SettingsEnums; +import android.content.Context; import android.os.Bundle; import androidx.appcompat.app.AlertDialog; @@ -29,41 +31,51 @@ import androidx.preference.Preference.OnPreferenceChangeListener; import androidx.preference.SwitchPreference; import com.android.settings.R; +import com.android.settings.Settings; import com.android.settings.applications.AppInfoWithHeader; +import com.android.settings.applications.AppStateAppOpsBridge; +import com.android.settings.applications.AppStateTurnScreenOnBridge; +import com.android.settingslib.applications.ApplicationsState; /** * Detail page for turn screen on special app access. */ -public class TurnScreenOnDetails extends AppInfoWithHeader - implements OnPreferenceChangeListener { +public class TurnScreenOnDetails extends AppInfoWithHeader implements OnPreferenceChangeListener { private static final String KEY_APP_OPS_SETTINGS_SWITCH = "app_ops_settings_switch"; - private SwitchPreference mSwitchPref; + private AppStateTurnScreenOnBridge mAppBridge; private AppOpsManager mAppOpsManager; + private SwitchPreference mSwitchPref; + private AppStateAppOpsBridge.PermissionState mPermissionState; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mAppOpsManager = this.getSystemService(AppOpsManager.class); + final Context context = getActivity(); + mAppBridge = new AppStateTurnScreenOnBridge(context, mState, /*callback=*/ null); + mAppOpsManager = context.getSystemService(AppOpsManager.class); // find preferences addPreferencesFromResource(R.xml.turn_screen_on_permissions_details); mSwitchPref = (SwitchPreference) findPreference(KEY_APP_OPS_SETTINGS_SWITCH); - - // set title/summary for all of them - mSwitchPref.setTitle(R.string.allow_turn_screen_on); - - // install event listeners mSwitchPref.setOnPreferenceChangeListener(this); } @Override public boolean onPreferenceChange(Preference preference, Object newValue) { + final boolean checked = (Boolean) newValue; if (preference == mSwitchPref) { - setTurnScreenOnAppOp(mPackageInfo.applicationInfo.uid, mPackageName, - (Boolean) newValue); + if (mPermissionState != null && checked != mPermissionState.isPermissible()) { + if (Settings.TurnScreenOnSettingsActivity.class.getName().equals( + getIntent().getComponent().getClassName())) { + setResult(checked ? RESULT_OK : RESULT_CANCELED); + } + setCanTurnScreenOn(checked); + refreshUi(); + } return true; } return false; @@ -71,9 +83,13 @@ public class TurnScreenOnDetails extends AppInfoWithHeader @Override protected boolean refreshUi() { - boolean isAllowed = isTurnScreenOnAllowed(mAppOpsManager, - mPackageInfo.applicationInfo.uid, mPackageName); - mSwitchPref.setChecked(isAllowed); + if (mPackageInfo == null || mPackageInfo.applicationInfo == null) { + return false; + } + mPermissionState = mAppBridge.getPermissionInfo(mPackageName, + mPackageInfo.applicationInfo.uid); + mSwitchPref.setEnabled(mPermissionState.permissionDeclared); + mSwitchPref.setChecked(mPermissionState.isPermissible()); return true; } @@ -91,28 +107,24 @@ public class TurnScreenOnDetails extends AppInfoWithHeader * Sets whether the app associated with the given {@code packageName} is allowed to turn the * screen on. */ - void setTurnScreenOnAppOp(int uid, String packageName, boolean value) { - final int newMode = value ? MODE_ALLOWED : MODE_ERRORED; - mAppOpsManager.setMode(OP_TURN_SCREEN_ON, uid, packageName, newMode); - } - - /** - * @return whether the app associated with the given {@code packageName} is allowed to turn the - * screen on. - */ - static boolean isTurnScreenOnAllowed(AppOpsManager appOpsManager, int uid, String packageName) { - return appOpsManager.checkOpNoThrow(OP_TURN_SCREEN_ON, uid, packageName) == MODE_ALLOWED; + void setCanTurnScreenOn(boolean newState) { + mAppOpsManager.setUidMode(AppOpsManager.OPSTR_TURN_SCREEN_ON, + mPackageInfo.applicationInfo.uid, newState ? MODE_ALLOWED : MODE_ERRORED); } /** * @return the summary for the current state of whether the app associated with the given * packageName is allowed to turn the screen on. */ - public static int getPreferenceSummary(AppOpsManager appOpsManager, int uid, - String packageName) { - final boolean enabled = TurnScreenOnDetails.isTurnScreenOnAllowed(appOpsManager, uid, - packageName); - return enabled ? R.string.app_permission_summary_allowed + public static int getSummary(Context context, ApplicationsState.AppEntry entry) { + final AppStateAppOpsBridge.PermissionState state; + if (entry.extraInfo instanceof AppStateAppOpsBridge.PermissionState) { + state = (AppStateAppOpsBridge.PermissionState) entry.extraInfo; + } else { + state = new AppStateTurnScreenOnBridge(context, /*appState=*/ null, + /*callback=*/ null).getPermissionInfo(entry.info.packageName, entry.info.uid); + } + return state.isPermissible() ? R.string.app_permission_summary_allowed : R.string.app_permission_summary_not_allowed; } } diff --git a/src/com/android/settings/applications/manageapplications/AppFilterRegistry.java b/src/com/android/settings/applications/manageapplications/AppFilterRegistry.java index f3195921dcb..e7bb88dadf1 100644 --- a/src/com/android/settings/applications/manageapplications/AppFilterRegistry.java +++ b/src/com/android/settings/applications/manageapplications/AppFilterRegistry.java @@ -30,6 +30,7 @@ import com.android.settings.applications.AppStateMediaManagementAppsBridge; import com.android.settings.applications.AppStateNotificationBridge; import com.android.settings.applications.AppStateOverlayBridge; import com.android.settings.applications.AppStatePowerBridge; +import com.android.settings.applications.AppStateTurnScreenOnBridge; import com.android.settings.applications.AppStateUsageBridge; import com.android.settings.applications.AppStateWriteSettingsBridge; import com.android.settings.nfc.AppStateNfcTagAppsBridge; @@ -67,6 +68,7 @@ public class AppFilterRegistry { FILTER_LONG_BACKGROUND_TASKS, FILTER_APPS_CLONE, FILTER_APPS_NFC_TAG, + FILTER_APPS_TURN_SCREEN_ON, }) @interface FilterType {} @@ -98,8 +100,9 @@ public class AppFilterRegistry { public static final int FILTER_LONG_BACKGROUND_TASKS = 24; public static final int FILTER_APPS_CLONE = 25; public static final int FILTER_APPS_NFC_TAG = 26; - private static final int NUM_FILTER_ENTRIES = 27; - // Next id: 27. If you add an entry here, please change NUM_FILTER_ENTRIES. + public static final int FILTER_APPS_TURN_SCREEN_ON = 27; + private static final int NUM_FILTER_ENTRIES = 28; + // Next id: 28. If you add an entry here, please change NUM_FILTER_ENTRIES. private static AppFilterRegistry sRegistry; @@ -271,6 +274,12 @@ public class AppFilterRegistry { AppStateNfcTagAppsBridge.FILTER_APPS_NFC_TAG, FILTER_APPS_NFC_TAG, R.string.change_nfc_tag_apps_title); + + // Apps that are allowed to turn the screen on. + mFilters[FILTER_APPS_TURN_SCREEN_ON] = new AppFilterItem( + AppStateTurnScreenOnBridge.FILTER_TURN_SCREEN_ON_APPS, + FILTER_APPS_TURN_SCREEN_ON, + R.string.turn_screen_on_title); } public static AppFilterRegistry getInstance() { @@ -313,6 +322,8 @@ public class AppFilterRegistry { return FILTER_APPS_CLONE; case ManageApplications.LIST_TYPE_NFC_TAG_APPS: return FILTER_APPS_NFC_TAG; + case ManageApplications.LIST_TYPE_TURN_SCREEN_ON: + return FILTER_APPS_TURN_SCREEN_ON; default: return FILTER_APPS_ALL; } diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java index 0fbbf18f0a5..16b1b6c086f 100644 --- a/src/com/android/settings/applications/manageapplications/ManageApplications.java +++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java @@ -110,6 +110,7 @@ import com.android.settings.Settings.MediaManagementAppsActivity; import com.android.settings.Settings.NotificationAppListActivity; import com.android.settings.Settings.NotificationReviewPermissionsActivity; import com.android.settings.Settings.OverlaySettingsActivity; +import com.android.settings.Settings.TurnScreenOnSettingsActivity; import com.android.settings.Settings.UsageAccessSettingsActivity; import com.android.settings.Settings.WriteSettingsActivity; import com.android.settings.SettingsActivity; @@ -129,6 +130,7 @@ import com.android.settings.applications.AppStateNotificationBridge; import com.android.settings.applications.AppStateNotificationBridge.NotificationsSentState; import com.android.settings.applications.AppStateOverlayBridge; import com.android.settings.applications.AppStatePowerBridge; +import com.android.settings.applications.AppStateTurnScreenOnBridge; import com.android.settings.applications.AppStateUsageBridge; import com.android.settings.applications.AppStateUsageBridge.UsageState; import com.android.settings.applications.AppStateWriteSettingsBridge; @@ -142,6 +144,7 @@ import com.android.settings.applications.appinfo.ExternalSourcesDetails; import com.android.settings.applications.appinfo.LongBackgroundTasksDetails; import com.android.settings.applications.appinfo.ManageExternalStorageDetails; import com.android.settings.applications.appinfo.MediaManagementAppsDetails; +import com.android.settings.applications.appinfo.TurnScreenOnDetails; import com.android.settings.applications.appinfo.WriteSettingsDetails; import com.android.settings.core.InstrumentedFragment; import com.android.settings.core.SubSettingLauncher; @@ -268,6 +271,7 @@ public class ManageApplications extends InstrumentedFragment public static final int LIST_TYPE_LONG_BACKGROUND_TASKS = 16; public static final int LIST_TYPE_CLONED_APPS = 17; public static final int LIST_TYPE_NFC_TAG_APPS = 18; + public static final int LIST_TYPE_TURN_SCREEN_ON = 19; // List types that should show instant apps. public static final Set LIST_TYPES_WITH_INSTANT = new ArraySet<>(Arrays.asList( @@ -569,6 +573,8 @@ public class ManageApplications extends InstrumentedFragment return SettingsEnums.CLONED_APPS; case LIST_TYPE_NFC_TAG_APPS: return SettingsEnums.CONFIG_NFC_TAG_APP_PREF; + case LIST_TYPE_TURN_SCREEN_ON: + return SettingsEnums.SETTINGS_TURN_SCREEN_ON_ACCESS; default: return SettingsEnums.PAGE_UNKNOWN; } @@ -739,6 +745,9 @@ public class ManageApplications extends InstrumentedFragment startAppInfoFragment(ChangeNfcTagAppsStateDetails.class, R.string.change_nfc_tag_apps_title); break; + case LIST_TYPE_TURN_SCREEN_ON: + startAppInfoFragment(TurnScreenOnDetails.class, R.string.turn_screen_on_title); + break; // TODO: Figure out if there is a way where we can spin up the profile's settings // process ahead of time, to avoid a long load of data when user clicks on a managed // app. Maybe when they load the list of apps that contains managed profile apps. @@ -1066,6 +1075,8 @@ public class ManageApplications extends InstrumentedFragment screenTitle = R.string.cloned_apps_dashboard_title; } else if (className.equals(ChangeNfcTagAppsActivity.class.getName())) { screenTitle = R.string.change_nfc_tag_apps_title; + } else if (className.equals(TurnScreenOnSettingsActivity.class.getName())) { + screenTitle = R.string.turn_screen_on_title; } else { if (screenTitle == -1) { screenTitle = R.string.all_apps; @@ -1276,6 +1287,8 @@ public class ManageApplications extends InstrumentedFragment mExtraInfoBridge = new AppStateClonedAppsBridge(mContext, mState, this); } else if (mManageApplications.mListType == LIST_TYPE_NFC_TAG_APPS) { mExtraInfoBridge = new AppStateNfcTagAppsBridge(mContext, mState, this); + } else if (mManageApplications.mListType == LIST_TYPE_TURN_SCREEN_ON) { + mExtraInfoBridge = new AppStateTurnScreenOnBridge(mContext, mState, this); } else { mExtraInfoBridge = null; } @@ -1830,6 +1843,9 @@ public class ManageApplications extends InstrumentedFragment holder.setSummary( ChangeNfcTagAppsStateDetails.getSummary(mContext, entry)); break; + case LIST_TYPE_TURN_SCREEN_ON: + holder.setSummary(TurnScreenOnDetails.getSummary(mContext, entry)); + break; default: holder.updateSizeText(entry, mManageApplications.mInvalidSizeStr, mWhichSize); break; diff --git a/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt b/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt index 6be5c207f28..78a4a6bfe54 100644 --- a/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt +++ b/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt @@ -33,6 +33,7 @@ import com.android.settings.Settings.NotificationAppListActivity import com.android.settings.Settings.NotificationReviewPermissionsActivity import com.android.settings.Settings.OverlaySettingsActivity import com.android.settings.Settings.StorageUseActivity +import com.android.settings.Settings.TurnScreenOnSettingsActivity import com.android.settings.Settings.UsageAccessSettingsActivity import com.android.settings.Settings.WriteSettingsActivity import com.android.settings.applications.appinfo.AppLocaleDetails @@ -51,6 +52,7 @@ import com.android.settings.applications.manageapplications.ManageApplications.L import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_NOTIFICATION import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_OVERLAY import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_STORAGE +import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_TURN_SCREEN_ON import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_USAGE_ACCESS import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_WIFI_ACCESS import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_WRITE_SETTINGS @@ -88,6 +90,7 @@ object ManageApplicationsUtil { LongBackgroundTasksActivity::class to LIST_TYPE_LONG_BACKGROUND_TASKS, ClonedAppsListActivity::class to LIST_TYPE_CLONED_APPS, ChangeNfcTagAppsActivity::class to LIST_TYPE_NFC_TAG_APPS, + TurnScreenOnSettingsActivity::class to LIST_TYPE_TURN_SCREEN_ON, ) @JvmField diff --git a/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnSettings.java b/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnSettings.java deleted file mode 100644 index 742831e4716..00000000000 --- a/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnSettings.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.settings.applications.specialaccess.turnscreenon; - -import android.Manifest; -import android.annotation.Nullable; -import android.app.AppOpsManager; -import android.app.settings.SettingsEnums; -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.UserInfo; -import android.os.Bundle; -import android.os.UserHandle; -import android.os.UserManager; -import android.util.IconDrawableFactory; -import android.util.Pair; -import android.view.View; - -import androidx.annotation.VisibleForTesting; -import androidx.preference.Preference; -import androidx.preference.Preference.OnPreferenceClickListener; -import androidx.preference.PreferenceScreen; - -import com.android.settings.R; -import com.android.settings.Utils; -import com.android.settings.applications.AppInfoBase; -import com.android.settings.search.BaseSearchIndexProvider; -import com.android.settings.widget.EmptyTextSettings; -import com.android.settingslib.search.SearchIndexable; -import com.android.settingslib.widget.AppPreference; - -import java.text.Collator; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; - -/** - * Settings page for providing special app access to turn the screen of the device on. - */ -@SearchIndexable -public class TurnScreenOnSettings extends EmptyTextSettings { - - @VisibleForTesting - static final List IGNORE_PACKAGE_LIST = new ArrayList<>(); - - static { - IGNORE_PACKAGE_LIST.add(Utils.SYSTEMUI_PACKAGE_NAME); - } - - /** - * Comparator by name, then user id. - * {@see PackageItemInfo#DisplayNameComparator} - */ - static class AppComparator implements Comparator> { - - private final Collator mCollator = Collator.getInstance(); - private final PackageManager mPm; - - AppComparator(PackageManager pm) { - mPm = pm; - } - - public final int compare(Pair a, - Pair b) { - CharSequence sa = a.first.loadLabel(mPm); - if (sa == null) sa = a.first.name; - CharSequence sb = b.first.loadLabel(mPm); - if (sb == null) sb = b.first.name; - int nameCmp = mCollator.compare(sa.toString(), sb.toString()); - if (nameCmp != 0) { - return nameCmp; - } else { - return a.second - b.second; - } - } - } - - private AppOpsManager mAppOpsManager; - private Context mContext; - private PackageManager mPackageManager; - private UserManager mUserManager; - private IconDrawableFactory mIconDrawableFactory; - - public TurnScreenOnSettings() { - // Do nothing - } - - public TurnScreenOnSettings(PackageManager pm, UserManager um) { - mPackageManager = pm; - mUserManager = um; - } - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - - mContext = getActivity(); - mPackageManager = mContext.getPackageManager(); - mUserManager = mContext.getSystemService(UserManager.class); - mAppOpsManager = mContext.getSystemService(AppOpsManager.class); - mIconDrawableFactory = IconDrawableFactory.newInstance(mContext); - } - - @Override - public void onResume() { - super.onResume(); - - // Clear the prefs - final PreferenceScreen screen = getPreferenceScreen(); - screen.removeAll(); - - // Fetch the set of applications for each profile which have the permission required to turn - // the screen on with a wake lock. - final ArrayList> apps = collectTurnScreenOnApps( - UserHandle.myUserId()); - apps.sort(new AppComparator(mPackageManager)); - - // Rebuild the list of prefs - final Context prefContext = getPrefContext(); - for (final Pair appData : apps) { - final ApplicationInfo appInfo = appData.first; - final int userId = appData.second; - final UserHandle user = UserHandle.of(userId); - final String packageName = appInfo.packageName; - final CharSequence label = appInfo.loadLabel(mPackageManager); - - final Preference pref = new AppPreference(prefContext); - pref.setIcon(mIconDrawableFactory.getBadgedIcon(appInfo, userId)); - pref.setTitle(mPackageManager.getUserBadgedLabel(label, user)); - pref.setSummary(TurnScreenOnDetails.getPreferenceSummary(mAppOpsManager, - appInfo.uid, packageName)); - pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - AppInfoBase.startAppInfoFragment(TurnScreenOnDetails.class, - getString(R.string.turn_screen_on_title), - packageName, appInfo.uid, - TurnScreenOnSettings.this, -1, getMetricsCategory()); - return true; - } - }); - screen.addPreference(pref); - } - } - - @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setEmptyText(R.string.no_applications); - } - - @Override - protected int getPreferenceScreenResId() { - return R.xml.turn_screen_on_settings; - } - - @Override - public int getMetricsCategory() { - return SettingsEnums.SETTINGS_TURN_SCREEN_ON_ACCESS; - } - - /** - * @return the list of applications for the given user and all their profiles that can turn on - * the screen with wake locks. - */ - @VisibleForTesting - ArrayList> collectTurnScreenOnApps(int userId) { - final ArrayList> apps = new ArrayList<>(); - final ArrayList userIds = new ArrayList<>(); - for (UserInfo user : mUserManager.getProfiles(userId)) { - userIds.add(user.id); - } - - for (int id : userIds) { - final List installedPackages = mPackageManager.getInstalledPackagesAsUser( - /* flags= */ 0, id); - for (PackageInfo packageInfo : installedPackages) { - if (hasTurnScreenOnPermission(mPackageManager, packageInfo.packageName)) { - apps.add(new Pair<>(packageInfo.applicationInfo, id)); - } - } - } - return apps; - } - - /** - * @return true if the package has the permission to turn the screen on. - */ - @VisibleForTesting - static boolean hasTurnScreenOnPermission(PackageManager packageManager, String packageName) { - if (IGNORE_PACKAGE_LIST.contains(packageName)) { - return false; - } - return packageManager.checkPermission(Manifest.permission.TURN_SCREEN_ON, packageName) - == PackageManager.PERMISSION_GRANTED; - } - - public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = - new BaseSearchIndexProvider(R.xml.turn_screen_on_settings); -} diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java index 708f317994e..9db4ddd8d45 100644 --- a/src/com/android/settings/core/gateway/SettingsGateway.java +++ b/src/com/android/settings/core/gateway/SettingsGateway.java @@ -50,6 +50,7 @@ import com.android.settings.applications.appinfo.ExternalSourcesDetails; import com.android.settings.applications.appinfo.LongBackgroundTasksDetails; import com.android.settings.applications.appinfo.ManageExternalStorageDetails; import com.android.settings.applications.appinfo.MediaManagementAppsDetails; +import com.android.settings.applications.appinfo.TurnScreenOnDetails; import com.android.settings.applications.appinfo.WriteSettingsDetails; import com.android.settings.applications.appops.BackgroundCheckSummary; import com.android.settings.applications.assist.ManageAssist; @@ -62,8 +63,6 @@ import com.android.settings.applications.specialaccess.notificationaccess.Notifi import com.android.settings.applications.specialaccess.pictureinpicture.PictureInPictureDetails; import com.android.settings.applications.specialaccess.pictureinpicture.PictureInPictureSettings; import com.android.settings.applications.specialaccess.premiumsms.PremiumSmsAccess; -import com.android.settings.applications.specialaccess.turnscreenon.TurnScreenOnDetails; -import com.android.settings.applications.specialaccess.turnscreenon.TurnScreenOnSettings; import com.android.settings.applications.specialaccess.vrlistener.VrListenerSettings; import com.android.settings.applications.specialaccess.zenaccess.ZenAccessDetails; import com.android.settings.backup.PrivacySettings; @@ -366,7 +365,6 @@ public class SettingsGateway { OneHandedSettings.class.getName(), MobileNetworkSettings.class.getName(), AppLocaleDetails.class.getName(), - TurnScreenOnSettings.class.getName(), TurnScreenOnDetails.class.getName(), NfcAndPaymentFragment.class.getName(), ColorAndMotionFragment.class.getName(), diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/TurnScreenOnDetailsTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/TurnScreenOnDetailsTest.java new file mode 100644 index 00000000000..180b8058b69 --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/appinfo/TurnScreenOnDetailsTest.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.applications.appinfo; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; + +import androidx.preference.SwitchPreference; + +import com.android.settings.applications.AppStateAppOpsBridge; +import com.android.settings.applications.AppStateTurnScreenOnBridge; + +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.util.ReflectionHelpers; + +@RunWith(RobolectricTestRunner.class) +public class TurnScreenOnDetailsTest { + + @Mock + private SwitchPreference mSwitchPref; + @Mock + private PackageInfo mPackageInfo; + @Mock + private AppStateTurnScreenOnBridge mAppStateBridge; + @Mock + private AppStateAppOpsBridge.PermissionState mPermissionState; + + private final TurnScreenOnDetails mFragment = new TurnScreenOnDetails(); + + + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + ReflectionHelpers.setField(mFragment, "mSwitchPref", mSwitchPref); + ReflectionHelpers.setField(mFragment, "mAppBridge", mAppStateBridge); + } + + @Test + public void refreshUi_noPackageInfo_shouldReturnFalseAndNoCrash() { + mFragment.refreshUi(); + + assertThat(mFragment.refreshUi()).isFalse(); + // should not crash + } + + @Test + public void refreshUi_noApplicationInfo_shouldReturnFalseAndNoCrash() { + ReflectionHelpers.setField(mFragment, "mPackageInfo", mPackageInfo); + + mFragment.refreshUi(); + + assertThat(mFragment.refreshUi()).isFalse(); + // should not crash + } + + @Test + public void refreshUi_hasApplicationInfo_shouldReturnTrue() { + ReflectionHelpers.setField(mFragment, "mPackageInfo", mPackageInfo); + mPackageInfo.applicationInfo = new ApplicationInfo(); + when(mAppStateBridge.getPermissionInfo(nullable(String.class), anyInt())) + .thenReturn(mPermissionState); + + mFragment.refreshUi(); + + assertThat(mFragment.refreshUi()).isTrue(); + } + + @Test + public void refreshUi_permissionNotDeclared_switchPreferenceDisabled() { + ReflectionHelpers.setField(mFragment, "mPackageInfo", mPackageInfo); + mPackageInfo.applicationInfo = new ApplicationInfo(); + when(mAppStateBridge.getPermissionInfo(nullable(String.class), anyInt())) + .thenReturn(mPermissionState); + mPermissionState.permissionDeclared = false; + + mFragment.refreshUi(); + verify(mSwitchPref).setEnabled(false); + } + + @Test + public void refreshUi_permissionDeclared_switchPreferenceEnabled() { + ReflectionHelpers.setField(mFragment, "mPackageInfo", mPackageInfo); + mPackageInfo.applicationInfo = new ApplicationInfo(); + when(mAppStateBridge.getPermissionInfo(nullable(String.class), anyInt())) + .thenReturn(mPermissionState); + mPermissionState.permissionDeclared = true; + + mFragment.refreshUi(); + verify(mSwitchPref).setEnabled(true); + } + + @Test + public void refreshUi_turnScreenOnAllowed_switchPreferenceChecked() { + ReflectionHelpers.setField(mFragment, "mPackageInfo", mPackageInfo); + mPackageInfo.applicationInfo = new ApplicationInfo(); + when(mAppStateBridge.getPermissionInfo(nullable(String.class), anyInt())) + .thenReturn(mPermissionState); + + when(mPermissionState.isPermissible()).thenReturn(true); + mFragment.refreshUi(); + verify(mSwitchPref).setChecked(true); + } + + @Test + public void refreshUi_turnScreenOnDisallowed_switchPreferenceUnchecked() { + ReflectionHelpers.setField(mFragment, "mPackageInfo", mPackageInfo); + mPackageInfo.applicationInfo = new ApplicationInfo(); + when(mAppStateBridge.getPermissionInfo(nullable(String.class), anyInt())) + .thenReturn(mPermissionState); + + when(mPermissionState.isPermissible()).thenReturn(false); + mFragment.refreshUi(); + verify(mSwitchPref).setChecked(false); + } +} diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnDetailsTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnDetailsTest.java deleted file mode 100644 index c27ad2b92bf..00000000000 --- a/tests/robotests/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnDetailsTest.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.applications.specialaccess.turnscreenon; - -import static android.app.AppOpsManager.MODE_ALLOWED; -import static android.app.AppOpsManager.MODE_ERRORED; -import static android.app.AppOpsManager.OP_TURN_SCREEN_ON; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -import android.app.AppOpsManager; - -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 TurnScreenOnDetailsTest { - - private static final int UID = 0; - private static final String PACKAGE_NAME = "com.android.fake.package"; - - @Mock - private AppOpsManager mAppOpsManager; - - - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void isTurnScreenOnAllowed_appOpErrored_shouldReturnFalse() { - when(mAppOpsManager.checkOpNoThrow(eq(OP_TURN_SCREEN_ON), eq(UID), - eq(PACKAGE_NAME))).thenReturn(MODE_ERRORED); - - boolean isAllowed = TurnScreenOnDetails.isTurnScreenOnAllowed(mAppOpsManager, UID, - PACKAGE_NAME); - - assertThat(isAllowed).isFalse(); - } - - @Test - public void isTurnScreenOnAllowed_appOpAllowed_shouldReturnTrue() { - when(mAppOpsManager.checkOpNoThrow(eq(OP_TURN_SCREEN_ON), eq(UID), - eq(PACKAGE_NAME))).thenReturn(MODE_ALLOWED); - - boolean isAllowed = TurnScreenOnDetails.isTurnScreenOnAllowed(mAppOpsManager, UID, - PACKAGE_NAME); - - assertThat(isAllowed).isTrue(); - } -} diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnSettingsTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnSettingsTest.java deleted file mode 100644 index a2602110500..00000000000 --- a/tests/robotests/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnSettingsTest.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.applications.specialaccess.turnscreenon; - - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -import android.Manifest; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.UserInfo; -import android.os.UserManager; -import android.util.Pair; - -import com.android.settings.testutils.FakeFeatureFactory; - -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 java.util.ArrayList; -import java.util.List; - -@RunWith(RobolectricTestRunner.class) -public class TurnScreenOnSettingsTest { - - private static final int PRIMARY_USER_ID = 0; - private static final int PROFILE_USER_ID = 10; - - private TurnScreenOnSettings mFragment; - @Mock - private PackageManager mPackageManager; - @Mock - private UserManager mUserManager; - private ArrayList mPrimaryUserPackages; - private ArrayList mProfileUserPackages; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - FakeFeatureFactory.setupForTest(); - mFragment = new TurnScreenOnSettings(mPackageManager, mUserManager); - mPrimaryUserPackages = new ArrayList<>(); - mProfileUserPackages = new ArrayList<>(); - when(mPackageManager.getInstalledPackagesAsUser(anyInt(), eq(PRIMARY_USER_ID))) - .thenReturn(mPrimaryUserPackages); - when(mPackageManager.getInstalledPackagesAsUser(anyInt(), eq(PROFILE_USER_ID))) - .thenReturn(mProfileUserPackages); - - UserInfo primaryUserInfo = new UserInfo(); - primaryUserInfo.id = PRIMARY_USER_ID; - UserInfo profileUserInfo = new UserInfo(); - profileUserInfo.id = PROFILE_USER_ID; - - when(mUserManager.getProfiles(PRIMARY_USER_ID)) - .thenReturn(ImmutableList.of(primaryUserInfo, profileUserInfo)); - } - - @Test - public void testCollectTurnScreenOnApps_variousPackages_shouldReturnOnlyPackagesWithTurnScreenOnPermission() { - PackageInfo primaryP1 = createPackage("Calculator", true); - PackageInfo primaryP2 = createPackage("Clock", false); - PackageInfo profileP1 = createPackage("Browser", false); - PackageInfo profileP2 = createPackage("Files", true); - mPrimaryUserPackages.add(primaryP1); - mPrimaryUserPackages.add(primaryP2); - mProfileUserPackages.add(profileP1); - mProfileUserPackages.add(profileP2); - - List> apps = mFragment.collectTurnScreenOnApps( - PRIMARY_USER_ID); - - assertThat(containsPackages(apps, primaryP1, profileP2)).isTrue(); - assertThat(containsPackages(apps, primaryP2, profileP1)).isFalse(); - } - - @Test - public void collectTurnScreenOnApps_noTurnScreenOnPackages_shouldReturnEmptyList() { - PackageInfo primaryP1 = createPackage("Calculator", false); - PackageInfo profileP1 = createPackage("Browser", false); - mPrimaryUserPackages.add(primaryP1); - mProfileUserPackages.add(profileP1); - - List> apps = mFragment.collectTurnScreenOnApps( - PRIMARY_USER_ID); - - assertThat(apps).isEmpty(); - } - - @Test - public void sort_multiplePackages_appsShouldBeOrderedByAppName() { - PackageInfo primaryP1 = createPackage("Android", true); - PackageInfo primaryP2 = createPackage("Boop", true); - PackageInfo primaryP3 = createPackage("Deck", true); - PackageInfo profileP1 = createPackage("Android", true); - PackageInfo profileP2 = createPackage("Cool", true); - PackageInfo profileP3 = createPackage("Fast", false); - mPrimaryUserPackages.add(primaryP1); - mPrimaryUserPackages.add(primaryP2); - mPrimaryUserPackages.add(primaryP3); - mProfileUserPackages.add(profileP1); - mProfileUserPackages.add(profileP2); - mProfileUserPackages.add(profileP3); - List> apps = mFragment.collectTurnScreenOnApps( - PRIMARY_USER_ID); - - apps.sort(new TurnScreenOnSettings.AppComparator(null)); - - assertThat(isOrdered(apps, primaryP1, profileP1, primaryP2, profileP2, primaryP3)).isTrue(); - } - - @Test - public void hasTurnScreenOnPermission_ignoredPackages_shouldReturnFalse() { - boolean res = false; - - for (String ignoredPackage : TurnScreenOnSettings.IGNORE_PACKAGE_LIST) { - res |= TurnScreenOnSettings.hasTurnScreenOnPermission(mPackageManager, ignoredPackage); - } - - assertThat(res).isFalse(); - } - - private boolean containsPackages(List> apps, - PackageInfo... packages) { - for (PackageInfo aPackage : packages) { - boolean found = false; - for (Pair app : apps) { - if (app.first == aPackage.applicationInfo) { - found = true; - break; - } - } - if (!found) { - return false; - } - } - return true; - } - - private boolean isOrdered(List> apps, PackageInfo... packages) { - if (apps.size() != packages.length) { - return false; - } - - for (int i = 0; i < packages.length; i++) { - if (packages[i].applicationInfo != apps.get(i).first) { - return false; - } - } - return true; - } - - private PackageInfo createPackage(String packageName, boolean hasTurnScreenOnPermission) { - PackageInfo pi = new PackageInfo(); - when(mPackageManager.checkPermission(Manifest.permission.TURN_SCREEN_ON, - packageName)).thenReturn( - hasTurnScreenOnPermission ? PackageManager.PERMISSION_GRANTED - : PackageManager.PERMISSION_DENIED); - pi.packageName = packageName; - pi.applicationInfo = new ApplicationInfo(); - pi.applicationInfo.name = packageName; - return pi; - } -} diff --git a/tests/unit/src/com/android/settings/applications/manageapplications/AppFilterRegistryTest.java b/tests/unit/src/com/android/settings/applications/manageapplications/AppFilterRegistryTest.java index ff5c0d860cf..023fc0f548b 100644 --- a/tests/unit/src/com/android/settings/applications/manageapplications/AppFilterRegistryTest.java +++ b/tests/unit/src/com/android/settings/applications/manageapplications/AppFilterRegistryTest.java @@ -23,6 +23,7 @@ import static com.android.settings.applications.manageapplications.AppFilterRegi import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_MEDIA_MANAGEMENT; import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_POWER_ALLOWLIST; import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_RECENT; +import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_TURN_SCREEN_ON; import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_USAGE_ACCESS; import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_WITH_OVERLAY; import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_WRITE_SETTINGS; @@ -38,6 +39,7 @@ import static com.android.settings.applications.manageapplications.ManageApplica import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_NOTIFICATION; import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_OVERLAY; import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_STORAGE; +import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_TURN_SCREEN_ON; import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_USAGE_ACCESS; import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_WRITE_SETTINGS; @@ -82,5 +84,8 @@ public class AppFilterRegistryTest { assertThat(registry.getDefaultFilterType(LIST_TYPE_LONG_BACKGROUND_TASKS)) .isEqualTo(FILTER_LONG_BACKGROUND_TASKS); + + assertThat(registry.getDefaultFilterType(LIST_TYPE_TURN_SCREEN_ON)).isEqualTo( + FILTER_APPS_TURN_SCREEN_ON); } } From 5e987f49dd7f1bb3d54c17730c5cee4f2d1f7ed7 Mon Sep 17 00:00:00 2001 From: Zoey Chen Date: Tue, 18 Apr 2023 08:21:21 +0000 Subject: [PATCH 7/9] [Settings] Update the footer link in Regional Preferences Bug: 277573274 Test: local test Change-Id: I0c02cee71365f98d89d98e4147e1eb5245b7ede9 --- res/values/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index eb53143aa82..e02d2a75762 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -431,8 +431,8 @@ If an app doesn’t support regional preferences, the app will use its default locale settings. Learn more about language preferences. - - https://support.google.com/android + + https://support.google.com/android?p=regional_preferences {count, plural, From d965ff30493f58da1aa8d88fa5bfbf571b047c67 Mon Sep 17 00:00:00 2001 From: Weng Su Date: Mon, 17 Apr 2023 12:20:42 +0800 Subject: [PATCH 8/9] Fallback to "Extended Compatibility" if Speed feature is not ready - Fallback to the "Extended Compatibility" design when the following conditions occur - 5 GHz band is not supported on the device - 5 GHz SAP available channels cannot be obtained from WifiManager - 6 GHz SAP available channels cannot be obtained from WifiManager Bug: 272450463 Test: manual test atest -c WifiTetherSettingsTest atest -c WifiTetherViewModelTest \ WifiHotspotRepositoryTest \ WifiTetherSecurityPreferenceControllerTest.java \ WifiTetherMaximizeCompatibilityPreferenceControllerTest Change-Id: If7c8c41ebe86f5e7d8e4737ab7a82d38c9d633de --- .../repository/WifiHotspotRepository.java | 65 ++++++++++++++ ...mizeCompatibilityPreferenceController.java | 18 ++++ ...ifiTetherSecurityPreferenceController.java | 18 ++++ .../wifi/tether/WifiTetherSettings.java | 18 +++- .../wifi/tether/WifiTetherViewModel.java | 17 +++- .../wifi/tether/WifiTetherSettingsTest.java | 89 +++++++++++++------ .../repository/WifiHotspotRepositoryTest.java | 60 +++++++++++++ ...CompatibilityPreferenceControllerTest.java | 7 ++ ...etherSecurityPreferenceControllerTest.java | 7 ++ .../wifi/tether/WifiTetherViewModelTest.java | 12 ++- 10 files changed, 278 insertions(+), 33 deletions(-) diff --git a/src/com/android/settings/wifi/repository/WifiHotspotRepository.java b/src/com/android/settings/wifi/repository/WifiHotspotRepository.java index 91de4821815..c96896f7aa6 100644 --- a/src/com/android/settings/wifi/repository/WifiHotspotRepository.java +++ b/src/com/android/settings/wifi/repository/WifiHotspotRepository.java @@ -32,9 +32,11 @@ import android.text.TextUtils; import android.util.Log; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; +import com.android.settings.R; import com.android.settings.overlay.FeatureFactory; import java.util.HashMap; @@ -96,6 +98,10 @@ public class WifiHotspotRepository { protected String mCurrentCountryCode; protected ActiveCountryCodeChangedCallback mActiveCountryCodeChangedCallback; + @VisibleForTesting + Boolean mIsConfigShowSpeed; + private Boolean mIsSpeedFeatureAvailable; + public WifiHotspotRepository(@NonNull Context appContext, @NonNull WifiManager wifiManager) { mAppContext = appContext; mWifiManager = wifiManager; @@ -314,6 +320,7 @@ public class WifiHotspotRepository { /** * Return whether Wi-Fi Dual Band is supported or not. + * * @return {@code true} if Wi-Fi Dual Band is supported */ public boolean isDualBand() { @@ -326,6 +333,7 @@ public class WifiHotspotRepository { /** * Return whether Wi-Fi 5 GHz band is supported or not. + * * @return {@code true} if Wi-Fi 5 GHz Band is supported */ public boolean is5GHzBandSupported() { @@ -338,6 +346,7 @@ public class WifiHotspotRepository { /** * Return whether Wi-Fi Hotspot 5 GHz band is available or not. + * * @return {@code true} if Wi-Fi Hotspot 5 GHz Band is available */ public boolean is5gAvailable() { @@ -371,6 +380,7 @@ public class WifiHotspotRepository { /** * Return whether Wi-Fi 6 GHz band is supported or not. + * * @return {@code true} if Wi-Fi 6 GHz Band is supported */ public boolean is6GHzBandSupported() { @@ -383,6 +393,7 @@ public class WifiHotspotRepository { /** * Return whether Wi-Fi Hotspot 6 GHz band is available or not. + * * @return {@code true} if Wi-Fi Hotspot 6 GHz Band is available */ public boolean is6gAvailable() { @@ -432,9 +443,63 @@ public class WifiHotspotRepository { // This is expected on some hardware. Log.e(TAG, "Querying usable SAP channels is unsupported, band:" + band); } + // Disable Wi-Fi hotspot speed feature if an error occurs while getting usable channels. + mIsSpeedFeatureAvailable = false; + Log.w(TAG, "isChannelAvailable(): Wi-Fi hotspot speed feature disabled"); return defaultValue; } + private boolean isConfigShowSpeed() { + if (mIsConfigShowSpeed == null) { + mIsConfigShowSpeed = mAppContext.getResources() + .getBoolean(R.bool.config_show_wifi_hotspot_speed); + log("isConfigShowSpeed():" + mIsConfigShowSpeed); + } + return mIsConfigShowSpeed; + } + + /** + * Return whether Wi-Fi Hotspot Speed Feature is available or not. + * + * @return {@code true} if Wi-Fi Hotspot Speed Feature is available + */ + public boolean isSpeedFeatureAvailable() { + if (mIsSpeedFeatureAvailable != null) { + return mIsSpeedFeatureAvailable; + } + + // Check config to show Wi-Fi hotspot speed feature + if (!isConfigShowSpeed()) { + mIsSpeedFeatureAvailable = false; + log("isSpeedFeatureAvailable():false, isConfigShowSpeed():false"); + return false; + } + + // Check if 5 GHz band is not supported + if (!is5GHzBandSupported()) { + mIsSpeedFeatureAvailable = false; + log("isSpeedFeatureAvailable():false, 5 GHz band is not supported on this device"); + return false; + } + // Check if 5 GHz band SAP channel is not ready + isChannelAvailable(WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS, true /* defaultValue */); + if (mIsSpeedFeatureAvailable != null && !mIsSpeedFeatureAvailable) { + log("isSpeedFeatureAvailable():false, error occurred while getting 5 GHz SAP channel"); + return false; + } + + // Check if 6 GHz band SAP channel is not ready + isChannelAvailable(WifiScanner.WIFI_BAND_6_GHZ, false /* defaultValue */); + if (mIsSpeedFeatureAvailable != null && !mIsSpeedFeatureAvailable) { + log("isSpeedFeatureAvailable():false, error occurred while getting 6 GHz SAP channel"); + return false; + } + + mIsSpeedFeatureAvailable = true; + log("isSpeedFeatureAvailable():true"); + return true; + } + protected void purgeRefreshData() { mIs5gAvailable = null; mIs6gAvailable = null; diff --git a/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceController.java index a1a10ea643a..448a2a30cc4 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceController.java +++ b/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceController.java @@ -25,6 +25,7 @@ import androidx.preference.Preference; import androidx.preference.SwitchPreference; import com.android.settings.R; +import com.android.settings.overlay.FeatureFactory; /** * This controller helps to manage the state of maximize compatibility switch preference. @@ -36,13 +37,30 @@ public class WifiTetherMaximizeCompatibilityPreferenceController extends public static final String PREF_KEY = "wifi_tether_maximize_compatibility"; private boolean mIsChecked; + @VisibleForTesting + boolean mShouldHidePreference; public WifiTetherMaximizeCompatibilityPreferenceController(Context context, WifiTetherBasePreferenceController.OnTetherConfigUpdateListener listener) { super(context, listener); + // If the Wi-Fi Hotspot Speed Feature available, then hide this controller. + mShouldHidePreference = FeatureFactory.getFactory(context) + .getWifiFeatureProvider().getWifiHotspotRepository().isSpeedFeatureAvailable(); + Log.d(TAG, "mShouldHidePreference:" + mShouldHidePreference); + if (mShouldHidePreference) { + return; + } mIsChecked = isMaximizeCompatibilityEnabled(); } + @Override + public boolean isAvailable() { + if (mShouldHidePreference) { + return false; + } + return super.isAvailable(); + } + @Override public String getPreferenceKey() { return PREF_KEY; diff --git a/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceController.java index 286b023f757..9a9be989de9 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceController.java +++ b/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceController.java @@ -32,6 +32,7 @@ import androidx.preference.Preference; import com.android.settings.R; import com.android.settings.core.FeatureFlags; +import com.android.settings.overlay.FeatureFactory; import java.util.LinkedHashMap; import java.util.Map; @@ -48,10 +49,19 @@ public class WifiTetherSecurityPreferenceController extends WifiTetherBasePrefer private int mSecurityValue; @VisibleForTesting boolean mIsWpa3Supported = true; + @VisibleForTesting + boolean mShouldHidePreference; public WifiTetherSecurityPreferenceController(Context context, OnTetherConfigUpdateListener listener) { super(context, listener); + // If the Wi-Fi Hotspot Speed Feature available, then hide this controller. + mShouldHidePreference = FeatureFactory.getFactory(context) + .getWifiFeatureProvider().getWifiHotspotRepository().isSpeedFeatureAvailable(); + Log.d(TAG, "shouldHidePreference():" + mShouldHidePreference); + if (mShouldHidePreference) { + return; + } final String[] securityNames = mContext.getResources().getStringArray( R.array.wifi_tether_security); final String[] securityValues = mContext.getResources().getStringArray( @@ -62,6 +72,14 @@ public class WifiTetherSecurityPreferenceController extends WifiTetherBasePrefer mWifiManager.registerSoftApCallback(context.getMainExecutor(), this); } + @Override + public boolean isAvailable() { + if (mShouldHidePreference) { + return false; + } + return super.isAvailable(); + } + @Override public String getPreferenceKey() { return FeatureFlagUtils.isEnabled(mContext, FeatureFlags.TETHER_ALL_IN_ONE) diff --git a/src/com/android/settings/wifi/tether/WifiTetherSettings.java b/src/com/android/settings/wifi/tether/WifiTetherSettings.java index bdb1a2e6e51..174ccb01c43 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherSettings.java +++ b/src/com/android/settings/wifi/tether/WifiTetherSettings.java @@ -136,12 +136,22 @@ public class WifiTetherSettings extends RestrictedDashboardFragment mWifiTetherViewModel = FeatureFactory.getFactory(getContext()).getWifiFeatureProvider() .getWifiTetherViewModel(this); - mWifiHotspotSecurity = findPreference(KEY_WIFI_HOTSPOT_SECURITY); - if (mWifiHotspotSecurity != null && mWifiHotspotSecurity.isVisible()) { - mWifiTetherViewModel.getSecuritySummary().observe(this, this::onSecuritySummaryChanged); + if (mWifiTetherViewModel != null) { + setupSpeedFeature(mWifiTetherViewModel.isSpeedFeatureAvailable()); } + } + + @VisibleForTesting + void setupSpeedFeature(boolean isSpeedFeatureAvailable) { + mWifiHotspotSecurity = findPreference(KEY_WIFI_HOTSPOT_SECURITY); mWifiHotspotSpeed = findPreference(KEY_WIFI_HOTSPOT_SPEED); - if (mWifiHotspotSpeed != null && mWifiHotspotSpeed.isVisible()) { + if (mWifiHotspotSecurity == null || mWifiHotspotSpeed == null) { + return; + } + mWifiHotspotSecurity.setVisible(isSpeedFeatureAvailable); + mWifiHotspotSpeed.setVisible(isSpeedFeatureAvailable); + if (isSpeedFeatureAvailable) { + mWifiTetherViewModel.getSecuritySummary().observe(this, this::onSecuritySummaryChanged); mWifiTetherViewModel.getSpeedSummary().observe(this, this::onSpeedSummaryChanged); } } diff --git a/src/com/android/settings/wifi/tether/WifiTetherViewModel.java b/src/com/android/settings/wifi/tether/WifiTetherViewModel.java index 6bb2cd5ef4d..dd4ca28999c 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherViewModel.java +++ b/src/com/android/settings/wifi/tether/WifiTetherViewModel.java @@ -83,8 +83,21 @@ public class WifiTetherViewModel extends AndroidViewModel { @Override protected void onCleared() { - mWifiHotspotRepository.getSecurityType().removeObserver(mSecurityTypeObserver); - mWifiHotspotRepository.getSpeedType().removeObserver(mSpeedTypeObserver); + if (mSecuritySummary != null) { + mWifiHotspotRepository.getSecurityType().removeObserver(mSecurityTypeObserver); + } + if (mSpeedSummary != null) { + mWifiHotspotRepository.getSpeedType().removeObserver(mSpeedTypeObserver); + } + } + + /** + * Return whether Wi-Fi Hotspot Speed Feature is available or not. + * + * @return {@code true} if Wi-Fi Hotspot Speed Feature is available + */ + public boolean isSpeedFeatureAvailable() { + return mWifiHotspotRepository.isSpeedFeatureAvailable(); } /** diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java index fb64023033e..e2641777d25 100644 --- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java +++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java @@ -17,6 +17,8 @@ package com.android.settings.wifi.tether; import static com.android.settings.wifi.WifiUtils.setCanShowWifiHotspotCached; +import static com.android.settings.wifi.tether.WifiTetherSettings.KEY_WIFI_HOTSPOT_SECURITY; +import static com.android.settings.wifi.tether.WifiTetherSettings.KEY_WIFI_HOTSPOT_SPEED; import static com.google.common.truth.Truth.assertThat; @@ -26,6 +28,7 @@ import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doNothing; 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; @@ -41,6 +44,7 @@ import android.util.FeatureFlagUtils; import android.widget.TextView; import androidx.fragment.app.FragmentActivity; +import androidx.lifecycle.LiveData; import androidx.lifecycle.ViewModelStoreOwner; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; @@ -79,7 +83,7 @@ public class WifiTetherSettingsTest { @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); @Spy - Context mContext = ApplicationProvider.getApplicationContext(); + private Context mContext = ApplicationProvider.getApplicationContext(); @Mock private WifiManager mWifiManager; @Mock @@ -95,11 +99,19 @@ public class WifiTetherSettingsTest { @Mock private TextView mEmptyTextView; @Mock - WifiTetherViewModel mWifiTetherViewModel; + private WifiTetherViewModel mWifiTetherViewModel; @Mock - WifiHotspotRepository mWifiHotspotRepository; + private WifiHotspotRepository mWifiHotspotRepository; + @Mock + private Preference mWifiHotspotSecurity; + @Mock + private LiveData mSecuritySummary; + @Mock + private Preference mWifiHotspotSpeed; + @Mock + private LiveData mSpeedSummary; - private WifiTetherSettings mWifiTetherSettings; + private WifiTetherSettings mSettings; @Before public void setUp() { @@ -118,19 +130,25 @@ public class WifiTetherSettingsTest { when(provider.getWifiHotspotRepository()).thenReturn(mWifiHotspotRepository); when(provider.getWifiTetherViewModel(mock(ViewModelStoreOwner.class))) .thenReturn(mWifiTetherViewModel); + when(mWifiTetherViewModel.isSpeedFeatureAvailable()).thenReturn(false); + when(mWifiTetherViewModel.getSecuritySummary()).thenReturn(mSecuritySummary); + when(mWifiTetherViewModel.getSpeedSummary()).thenReturn(mSpeedSummary); - mWifiTetherSettings = new WifiTetherSettings(mWifiRestriction); + mSettings = spy(new WifiTetherSettings(mWifiRestriction)); + mSettings.mWifiTetherViewModel = mWifiTetherViewModel; + when(mSettings.findPreference(KEY_WIFI_HOTSPOT_SECURITY)).thenReturn(mWifiHotspotSecurity); + when(mSettings.findPreference(KEY_WIFI_HOTSPOT_SPEED)).thenReturn(mWifiHotspotSpeed); } @Test @Config(shadows = ShadowRestrictedDashboardFragment.class) public void onCreate_canNotShowWifiHotspot_shouldFinish() { setCanShowWifiHotspotCached(false); - mWifiTetherSettings = spy(new WifiTetherSettings(mWifiRestriction)); + mSettings = spy(new WifiTetherSettings(mWifiRestriction)); - mWifiTetherSettings.onCreate(null); + mSettings.onCreate(null); - verify(mWifiTetherSettings).finish(); + verify(mSettings).finish(); } @Test @@ -138,7 +156,7 @@ public class WifiTetherSettingsTest { public void onStart_uiIsRestricted_removeAllPreferences() { spyWifiTetherSettings(); - mWifiTetherSettings.onStart(); + mSettings.onStart(); verify(mPreferenceScreen).removeAll(); } @@ -149,7 +167,7 @@ public class WifiTetherSettingsTest { spyWifiTetherSettings(); when(mWifiRestriction.isHotspotAvailable(mContext)).thenReturn(false); - mWifiTetherSettings.onStart(); + mSettings.onStart(); verify(mPreferenceScreen).removeAll(); verify(mEmptyTextView).setText(anyInt()); @@ -158,21 +176,21 @@ public class WifiTetherSettingsTest { @Test public void onSecuritySummaryChanged_canNotShowWifiHotspot_returnFalse() { int stringResId = R.string.wifi_security_sae; - mWifiTetherSettings.mWifiHotspotSecurity = mock(Preference.class); + mSettings.mWifiHotspotSecurity = mock(Preference.class); - mWifiTetherSettings.onSecuritySummaryChanged(stringResId); + mSettings.onSecuritySummaryChanged(stringResId); - verify(mWifiTetherSettings.mWifiHotspotSecurity).setSummary(stringResId); + verify(mSettings.mWifiHotspotSecurity).setSummary(stringResId); } @Test public void onSpeedSummaryChanged_canNotShowWifiHotspot_returnFalse() { int stringResId = R.string.wifi_hotspot_speed_summary_6g; - mWifiTetherSettings.mWifiHotspotSpeed = mock(Preference.class); + mSettings.mWifiHotspotSpeed = mock(Preference.class); - mWifiTetherSettings.onSpeedSummaryChanged(stringResId); + mSettings.onSpeedSummaryChanged(stringResId); - verify(mWifiTetherSettings.mWifiHotspotSpeed).setSummary(stringResId); + verify(mSettings.mWifiHotspotSpeed).setSummary(stringResId); } @Test @@ -183,7 +201,7 @@ public class WifiTetherSettingsTest { @Test public void createPreferenceControllers_hasAutoOffPreference() { - assertThat(mWifiTetherSettings.createPreferenceControllers(mContext) + assertThat(mSettings.createPreferenceControllers(mContext) .stream() .filter(controller -> controller instanceof WifiTetherAutoOffPreferenceController) .count()) @@ -270,23 +288,42 @@ public class WifiTetherSettingsTest { .isFalse(); } + @Test + public void setupSpeedFeature_speedFeatureIsAvailable_setVisibleToTrue() { + mSettings.setupSpeedFeature(true); + + verify(mWifiHotspotSecurity).setVisible(true); + verify(mWifiHotspotSpeed).setVisible(true); + verify(mSecuritySummary).observe(any(), any()); + verify(mSpeedSummary).observe(any(), any()); + } + + @Test + public void setupSpeedFeature_speedFeatureIsNotAvailable_setVisibleToFalse() { + mSettings.setupSpeedFeature(false); + + verify(mWifiHotspotSecurity).setVisible(false); + verify(mWifiHotspotSpeed).setVisible(false); + verify(mSecuritySummary, never()).observe(any(), any()); + verify(mSpeedSummary, never()).observe(any(), any()); + } + private void spyWifiTetherSettings() { - mWifiTetherSettings = spy(new WifiTetherSettings(mWifiRestriction)); + mSettings = spy(new WifiTetherSettings(mWifiRestriction)); final FragmentActivity activity = mock(FragmentActivity.class); - when(mWifiTetherSettings.getActivity()).thenReturn(activity); - when(mWifiTetherSettings.getContext()).thenReturn(mContext); + when(mSettings.getActivity()).thenReturn(activity); + when(mSettings.getContext()).thenReturn(mContext); final Resources.Theme theme = mContext.getTheme(); when(activity.getTheme()).thenReturn(theme); when(activity.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); - doNothing().when(mWifiTetherSettings) - .onCreatePreferences(any(Bundle.class), nullable(String.class)); + doNothing().when(mSettings).onCreatePreferences(any(Bundle.class), nullable(String.class)); final FakeFeatureFactory fakeFeatureFactory = FakeFeatureFactory.setupForTest(); - ReflectionHelpers.setField(mWifiTetherSettings, "mDashboardFeatureProvider", + ReflectionHelpers.setField(mSettings, "mDashboardFeatureProvider", fakeFeatureFactory.dashboardFeatureProvider); - ReflectionHelpers.setField(mWifiTetherSettings, "mEmptyTextView", mEmptyTextView); - doReturn(mPreferenceScreen).when(mWifiTetherSettings).getPreferenceScreen(); + ReflectionHelpers.setField(mSettings, "mEmptyTextView", mEmptyTextView); + doReturn(mPreferenceScreen).when(mSettings).getPreferenceScreen(); - mWifiTetherSettings.onCreate(Bundle.EMPTY); + mSettings.onCreate(Bundle.EMPTY); } @Implements(RestrictedDashboardFragment.class) diff --git a/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java b/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java index dbbdfec4a3c..6559c12ed1d 100644 --- a/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java +++ b/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java @@ -36,6 +36,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -592,6 +593,65 @@ public class WifiHotspotRepositoryTest { assertThat(mWifiHotspotRepository.get6gAvailable()).isNotNull(); } + @Test + public void isSpeedFeatureAvailable_configNotShow_returnFalse() { + mWifiHotspotRepository.mIsConfigShowSpeed = false; + + assertThat(mWifiHotspotRepository.isSpeedFeatureAvailable()).isFalse(); + } + + @Test + public void isSpeedFeatureAvailable_5gBandNotSupported_returnFalse() { + mWifiHotspotRepository.mIsConfigShowSpeed = true; + mWifiHotspotRepository.mIs5gBandSupported = false; + + assertThat(mWifiHotspotRepository.isSpeedFeatureAvailable()).isFalse(); + } + + @Test + public void isSpeedFeatureAvailable_throwExceptionWhenGet5gSapChannel_returnFalse() { + mWifiHotspotRepository.mIsConfigShowSpeed = true; + mWifiHotspotRepository.mIs5gBandSupported = true; + doThrow(IllegalArgumentException.class).when(mWifiManager) + .getUsableChannels(WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS, OP_MODE_SAP); + + assertThat(mWifiHotspotRepository.isSpeedFeatureAvailable()).isFalse(); + + doThrow(UnsupportedOperationException.class).when(mWifiManager) + .getUsableChannels(WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS, OP_MODE_SAP); + + assertThat(mWifiHotspotRepository.isSpeedFeatureAvailable()).isFalse(); + } + + @Test + public void isSpeedFeatureAvailable_throwExceptionWhenGet6gSapChannel_returnFalse() { + mWifiHotspotRepository.mIsConfigShowSpeed = true; + mWifiHotspotRepository.mIs5gBandSupported = true; + doReturn(Arrays.asList(new WifiAvailableChannel(FREQ_5GHZ, OP_MODE_SAP))).when(mWifiManager) + .getUsableChannels(WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS, OP_MODE_SAP); + doThrow(IllegalArgumentException.class).when(mWifiManager) + .getUsableChannels(WifiScanner.WIFI_BAND_6_GHZ, OP_MODE_SAP); + + assertThat(mWifiHotspotRepository.isSpeedFeatureAvailable()).isFalse(); + + doThrow(UnsupportedOperationException.class).when(mWifiManager) + .getUsableChannels(WifiScanner.WIFI_BAND_6_GHZ, OP_MODE_SAP); + + assertThat(mWifiHotspotRepository.isSpeedFeatureAvailable()).isFalse(); + } + + @Test + public void isSpeedFeatureAvailable_conditionsAreReady_returnTrue() { + mWifiHotspotRepository.mIsConfigShowSpeed = true; + mWifiHotspotRepository.mIs5gBandSupported = true; + doReturn(Arrays.asList(new WifiAvailableChannel(FREQ_5GHZ, OP_MODE_SAP))).when(mWifiManager) + .getUsableChannels(WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS, OP_MODE_SAP); + doReturn(Arrays.asList(new WifiAvailableChannel(FREQ_6GHZ, OP_MODE_SAP))).when(mWifiManager) + .getUsableChannels(WifiScanner.WIFI_BAND_6_GHZ, OP_MODE_SAP); + + assertThat(mWifiHotspotRepository.isSpeedFeatureAvailable()).isTrue(); + } + private void mockConfigSecurityType(int securityType) { mockConfig(securityType, SPEED_2GHZ); } diff --git a/tests/unit/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceControllerTest.java b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceControllerTest.java index 3d8b24c5f2f..529aceaadb6 100644 --- a/tests/unit/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceControllerTest.java +++ b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceControllerTest.java @@ -237,4 +237,11 @@ public class WifiTetherMaximizeCompatibilityPreferenceControllerTest { assertThat(builder.build().getBand()).isEqualTo(SoftApConfiguration.BAND_2GHZ); } + + @Test + public void isAvailable_shouldHidePreference_returnFalse() { + mController.mShouldHidePreference = true; + + assertThat(mController.isAvailable()).isFalse(); + } } diff --git a/tests/unit/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceControllerTest.java b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceControllerTest.java index c86e3e5b7a4..1ce05f8940e 100644 --- a/tests/unit/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceControllerTest.java +++ b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceControllerTest.java @@ -203,4 +203,11 @@ public class WifiTetherSecurityPreferenceControllerTest { .isEqualTo(SoftApConfiguration.SECURITY_TYPE_WPA2_PSK); assertThat(mPreference.getSummary().toString()).isEqualTo("WPA2-Personal"); } + + @Test + public void isAvailable_shouldHidePreference_returnFalse() { + mController.mShouldHidePreference = true; + + assertThat(mController.isAvailable()).isFalse(); + } } diff --git a/tests/unit/src/com/android/settings/wifi/tether/WifiTetherViewModelTest.java b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherViewModelTest.java index 4c8ce5b0baa..36da3903a61 100644 --- a/tests/unit/src/com/android/settings/wifi/tether/WifiTetherViewModelTest.java +++ b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherViewModelTest.java @@ -72,7 +72,10 @@ public class WifiTetherViewModelTest { } @Test - public void onCleared_setAutoRefreshFalse() { + public void onCleared_removeObservers() { + mViewModel.getSecuritySummary(); + mViewModel.getSpeedSummary(); + mViewModel.onCleared(); verify(mSecurityType).removeObserver(mViewModel.mSecurityTypeObserver); @@ -116,4 +119,11 @@ public class WifiTetherViewModelTest { assertThat(mViewModel.mSpeedSummary).isNotNull(); verify(mSpeedType).observeForever(mViewModel.mSpeedTypeObserver); } + + @Test + public void isSpeedFeatureAvailable_verifyRepositoryIsCalled() { + mViewModel.isSpeedFeatureAvailable(); + + verify(mWifiHotspotRepository).isSpeedFeatureAvailable(); + } } From 94f08e4ba1b07c8eee8e6e229ca58d82551932b4 Mon Sep 17 00:00:00 2001 From: Liana Kazanova Date: Tue, 18 Apr 2023 17:47:14 +0000 Subject: [PATCH 9/9] Revert "List apps which requested the TURN_SCREEN_ON appOp permi..." Revert submission 22338257-turnScreenOnPermission_pregrant Reason for revert: b/278739832 Reverted changes: /q/submissionid:22338257-turnScreenOnPermission_pregrant Change-Id: Iada276497c37c0e89a2cde8706394949d468e8e5 --- AndroidManifest.xml | 15 +- res/xml/special_access.xml | 6 +- res/xml/turn_screen_on_settings.xml | 23 ++ src/com/android/settings/Settings.java | 1 - .../AppStateTurnScreenOnBridge.java | 86 ------- .../manageapplications/AppFilterRegistry.java | 15 +- .../ManageApplications.java | 16 -- .../ManageApplicationsUtil.kt | 3 - .../turnscreenon}/TurnScreenOnDetails.java | 78 +++---- .../turnscreenon/TurnScreenOnSettings.java | 215 ++++++++++++++++++ .../core/gateway/SettingsGateway.java | 4 +- .../appinfo/TurnScreenOnDetailsTest.java | 142 ------------ .../turnscreenon/TurnScreenOnDetailsTest.java | 74 ++++++ .../TurnScreenOnSettingsTest.java | 188 +++++++++++++++ .../AppFilterRegistryTest.java | 5 - 15 files changed, 540 insertions(+), 331 deletions(-) create mode 100644 res/xml/turn_screen_on_settings.xml delete mode 100644 src/com/android/settings/applications/AppStateTurnScreenOnBridge.java rename src/com/android/settings/applications/{appinfo => specialaccess/turnscreenon}/TurnScreenOnDetails.java (50%) create mode 100644 src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnSettings.java delete mode 100644 tests/robotests/src/com/android/settings/applications/appinfo/TurnScreenOnDetailsTest.java create mode 100644 tests/robotests/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnDetailsTest.java create mode 100644 tests/robotests/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnSettingsTest.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index aa1bed5068d..6aad2afc282 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -3456,24 +3456,11 @@ + android:value="com.android.settings.applications.specialaccess.turnscreenon.TurnScreenOnSettings" /> - - - - - - - - - - - + android:fragment="com.android.settings.applications.specialaccess.turnscreenon.TurnScreenOnSettings" /> + + + diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java index 7511093c1c2..50a52aa4d1f 100644 --- a/src/com/android/settings/Settings.java +++ b/src/com/android/settings/Settings.java @@ -322,7 +322,6 @@ public class Settings extends SettingsActivity { public static class PremiumSmsAccessActivity extends SettingsActivity { /* empty */ } public static class PictureInPictureSettingsActivity extends SettingsActivity { /* empty */ } public static class TurnScreenOnSettingsActivity extends SettingsActivity { /* empty */ } - public static class AppTurnScreenOnSettingsActivity extends SettingsActivity { /* empty */ } public static class AppPictureInPictureSettingsActivity extends SettingsActivity { /* empty */ } public static class ZenAccessSettingsActivity extends SettingsActivity { /* empty */ } public static class ZenAccessDetailSettingsActivity extends SettingsActivity {} diff --git a/src/com/android/settings/applications/AppStateTurnScreenOnBridge.java b/src/com/android/settings/applications/AppStateTurnScreenOnBridge.java deleted file mode 100644 index 8a75c0bf3c1..00000000000 --- a/src/com/android/settings/applications/AppStateTurnScreenOnBridge.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.applications; - -import android.Manifest; -import android.app.AppOpsManager; -import android.content.Context; - -import com.android.settingslib.applications.ApplicationsState; -import com.android.settingslib.applications.ApplicationsState.AppEntry; -import com.android.settingslib.applications.ApplicationsState.AppFilter; - -import java.util.List; - -/** - * Connects app op info to the ApplicationsState. Extends {@link AppStateAppOpsBridge} to tailor - * to the semantics of {@link Manifest.permission#TURN_SCREEN_ON}. - * Also provides app filters that can use the info. - */ -public class AppStateTurnScreenOnBridge extends AppStateAppOpsBridge { - private static final String APP_OP_STR = AppOpsManager.OPSTR_TURN_SCREEN_ON; - private static final String[] PERMISSIONS = { - Manifest.permission.TURN_SCREEN_ON - }; - - private AppOpsManager mAppOpsManager; - - public AppStateTurnScreenOnBridge(Context context, ApplicationsState appState, - Callback callback) { - super(context, appState, callback, AppOpsManager.strOpToOp(APP_OP_STR), PERMISSIONS); - mAppOpsManager = context.getSystemService(AppOpsManager.class); - } - - @Override - protected void updateExtraInfo(AppEntry app, String pkg, int uid) { - app.extraInfo = getPermissionInfo(pkg, uid); - } - - @Override - protected void loadAllExtraInfo() { - super.loadAllExtraInfo(); - final List apps = mAppSession.getAllApps(); - for (AppEntry app : apps) { - if (app.extraInfo instanceof PermissionState) { - ((PermissionState) app.extraInfo).appOpMode = mAppOpsManager.unsafeCheckOpNoThrow( - APP_OP_STR, app.info.uid, app.info.packageName); - } - } - } - - @Override - public PermissionState getPermissionInfo(String pkg, int uid) { - PermissionState ps = super.getPermissionInfo(pkg, uid); - ps.appOpMode = mAppOpsManager.unsafeCheckOpNoThrow(APP_OP_STR, uid, pkg); - return ps; - } - - public static final AppFilter FILTER_TURN_SCREEN_ON_APPS = new AppFilter() { - - @Override - public void init() { - } - - @Override - public boolean filterApp(AppEntry info) { - // If extraInfo != null, it means that the app has declared - // Manifest.permission.TURN_SCREEN_ON and therefore it should appear on our - // list - return info.extraInfo != null; - } - }; -} diff --git a/src/com/android/settings/applications/manageapplications/AppFilterRegistry.java b/src/com/android/settings/applications/manageapplications/AppFilterRegistry.java index e7bb88dadf1..f3195921dcb 100644 --- a/src/com/android/settings/applications/manageapplications/AppFilterRegistry.java +++ b/src/com/android/settings/applications/manageapplications/AppFilterRegistry.java @@ -30,7 +30,6 @@ import com.android.settings.applications.AppStateMediaManagementAppsBridge; import com.android.settings.applications.AppStateNotificationBridge; import com.android.settings.applications.AppStateOverlayBridge; import com.android.settings.applications.AppStatePowerBridge; -import com.android.settings.applications.AppStateTurnScreenOnBridge; import com.android.settings.applications.AppStateUsageBridge; import com.android.settings.applications.AppStateWriteSettingsBridge; import com.android.settings.nfc.AppStateNfcTagAppsBridge; @@ -68,7 +67,6 @@ public class AppFilterRegistry { FILTER_LONG_BACKGROUND_TASKS, FILTER_APPS_CLONE, FILTER_APPS_NFC_TAG, - FILTER_APPS_TURN_SCREEN_ON, }) @interface FilterType {} @@ -100,9 +98,8 @@ public class AppFilterRegistry { public static final int FILTER_LONG_BACKGROUND_TASKS = 24; public static final int FILTER_APPS_CLONE = 25; public static final int FILTER_APPS_NFC_TAG = 26; - public static final int FILTER_APPS_TURN_SCREEN_ON = 27; - private static final int NUM_FILTER_ENTRIES = 28; - // Next id: 28. If you add an entry here, please change NUM_FILTER_ENTRIES. + private static final int NUM_FILTER_ENTRIES = 27; + // Next id: 27. If you add an entry here, please change NUM_FILTER_ENTRIES. private static AppFilterRegistry sRegistry; @@ -274,12 +271,6 @@ public class AppFilterRegistry { AppStateNfcTagAppsBridge.FILTER_APPS_NFC_TAG, FILTER_APPS_NFC_TAG, R.string.change_nfc_tag_apps_title); - - // Apps that are allowed to turn the screen on. - mFilters[FILTER_APPS_TURN_SCREEN_ON] = new AppFilterItem( - AppStateTurnScreenOnBridge.FILTER_TURN_SCREEN_ON_APPS, - FILTER_APPS_TURN_SCREEN_ON, - R.string.turn_screen_on_title); } public static AppFilterRegistry getInstance() { @@ -322,8 +313,6 @@ public class AppFilterRegistry { return FILTER_APPS_CLONE; case ManageApplications.LIST_TYPE_NFC_TAG_APPS: return FILTER_APPS_NFC_TAG; - case ManageApplications.LIST_TYPE_TURN_SCREEN_ON: - return FILTER_APPS_TURN_SCREEN_ON; default: return FILTER_APPS_ALL; } diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java index 16b1b6c086f..0fbbf18f0a5 100644 --- a/src/com/android/settings/applications/manageapplications/ManageApplications.java +++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java @@ -110,7 +110,6 @@ import com.android.settings.Settings.MediaManagementAppsActivity; import com.android.settings.Settings.NotificationAppListActivity; import com.android.settings.Settings.NotificationReviewPermissionsActivity; import com.android.settings.Settings.OverlaySettingsActivity; -import com.android.settings.Settings.TurnScreenOnSettingsActivity; import com.android.settings.Settings.UsageAccessSettingsActivity; import com.android.settings.Settings.WriteSettingsActivity; import com.android.settings.SettingsActivity; @@ -130,7 +129,6 @@ import com.android.settings.applications.AppStateNotificationBridge; import com.android.settings.applications.AppStateNotificationBridge.NotificationsSentState; import com.android.settings.applications.AppStateOverlayBridge; import com.android.settings.applications.AppStatePowerBridge; -import com.android.settings.applications.AppStateTurnScreenOnBridge; import com.android.settings.applications.AppStateUsageBridge; import com.android.settings.applications.AppStateUsageBridge.UsageState; import com.android.settings.applications.AppStateWriteSettingsBridge; @@ -144,7 +142,6 @@ import com.android.settings.applications.appinfo.ExternalSourcesDetails; import com.android.settings.applications.appinfo.LongBackgroundTasksDetails; import com.android.settings.applications.appinfo.ManageExternalStorageDetails; import com.android.settings.applications.appinfo.MediaManagementAppsDetails; -import com.android.settings.applications.appinfo.TurnScreenOnDetails; import com.android.settings.applications.appinfo.WriteSettingsDetails; import com.android.settings.core.InstrumentedFragment; import com.android.settings.core.SubSettingLauncher; @@ -271,7 +268,6 @@ public class ManageApplications extends InstrumentedFragment public static final int LIST_TYPE_LONG_BACKGROUND_TASKS = 16; public static final int LIST_TYPE_CLONED_APPS = 17; public static final int LIST_TYPE_NFC_TAG_APPS = 18; - public static final int LIST_TYPE_TURN_SCREEN_ON = 19; // List types that should show instant apps. public static final Set LIST_TYPES_WITH_INSTANT = new ArraySet<>(Arrays.asList( @@ -573,8 +569,6 @@ public class ManageApplications extends InstrumentedFragment return SettingsEnums.CLONED_APPS; case LIST_TYPE_NFC_TAG_APPS: return SettingsEnums.CONFIG_NFC_TAG_APP_PREF; - case LIST_TYPE_TURN_SCREEN_ON: - return SettingsEnums.SETTINGS_TURN_SCREEN_ON_ACCESS; default: return SettingsEnums.PAGE_UNKNOWN; } @@ -745,9 +739,6 @@ public class ManageApplications extends InstrumentedFragment startAppInfoFragment(ChangeNfcTagAppsStateDetails.class, R.string.change_nfc_tag_apps_title); break; - case LIST_TYPE_TURN_SCREEN_ON: - startAppInfoFragment(TurnScreenOnDetails.class, R.string.turn_screen_on_title); - break; // TODO: Figure out if there is a way where we can spin up the profile's settings // process ahead of time, to avoid a long load of data when user clicks on a managed // app. Maybe when they load the list of apps that contains managed profile apps. @@ -1075,8 +1066,6 @@ public class ManageApplications extends InstrumentedFragment screenTitle = R.string.cloned_apps_dashboard_title; } else if (className.equals(ChangeNfcTagAppsActivity.class.getName())) { screenTitle = R.string.change_nfc_tag_apps_title; - } else if (className.equals(TurnScreenOnSettingsActivity.class.getName())) { - screenTitle = R.string.turn_screen_on_title; } else { if (screenTitle == -1) { screenTitle = R.string.all_apps; @@ -1287,8 +1276,6 @@ public class ManageApplications extends InstrumentedFragment mExtraInfoBridge = new AppStateClonedAppsBridge(mContext, mState, this); } else if (mManageApplications.mListType == LIST_TYPE_NFC_TAG_APPS) { mExtraInfoBridge = new AppStateNfcTagAppsBridge(mContext, mState, this); - } else if (mManageApplications.mListType == LIST_TYPE_TURN_SCREEN_ON) { - mExtraInfoBridge = new AppStateTurnScreenOnBridge(mContext, mState, this); } else { mExtraInfoBridge = null; } @@ -1843,9 +1830,6 @@ public class ManageApplications extends InstrumentedFragment holder.setSummary( ChangeNfcTagAppsStateDetails.getSummary(mContext, entry)); break; - case LIST_TYPE_TURN_SCREEN_ON: - holder.setSummary(TurnScreenOnDetails.getSummary(mContext, entry)); - break; default: holder.updateSizeText(entry, mManageApplications.mInvalidSizeStr, mWhichSize); break; diff --git a/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt b/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt index 78a4a6bfe54..6be5c207f28 100644 --- a/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt +++ b/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt @@ -33,7 +33,6 @@ import com.android.settings.Settings.NotificationAppListActivity import com.android.settings.Settings.NotificationReviewPermissionsActivity import com.android.settings.Settings.OverlaySettingsActivity import com.android.settings.Settings.StorageUseActivity -import com.android.settings.Settings.TurnScreenOnSettingsActivity import com.android.settings.Settings.UsageAccessSettingsActivity import com.android.settings.Settings.WriteSettingsActivity import com.android.settings.applications.appinfo.AppLocaleDetails @@ -52,7 +51,6 @@ import com.android.settings.applications.manageapplications.ManageApplications.L import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_NOTIFICATION import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_OVERLAY import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_STORAGE -import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_TURN_SCREEN_ON import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_USAGE_ACCESS import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_WIFI_ACCESS import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_WRITE_SETTINGS @@ -90,7 +88,6 @@ object ManageApplicationsUtil { LongBackgroundTasksActivity::class to LIST_TYPE_LONG_BACKGROUND_TASKS, ClonedAppsListActivity::class to LIST_TYPE_CLONED_APPS, ChangeNfcTagAppsActivity::class to LIST_TYPE_NFC_TAG_APPS, - TurnScreenOnSettingsActivity::class to LIST_TYPE_TURN_SCREEN_ON, ) @JvmField diff --git a/src/com/android/settings/applications/appinfo/TurnScreenOnDetails.java b/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnDetails.java similarity index 50% rename from src/com/android/settings/applications/appinfo/TurnScreenOnDetails.java rename to src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnDetails.java index 39e1a5fa2ad..4540ab9863a 100644 --- a/src/com/android/settings/applications/appinfo/TurnScreenOnDetails.java +++ b/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnDetails.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * 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. @@ -13,16 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.settings.applications.appinfo; +package com.android.settings.applications.specialaccess.turnscreenon; -import static android.app.Activity.RESULT_CANCELED; -import static android.app.Activity.RESULT_OK; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.MODE_ERRORED; +import static android.app.AppOpsManager.OP_TURN_SCREEN_ON; import android.app.AppOpsManager; import android.app.settings.SettingsEnums; -import android.content.Context; import android.os.Bundle; import androidx.appcompat.app.AlertDialog; @@ -31,51 +29,41 @@ import androidx.preference.Preference.OnPreferenceChangeListener; import androidx.preference.SwitchPreference; import com.android.settings.R; -import com.android.settings.Settings; import com.android.settings.applications.AppInfoWithHeader; -import com.android.settings.applications.AppStateAppOpsBridge; -import com.android.settings.applications.AppStateTurnScreenOnBridge; -import com.android.settingslib.applications.ApplicationsState; /** * Detail page for turn screen on special app access. */ -public class TurnScreenOnDetails extends AppInfoWithHeader implements OnPreferenceChangeListener { +public class TurnScreenOnDetails extends AppInfoWithHeader + implements OnPreferenceChangeListener { private static final String KEY_APP_OPS_SETTINGS_SWITCH = "app_ops_settings_switch"; - private AppStateTurnScreenOnBridge mAppBridge; - private AppOpsManager mAppOpsManager; private SwitchPreference mSwitchPref; - private AppStateAppOpsBridge.PermissionState mPermissionState; - + private AppOpsManager mAppOpsManager; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - final Context context = getActivity(); - mAppBridge = new AppStateTurnScreenOnBridge(context, mState, /*callback=*/ null); - mAppOpsManager = context.getSystemService(AppOpsManager.class); + mAppOpsManager = this.getSystemService(AppOpsManager.class); // find preferences addPreferencesFromResource(R.xml.turn_screen_on_permissions_details); mSwitchPref = (SwitchPreference) findPreference(KEY_APP_OPS_SETTINGS_SWITCH); + + // set title/summary for all of them + mSwitchPref.setTitle(R.string.allow_turn_screen_on); + + // install event listeners mSwitchPref.setOnPreferenceChangeListener(this); } @Override public boolean onPreferenceChange(Preference preference, Object newValue) { - final boolean checked = (Boolean) newValue; if (preference == mSwitchPref) { - if (mPermissionState != null && checked != mPermissionState.isPermissible()) { - if (Settings.TurnScreenOnSettingsActivity.class.getName().equals( - getIntent().getComponent().getClassName())) { - setResult(checked ? RESULT_OK : RESULT_CANCELED); - } - setCanTurnScreenOn(checked); - refreshUi(); - } + setTurnScreenOnAppOp(mPackageInfo.applicationInfo.uid, mPackageName, + (Boolean) newValue); return true; } return false; @@ -83,13 +71,9 @@ public class TurnScreenOnDetails extends AppInfoWithHeader implements OnPreferen @Override protected boolean refreshUi() { - if (mPackageInfo == null || mPackageInfo.applicationInfo == null) { - return false; - } - mPermissionState = mAppBridge.getPermissionInfo(mPackageName, - mPackageInfo.applicationInfo.uid); - mSwitchPref.setEnabled(mPermissionState.permissionDeclared); - mSwitchPref.setChecked(mPermissionState.isPermissible()); + boolean isAllowed = isTurnScreenOnAllowed(mAppOpsManager, + mPackageInfo.applicationInfo.uid, mPackageName); + mSwitchPref.setChecked(isAllowed); return true; } @@ -107,24 +91,28 @@ public class TurnScreenOnDetails extends AppInfoWithHeader implements OnPreferen * Sets whether the app associated with the given {@code packageName} is allowed to turn the * screen on. */ - void setCanTurnScreenOn(boolean newState) { - mAppOpsManager.setUidMode(AppOpsManager.OPSTR_TURN_SCREEN_ON, - mPackageInfo.applicationInfo.uid, newState ? MODE_ALLOWED : MODE_ERRORED); + void setTurnScreenOnAppOp(int uid, String packageName, boolean value) { + final int newMode = value ? MODE_ALLOWED : MODE_ERRORED; + mAppOpsManager.setMode(OP_TURN_SCREEN_ON, uid, packageName, newMode); + } + + /** + * @return whether the app associated with the given {@code packageName} is allowed to turn the + * screen on. + */ + static boolean isTurnScreenOnAllowed(AppOpsManager appOpsManager, int uid, String packageName) { + return appOpsManager.checkOpNoThrow(OP_TURN_SCREEN_ON, uid, packageName) == MODE_ALLOWED; } /** * @return the summary for the current state of whether the app associated with the given * packageName is allowed to turn the screen on. */ - public static int getSummary(Context context, ApplicationsState.AppEntry entry) { - final AppStateAppOpsBridge.PermissionState state; - if (entry.extraInfo instanceof AppStateAppOpsBridge.PermissionState) { - state = (AppStateAppOpsBridge.PermissionState) entry.extraInfo; - } else { - state = new AppStateTurnScreenOnBridge(context, /*appState=*/ null, - /*callback=*/ null).getPermissionInfo(entry.info.packageName, entry.info.uid); - } - return state.isPermissible() ? R.string.app_permission_summary_allowed + public static int getPreferenceSummary(AppOpsManager appOpsManager, int uid, + String packageName) { + final boolean enabled = TurnScreenOnDetails.isTurnScreenOnAllowed(appOpsManager, uid, + packageName); + return enabled ? R.string.app_permission_summary_allowed : R.string.app_permission_summary_not_allowed; } } diff --git a/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnSettings.java b/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnSettings.java new file mode 100644 index 00000000000..742831e4716 --- /dev/null +++ b/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnSettings.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.applications.specialaccess.turnscreenon; + +import android.Manifest; +import android.annotation.Nullable; +import android.app.AppOpsManager; +import android.app.settings.SettingsEnums; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; +import android.os.Bundle; +import android.os.UserHandle; +import android.os.UserManager; +import android.util.IconDrawableFactory; +import android.util.Pair; +import android.view.View; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; +import androidx.preference.Preference.OnPreferenceClickListener; +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.Utils; +import com.android.settings.applications.AppInfoBase; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settings.widget.EmptyTextSettings; +import com.android.settingslib.search.SearchIndexable; +import com.android.settingslib.widget.AppPreference; + +import java.text.Collator; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +/** + * Settings page for providing special app access to turn the screen of the device on. + */ +@SearchIndexable +public class TurnScreenOnSettings extends EmptyTextSettings { + + @VisibleForTesting + static final List IGNORE_PACKAGE_LIST = new ArrayList<>(); + + static { + IGNORE_PACKAGE_LIST.add(Utils.SYSTEMUI_PACKAGE_NAME); + } + + /** + * Comparator by name, then user id. + * {@see PackageItemInfo#DisplayNameComparator} + */ + static class AppComparator implements Comparator> { + + private final Collator mCollator = Collator.getInstance(); + private final PackageManager mPm; + + AppComparator(PackageManager pm) { + mPm = pm; + } + + public final int compare(Pair a, + Pair b) { + CharSequence sa = a.first.loadLabel(mPm); + if (sa == null) sa = a.first.name; + CharSequence sb = b.first.loadLabel(mPm); + if (sb == null) sb = b.first.name; + int nameCmp = mCollator.compare(sa.toString(), sb.toString()); + if (nameCmp != 0) { + return nameCmp; + } else { + return a.second - b.second; + } + } + } + + private AppOpsManager mAppOpsManager; + private Context mContext; + private PackageManager mPackageManager; + private UserManager mUserManager; + private IconDrawableFactory mIconDrawableFactory; + + public TurnScreenOnSettings() { + // Do nothing + } + + public TurnScreenOnSettings(PackageManager pm, UserManager um) { + mPackageManager = pm; + mUserManager = um; + } + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + mContext = getActivity(); + mPackageManager = mContext.getPackageManager(); + mUserManager = mContext.getSystemService(UserManager.class); + mAppOpsManager = mContext.getSystemService(AppOpsManager.class); + mIconDrawableFactory = IconDrawableFactory.newInstance(mContext); + } + + @Override + public void onResume() { + super.onResume(); + + // Clear the prefs + final PreferenceScreen screen = getPreferenceScreen(); + screen.removeAll(); + + // Fetch the set of applications for each profile which have the permission required to turn + // the screen on with a wake lock. + final ArrayList> apps = collectTurnScreenOnApps( + UserHandle.myUserId()); + apps.sort(new AppComparator(mPackageManager)); + + // Rebuild the list of prefs + final Context prefContext = getPrefContext(); + for (final Pair appData : apps) { + final ApplicationInfo appInfo = appData.first; + final int userId = appData.second; + final UserHandle user = UserHandle.of(userId); + final String packageName = appInfo.packageName; + final CharSequence label = appInfo.loadLabel(mPackageManager); + + final Preference pref = new AppPreference(prefContext); + pref.setIcon(mIconDrawableFactory.getBadgedIcon(appInfo, userId)); + pref.setTitle(mPackageManager.getUserBadgedLabel(label, user)); + pref.setSummary(TurnScreenOnDetails.getPreferenceSummary(mAppOpsManager, + appInfo.uid, packageName)); + pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + AppInfoBase.startAppInfoFragment(TurnScreenOnDetails.class, + getString(R.string.turn_screen_on_title), + packageName, appInfo.uid, + TurnScreenOnSettings.this, -1, getMetricsCategory()); + return true; + } + }); + screen.addPreference(pref); + } + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + setEmptyText(R.string.no_applications); + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.turn_screen_on_settings; + } + + @Override + public int getMetricsCategory() { + return SettingsEnums.SETTINGS_TURN_SCREEN_ON_ACCESS; + } + + /** + * @return the list of applications for the given user and all their profiles that can turn on + * the screen with wake locks. + */ + @VisibleForTesting + ArrayList> collectTurnScreenOnApps(int userId) { + final ArrayList> apps = new ArrayList<>(); + final ArrayList userIds = new ArrayList<>(); + for (UserInfo user : mUserManager.getProfiles(userId)) { + userIds.add(user.id); + } + + for (int id : userIds) { + final List installedPackages = mPackageManager.getInstalledPackagesAsUser( + /* flags= */ 0, id); + for (PackageInfo packageInfo : installedPackages) { + if (hasTurnScreenOnPermission(mPackageManager, packageInfo.packageName)) { + apps.add(new Pair<>(packageInfo.applicationInfo, id)); + } + } + } + return apps; + } + + /** + * @return true if the package has the permission to turn the screen on. + */ + @VisibleForTesting + static boolean hasTurnScreenOnPermission(PackageManager packageManager, String packageName) { + if (IGNORE_PACKAGE_LIST.contains(packageName)) { + return false; + } + return packageManager.checkPermission(Manifest.permission.TURN_SCREEN_ON, packageName) + == PackageManager.PERMISSION_GRANTED; + } + + public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider(R.xml.turn_screen_on_settings); +} diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java index 9db4ddd8d45..708f317994e 100644 --- a/src/com/android/settings/core/gateway/SettingsGateway.java +++ b/src/com/android/settings/core/gateway/SettingsGateway.java @@ -50,7 +50,6 @@ import com.android.settings.applications.appinfo.ExternalSourcesDetails; import com.android.settings.applications.appinfo.LongBackgroundTasksDetails; import com.android.settings.applications.appinfo.ManageExternalStorageDetails; import com.android.settings.applications.appinfo.MediaManagementAppsDetails; -import com.android.settings.applications.appinfo.TurnScreenOnDetails; import com.android.settings.applications.appinfo.WriteSettingsDetails; import com.android.settings.applications.appops.BackgroundCheckSummary; import com.android.settings.applications.assist.ManageAssist; @@ -63,6 +62,8 @@ import com.android.settings.applications.specialaccess.notificationaccess.Notifi import com.android.settings.applications.specialaccess.pictureinpicture.PictureInPictureDetails; import com.android.settings.applications.specialaccess.pictureinpicture.PictureInPictureSettings; import com.android.settings.applications.specialaccess.premiumsms.PremiumSmsAccess; +import com.android.settings.applications.specialaccess.turnscreenon.TurnScreenOnDetails; +import com.android.settings.applications.specialaccess.turnscreenon.TurnScreenOnSettings; import com.android.settings.applications.specialaccess.vrlistener.VrListenerSettings; import com.android.settings.applications.specialaccess.zenaccess.ZenAccessDetails; import com.android.settings.backup.PrivacySettings; @@ -365,6 +366,7 @@ public class SettingsGateway { OneHandedSettings.class.getName(), MobileNetworkSettings.class.getName(), AppLocaleDetails.class.getName(), + TurnScreenOnSettings.class.getName(), TurnScreenOnDetails.class.getName(), NfcAndPaymentFragment.class.getName(), ColorAndMotionFragment.class.getName(), diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/TurnScreenOnDetailsTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/TurnScreenOnDetailsTest.java deleted file mode 100644 index 180b8058b69..00000000000 --- a/tests/robotests/src/com/android/settings/applications/appinfo/TurnScreenOnDetailsTest.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.applications.appinfo; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; - -import androidx.preference.SwitchPreference; - -import com.android.settings.applications.AppStateAppOpsBridge; -import com.android.settings.applications.AppStateTurnScreenOnBridge; - -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.util.ReflectionHelpers; - -@RunWith(RobolectricTestRunner.class) -public class TurnScreenOnDetailsTest { - - @Mock - private SwitchPreference mSwitchPref; - @Mock - private PackageInfo mPackageInfo; - @Mock - private AppStateTurnScreenOnBridge mAppStateBridge; - @Mock - private AppStateAppOpsBridge.PermissionState mPermissionState; - - private final TurnScreenOnDetails mFragment = new TurnScreenOnDetails(); - - - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - ReflectionHelpers.setField(mFragment, "mSwitchPref", mSwitchPref); - ReflectionHelpers.setField(mFragment, "mAppBridge", mAppStateBridge); - } - - @Test - public void refreshUi_noPackageInfo_shouldReturnFalseAndNoCrash() { - mFragment.refreshUi(); - - assertThat(mFragment.refreshUi()).isFalse(); - // should not crash - } - - @Test - public void refreshUi_noApplicationInfo_shouldReturnFalseAndNoCrash() { - ReflectionHelpers.setField(mFragment, "mPackageInfo", mPackageInfo); - - mFragment.refreshUi(); - - assertThat(mFragment.refreshUi()).isFalse(); - // should not crash - } - - @Test - public void refreshUi_hasApplicationInfo_shouldReturnTrue() { - ReflectionHelpers.setField(mFragment, "mPackageInfo", mPackageInfo); - mPackageInfo.applicationInfo = new ApplicationInfo(); - when(mAppStateBridge.getPermissionInfo(nullable(String.class), anyInt())) - .thenReturn(mPermissionState); - - mFragment.refreshUi(); - - assertThat(mFragment.refreshUi()).isTrue(); - } - - @Test - public void refreshUi_permissionNotDeclared_switchPreferenceDisabled() { - ReflectionHelpers.setField(mFragment, "mPackageInfo", mPackageInfo); - mPackageInfo.applicationInfo = new ApplicationInfo(); - when(mAppStateBridge.getPermissionInfo(nullable(String.class), anyInt())) - .thenReturn(mPermissionState); - mPermissionState.permissionDeclared = false; - - mFragment.refreshUi(); - verify(mSwitchPref).setEnabled(false); - } - - @Test - public void refreshUi_permissionDeclared_switchPreferenceEnabled() { - ReflectionHelpers.setField(mFragment, "mPackageInfo", mPackageInfo); - mPackageInfo.applicationInfo = new ApplicationInfo(); - when(mAppStateBridge.getPermissionInfo(nullable(String.class), anyInt())) - .thenReturn(mPermissionState); - mPermissionState.permissionDeclared = true; - - mFragment.refreshUi(); - verify(mSwitchPref).setEnabled(true); - } - - @Test - public void refreshUi_turnScreenOnAllowed_switchPreferenceChecked() { - ReflectionHelpers.setField(mFragment, "mPackageInfo", mPackageInfo); - mPackageInfo.applicationInfo = new ApplicationInfo(); - when(mAppStateBridge.getPermissionInfo(nullable(String.class), anyInt())) - .thenReturn(mPermissionState); - - when(mPermissionState.isPermissible()).thenReturn(true); - mFragment.refreshUi(); - verify(mSwitchPref).setChecked(true); - } - - @Test - public void refreshUi_turnScreenOnDisallowed_switchPreferenceUnchecked() { - ReflectionHelpers.setField(mFragment, "mPackageInfo", mPackageInfo); - mPackageInfo.applicationInfo = new ApplicationInfo(); - when(mAppStateBridge.getPermissionInfo(nullable(String.class), anyInt())) - .thenReturn(mPermissionState); - - when(mPermissionState.isPermissible()).thenReturn(false); - mFragment.refreshUi(); - verify(mSwitchPref).setChecked(false); - } -} diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnDetailsTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnDetailsTest.java new file mode 100644 index 00000000000..c27ad2b92bf --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnDetailsTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.applications.specialaccess.turnscreenon; + +import static android.app.AppOpsManager.MODE_ALLOWED; +import static android.app.AppOpsManager.MODE_ERRORED; +import static android.app.AppOpsManager.OP_TURN_SCREEN_ON; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +import android.app.AppOpsManager; + +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 TurnScreenOnDetailsTest { + + private static final int UID = 0; + private static final String PACKAGE_NAME = "com.android.fake.package"; + + @Mock + private AppOpsManager mAppOpsManager; + + + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void isTurnScreenOnAllowed_appOpErrored_shouldReturnFalse() { + when(mAppOpsManager.checkOpNoThrow(eq(OP_TURN_SCREEN_ON), eq(UID), + eq(PACKAGE_NAME))).thenReturn(MODE_ERRORED); + + boolean isAllowed = TurnScreenOnDetails.isTurnScreenOnAllowed(mAppOpsManager, UID, + PACKAGE_NAME); + + assertThat(isAllowed).isFalse(); + } + + @Test + public void isTurnScreenOnAllowed_appOpAllowed_shouldReturnTrue() { + when(mAppOpsManager.checkOpNoThrow(eq(OP_TURN_SCREEN_ON), eq(UID), + eq(PACKAGE_NAME))).thenReturn(MODE_ALLOWED); + + boolean isAllowed = TurnScreenOnDetails.isTurnScreenOnAllowed(mAppOpsManager, UID, + PACKAGE_NAME); + + assertThat(isAllowed).isTrue(); + } +} diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnSettingsTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnSettingsTest.java new file mode 100644 index 00000000000..a2602110500 --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnSettingsTest.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.applications.specialaccess.turnscreenon; + + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +import android.Manifest; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; +import android.os.UserManager; +import android.util.Pair; + +import com.android.settings.testutils.FakeFeatureFactory; + +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 java.util.ArrayList; +import java.util.List; + +@RunWith(RobolectricTestRunner.class) +public class TurnScreenOnSettingsTest { + + private static final int PRIMARY_USER_ID = 0; + private static final int PROFILE_USER_ID = 10; + + private TurnScreenOnSettings mFragment; + @Mock + private PackageManager mPackageManager; + @Mock + private UserManager mUserManager; + private ArrayList mPrimaryUserPackages; + private ArrayList mProfileUserPackages; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + FakeFeatureFactory.setupForTest(); + mFragment = new TurnScreenOnSettings(mPackageManager, mUserManager); + mPrimaryUserPackages = new ArrayList<>(); + mProfileUserPackages = new ArrayList<>(); + when(mPackageManager.getInstalledPackagesAsUser(anyInt(), eq(PRIMARY_USER_ID))) + .thenReturn(mPrimaryUserPackages); + when(mPackageManager.getInstalledPackagesAsUser(anyInt(), eq(PROFILE_USER_ID))) + .thenReturn(mProfileUserPackages); + + UserInfo primaryUserInfo = new UserInfo(); + primaryUserInfo.id = PRIMARY_USER_ID; + UserInfo profileUserInfo = new UserInfo(); + profileUserInfo.id = PROFILE_USER_ID; + + when(mUserManager.getProfiles(PRIMARY_USER_ID)) + .thenReturn(ImmutableList.of(primaryUserInfo, profileUserInfo)); + } + + @Test + public void testCollectTurnScreenOnApps_variousPackages_shouldReturnOnlyPackagesWithTurnScreenOnPermission() { + PackageInfo primaryP1 = createPackage("Calculator", true); + PackageInfo primaryP2 = createPackage("Clock", false); + PackageInfo profileP1 = createPackage("Browser", false); + PackageInfo profileP2 = createPackage("Files", true); + mPrimaryUserPackages.add(primaryP1); + mPrimaryUserPackages.add(primaryP2); + mProfileUserPackages.add(profileP1); + mProfileUserPackages.add(profileP2); + + List> apps = mFragment.collectTurnScreenOnApps( + PRIMARY_USER_ID); + + assertThat(containsPackages(apps, primaryP1, profileP2)).isTrue(); + assertThat(containsPackages(apps, primaryP2, profileP1)).isFalse(); + } + + @Test + public void collectTurnScreenOnApps_noTurnScreenOnPackages_shouldReturnEmptyList() { + PackageInfo primaryP1 = createPackage("Calculator", false); + PackageInfo profileP1 = createPackage("Browser", false); + mPrimaryUserPackages.add(primaryP1); + mProfileUserPackages.add(profileP1); + + List> apps = mFragment.collectTurnScreenOnApps( + PRIMARY_USER_ID); + + assertThat(apps).isEmpty(); + } + + @Test + public void sort_multiplePackages_appsShouldBeOrderedByAppName() { + PackageInfo primaryP1 = createPackage("Android", true); + PackageInfo primaryP2 = createPackage("Boop", true); + PackageInfo primaryP3 = createPackage("Deck", true); + PackageInfo profileP1 = createPackage("Android", true); + PackageInfo profileP2 = createPackage("Cool", true); + PackageInfo profileP3 = createPackage("Fast", false); + mPrimaryUserPackages.add(primaryP1); + mPrimaryUserPackages.add(primaryP2); + mPrimaryUserPackages.add(primaryP3); + mProfileUserPackages.add(profileP1); + mProfileUserPackages.add(profileP2); + mProfileUserPackages.add(profileP3); + List> apps = mFragment.collectTurnScreenOnApps( + PRIMARY_USER_ID); + + apps.sort(new TurnScreenOnSettings.AppComparator(null)); + + assertThat(isOrdered(apps, primaryP1, profileP1, primaryP2, profileP2, primaryP3)).isTrue(); + } + + @Test + public void hasTurnScreenOnPermission_ignoredPackages_shouldReturnFalse() { + boolean res = false; + + for (String ignoredPackage : TurnScreenOnSettings.IGNORE_PACKAGE_LIST) { + res |= TurnScreenOnSettings.hasTurnScreenOnPermission(mPackageManager, ignoredPackage); + } + + assertThat(res).isFalse(); + } + + private boolean containsPackages(List> apps, + PackageInfo... packages) { + for (PackageInfo aPackage : packages) { + boolean found = false; + for (Pair app : apps) { + if (app.first == aPackage.applicationInfo) { + found = true; + break; + } + } + if (!found) { + return false; + } + } + return true; + } + + private boolean isOrdered(List> apps, PackageInfo... packages) { + if (apps.size() != packages.length) { + return false; + } + + for (int i = 0; i < packages.length; i++) { + if (packages[i].applicationInfo != apps.get(i).first) { + return false; + } + } + return true; + } + + private PackageInfo createPackage(String packageName, boolean hasTurnScreenOnPermission) { + PackageInfo pi = new PackageInfo(); + when(mPackageManager.checkPermission(Manifest.permission.TURN_SCREEN_ON, + packageName)).thenReturn( + hasTurnScreenOnPermission ? PackageManager.PERMISSION_GRANTED + : PackageManager.PERMISSION_DENIED); + pi.packageName = packageName; + pi.applicationInfo = new ApplicationInfo(); + pi.applicationInfo.name = packageName; + return pi; + } +} diff --git a/tests/unit/src/com/android/settings/applications/manageapplications/AppFilterRegistryTest.java b/tests/unit/src/com/android/settings/applications/manageapplications/AppFilterRegistryTest.java index 023fc0f548b..ff5c0d860cf 100644 --- a/tests/unit/src/com/android/settings/applications/manageapplications/AppFilterRegistryTest.java +++ b/tests/unit/src/com/android/settings/applications/manageapplications/AppFilterRegistryTest.java @@ -23,7 +23,6 @@ import static com.android.settings.applications.manageapplications.AppFilterRegi import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_MEDIA_MANAGEMENT; import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_POWER_ALLOWLIST; import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_RECENT; -import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_TURN_SCREEN_ON; import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_USAGE_ACCESS; import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_WITH_OVERLAY; import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_WRITE_SETTINGS; @@ -39,7 +38,6 @@ import static com.android.settings.applications.manageapplications.ManageApplica import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_NOTIFICATION; import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_OVERLAY; import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_STORAGE; -import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_TURN_SCREEN_ON; import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_USAGE_ACCESS; import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_WRITE_SETTINGS; @@ -84,8 +82,5 @@ public class AppFilterRegistryTest { assertThat(registry.getDefaultFilterType(LIST_TYPE_LONG_BACKGROUND_TASKS)) .isEqualTo(FILTER_LONG_BACKGROUND_TASKS); - - assertThat(registry.getDefaultFilterType(LIST_TYPE_TURN_SCREEN_ON)).isEqualTo( - FILTER_APPS_TURN_SCREEN_ON); } }