diff --git a/res/values/strings.xml b/res/values/strings.xml index 7e10006638d..01c38d83734 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1873,7 +1873,7 @@ Require device unlock for NFC - Allow NFC Payment and Transit use only when screen is unlocked + Allow NFC use only when screen is unlocked Android Beam @@ -2847,6 +2847,10 @@ Turn on screen attention Keep screen on when looking at it + + Camera access is required for Face Detection. Tap to manage permissions for Device Personalization Services + + Manage permissions Night Light @@ -9800,6 +9804,18 @@ Control app access to your data + + Unused apps + + + + %d unused app + %d unused apps + + + + Remove permissions and free up space + All apps @@ -10506,6 +10522,9 @@ Your admin can monitor and manage apps and data associated with this device, including settings, permissions, corporate access, network activity, and the device\'s location information. + + Your device admin may be able to access data associated + with this device and change this device\’s settings. Turn off diff --git a/res/xml/app_and_notification.xml b/res/xml/app_and_notification.xml index 49d8e9b85fd..8b4c912485c 100644 --- a/res/xml/app_and_notification.xml +++ b/res/xml/app_and_notification.xml @@ -83,6 +83,16 @@ + + + + + + + + + + + + + + + + + + - - * Controllers defined in xml are automatically {@link Lifecycle#addObserver(LifecycleObserver) - * wired up} to the settings lifecycle if they implement {@link LifecycleObserver}. */ public abstract class BasePreferenceController extends AbstractPreferenceController implements Sliceable { diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java index 7acbd1997c5..1b2be809a6d 100644 --- a/src/com/android/settings/dashboard/DashboardFragment.java +++ b/src/com/android/settings/dashboard/DashboardFragment.java @@ -27,7 +27,6 @@ import android.util.Log; import androidx.annotation.CallSuper; import androidx.annotation.VisibleForTesting; -import androidx.lifecycle.LifecycleObserver; import androidx.preference.Preference; import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceManager; @@ -45,6 +44,7 @@ import com.android.settings.overlay.FeatureFactory; import com.android.settings.widget.PrimarySwitchPreference; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.drawer.CategoryKey; import com.android.settingslib.drawer.DashboardCategory; import com.android.settingslib.drawer.ProviderTile; diff --git a/src/com/android/settings/display/SmartAutoRotatePermissionController.java b/src/com/android/settings/display/SmartAutoRotatePermissionController.java index 484db31f371..caa306d6a9d 100644 --- a/src/com/android/settings/display/SmartAutoRotatePermissionController.java +++ b/src/com/android/settings/display/SmartAutoRotatePermissionController.java @@ -22,11 +22,12 @@ import static com.android.settings.display.SmartAutoRotateController.isRotationR import android.content.Context; import android.content.Intent; import android.net.Uri; -import android.text.TextUtils; -import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; +import com.android.settings.R; import com.android.settings.core.BasePreferenceController; +import com.android.settingslib.widget.BannerMessagePreference; /** * The controller of camera based rotate permission warning preference. The preference appears when @@ -44,6 +45,18 @@ public class SmartAutoRotatePermissionController extends BasePreferenceControlle mIntent.setData(Uri.parse("package:" + packageName)); } + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + final BannerMessagePreference preference = + (BannerMessagePreference) screen.findPreference(getPreferenceKey()); + preference + .setPositiveButtonText(R.string.auto_rotate_manage_permission_button) + .setPositiveButtonOnClickListener(v -> { + mContext.startActivity(mIntent); + }); + } + @Override @AvailabilityStatus public int getAvailabilityStatus() { @@ -51,13 +64,4 @@ public class SmartAutoRotatePermissionController extends BasePreferenceControlle ? AVAILABLE_UNSEARCHABLE : UNSUPPORTED_ON_DEVICE; } - - @Override - public boolean handlePreferenceTreeClick(Preference preference) { - if (TextUtils.equals(getPreferenceKey(), preference.getKey())) { - mContext.startActivity(mIntent); - return true; - } - return super.handlePreferenceTreeClick(preference); - } } diff --git a/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelper.java b/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelper.java index cf23c94c74b..1140291a2a1 100644 --- a/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelper.java +++ b/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelper.java @@ -141,17 +141,14 @@ public class ActionDisabledByAdminDialogHelper { } private boolean isNotCurrentUserOrProfile(ComponentName admin, int userId) { - return !isFinancedDevice() - && (!RestrictedLockUtilsInternal.isAdminInCurrentUserOrProfile(mActivity, admin) - || !RestrictedLockUtils.isCurrentUserOrProfile(mActivity, userId)); + return !RestrictedLockUtilsInternal.isAdminInCurrentUserOrProfile(mActivity, admin) + || !RestrictedLockUtils.isCurrentUserOrProfile(mActivity, userId); } @VisibleForTesting void setAdminSupportIcon(View root, ComponentName admin, int userId) { ImageView supportIconView = root.requireViewById(R.id.admin_support_icon); - if (isFinancedDevice()) { - supportIconView.setVisibility(View.GONE); - } else if (isNotCurrentUserOrProfile(admin, userId)) { + if (isNotCurrentUserOrProfile(admin, userId)) { supportIconView.setImageDrawable( mActivity.getDrawable(com.android.internal.R.drawable.ic_info)); diff --git a/tests/robotests/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelperTest.java b/tests/robotests/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelperTest.java index b245017b680..5c3dacd46de 100644 --- a/tests/robotests/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelperTest.java +++ b/tests/robotests/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelperTest.java @@ -36,10 +36,8 @@ import android.content.pm.UserInfo; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; -import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; -import android.widget.ImageView; import android.widget.TextView; import androidx.appcompat.app.AlertDialog; @@ -108,20 +106,6 @@ public class ActionDisabledByAdminDialogHelperTest { Settings.DeviceAdminSettingsActivity.class.getName())); } - @Test - public void testSetAdminSupportIconForFinancedDevice_adminSupportIconIsGone() { - final ShadowDevicePolicyManager dpmShadow = ShadowDevicePolicyManager.getShadow(); - final ViewGroup view = new FrameLayout(mActivity); - final ImageView supportIconImageView = createAdminSupportIconImageView(view, mActivity); - final ComponentName component = new ComponentName("some.package.name", - "some.package.name.SomeClass"); - setupFinancedDevice(dpmShadow); - - mHelper.setAdminSupportIcon(view, component, 123); - - assertEquals(View.GONE, supportIconImageView.getVisibility()); - } - @Test public void testSetAdminSupportTitle() { final ViewGroup view = new FrameLayout(mActivity); @@ -260,14 +244,6 @@ public class ActionDisabledByAdminDialogHelperTest { verify(builder, never()).setNeutralButton(anyInt(), any()); } - private static ImageView createAdminSupportIconImageView(final ViewGroup view, - final Activity activity) { - final ImageView supportIconView = new ImageView(activity); - supportIconView.setId(R.id.admin_support_icon); - view.addView(supportIconView); - return supportIconView; - } - private static TextView createAdminSupportDialogTitleTextView(final ViewGroup view, final Activity activity) { final TextView textView = new TextView(activity); diff --git a/tests/unit/src/com/android/settings/applications/HibernatedAppsPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/HibernatedAppsPreferenceControllerTest.java new file mode 100644 index 00000000000..cf4c53e157c --- /dev/null +++ b/tests/unit/src/com/android/settings/applications/HibernatedAppsPreferenceControllerTest.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.applications; + +import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION; + +import static com.android.settings.Utils.PROPERTY_APP_HIBERNATION_ENABLED; +import static com.android.settings.core.BasePreferenceController.AVAILABLE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; + +import android.content.Context; +import android.provider.DeviceConfig; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * TODO(b/181172051): test getNumberHibernated() when the API implemented + */ +@RunWith(AndroidJUnit4.class) +public class HibernatedAppsPreferenceControllerTest { + + private static final String KEY = "key"; + private Context mContext; + private HibernatedAppsPreferenceController mController; + + @Before + public void setUp() { + DeviceConfig.setProperty(NAMESPACE_APP_HIBERNATION, PROPERTY_APP_HIBERNATION_ENABLED, + "true", false); + mContext = spy(ApplicationProvider.getApplicationContext()); + mController = new HibernatedAppsPreferenceController(mContext, KEY); + } + + @Test + public void getAvailabilityStatus_featureDisabled_shouldNotReturnAvailable() { + DeviceConfig.setProperty(NAMESPACE_APP_HIBERNATION, PROPERTY_APP_HIBERNATION_ENABLED, + "false", true); + + assertThat((mController).getAvailabilityStatus()).isNotEqualTo(AVAILABLE); + } +} diff --git a/tests/unit/src/com/android/settings/applications/appinfo/HibernationSwitchPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/appinfo/HibernationSwitchPreferenceControllerTest.java new file mode 100644 index 00000000000..e27942a6e69 --- /dev/null +++ b/tests/unit/src/com/android/settings/applications/appinfo/HibernationSwitchPreferenceControllerTest.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.applications.appinfo; + +import static android.app.AppOpsManager.MODE_ALLOWED; +import static android.app.AppOpsManager.MODE_DEFAULT; +import static android.app.AppOpsManager.MODE_IGNORED; +import static android.app.AppOpsManager.OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED; +import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION; + +import static com.android.settings.Utils.PROPERTY_APP_HIBERNATION_ENABLED; +import static com.android.settings.core.BasePreferenceController.AVAILABLE; + +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.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.AppOpsManager; +import android.content.Context; +import android.content.pm.PackageManager; +import android.provider.DeviceConfig; + +import androidx.preference.SwitchPreference; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +public class HibernationSwitchPreferenceControllerTest { + private static final int PACKAGE_UID = 1; + private static final String INVALID_PACKAGE_NAME = "invalid_package"; + private static final String KEY = "key"; + private static final String VALID_PACKAGE_NAME = "package"; + private static final String EXEMPTED_PACKAGE_NAME = "exempted_package"; + private static final String UNEXEMPTED_PACKAGE_NAME = "unexempted_package"; + @Mock + private AppOpsManager mAppOpsManager; + @Mock + private PackageManager mPackageManager; + @Mock + private SwitchPreference mPreference; + + private HibernationSwitchPreferenceController mController; + private Context mContext; + + @Before + public void setUp() throws PackageManager.NameNotFoundException { + MockitoAnnotations.initMocks(this); + mContext = spy(ApplicationProvider.getApplicationContext()); + when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager); + when(mPackageManager.getPackageUidAsUser(eq(VALID_PACKAGE_NAME), anyInt())) + .thenReturn(PACKAGE_UID); + when(mPackageManager.getPackageUidAsUser(eq(INVALID_PACKAGE_NAME), anyInt())) + .thenThrow(new PackageManager.NameNotFoundException()); + when(mPackageManager.getTargetSdkVersion(eq(EXEMPTED_PACKAGE_NAME))) + .thenReturn(android.os.Build.VERSION_CODES.Q); + when(mPackageManager.getTargetSdkVersion(eq(UNEXEMPTED_PACKAGE_NAME))) + .thenReturn(android.os.Build.VERSION_CODES.S); + when(mContext.getPackageManager()).thenReturn(mPackageManager); + DeviceConfig.setProperty(NAMESPACE_APP_HIBERNATION, PROPERTY_APP_HIBERNATION_ENABLED, + "true", true /* makeDefault */); + mController = new HibernationSwitchPreferenceController(mContext, KEY); + when(mPreference.getKey()).thenReturn(mController.getPreferenceKey()); + } + + @Test + public void getAvailabilityStatus_featureNotEnabled_shouldNotReturnAvailable() { + mController.setPackage(VALID_PACKAGE_NAME); + DeviceConfig.setProperty(NAMESPACE_APP_HIBERNATION, PROPERTY_APP_HIBERNATION_ENABLED, + "false", true /* makeDefault */); + + assertThat(mController.getAvailabilityStatus()).isNotEqualTo(AVAILABLE); + } + + @Test + public void getAvailabilityStatus_invalidPackage_shouldReturnNotAvailable() { + mController.setPackage(INVALID_PACKAGE_NAME); + + assertThat(mController.getAvailabilityStatus()).isNotEqualTo(AVAILABLE); + } + + @Test + public void getAvailabilityStatus_validPackage_shouldReturnAvailable() { + mController.setPackage(VALID_PACKAGE_NAME); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); + } + + @Test + public void updateState_exemptedByDefaultPackage_shouldNotCheck() { + when(mAppOpsManager.unsafeCheckOpNoThrow( + eq(OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED), anyInt(), eq(EXEMPTED_PACKAGE_NAME))) + .thenReturn(MODE_DEFAULT); + mController.setPackage(EXEMPTED_PACKAGE_NAME); + + mController.updateState(mPreference); + + verify(mPreference).setChecked(false); + } + + @Test + public void updateState_exemptedPackageOverrideByUser_shouldCheck() { + when(mAppOpsManager.unsafeCheckOpNoThrow( + eq(OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED), anyInt(), eq(EXEMPTED_PACKAGE_NAME))) + .thenReturn(MODE_ALLOWED); + mController.setPackage(EXEMPTED_PACKAGE_NAME); + + mController.updateState(mPreference); + + verify(mPreference).setChecked(true); + } + + @Test + public void updateState_unexemptedPackageOverrideByUser_shouldNotCheck() { + when(mAppOpsManager.unsafeCheckOpNoThrow( + eq(OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED), anyInt(), eq(UNEXEMPTED_PACKAGE_NAME))) + .thenReturn(MODE_IGNORED); + mController.setPackage(UNEXEMPTED_PACKAGE_NAME); + + mController.updateState(mPreference); + + verify(mPreference).setChecked(false); + } +}