Merge "Update the lint color check file."
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 968f650..e68feb8 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4771,7 +4771,7 @@
<!-- Title for the accessibility preference screen to enable video captioning. [CHAR LIMIT=35] -->
<string name="accessibility_captioning_title">Caption preferences</string>
<!-- Title for the accessibility preference screen to enable screen magnification. [CHAR LIMIT=35] -->
- <string name="accessibility_screen_magnification_title">Magnification</string>
+ <string name="accessibility_screen_magnification_title">Magnify</string>
<!-- Title for the accessibility preference screen to edit magnification area. [CHAR LIMIT=35] -->
<string name="accessibility_magnification_mode_title">Magnification area</string>
<!-- Message for the accessibility preference screen to edit magnification area dialog. [CHAR LIMIT=none] -->
@@ -4791,13 +4791,17 @@
<!-- Summary for the accessibility preference screen to show move controller. [CHAR LIMIT=none] -->
<string name="accessibility_magnification_window_control_switch_summary">Show a joystick-like controller to move the magnification area</string>
<!-- Title for the accessibility preference screen to enable screen magnification settings. [CHAR LIMIT=35] -->
- <string name="accessibility_magnification_service_settings_title">Magnify settings</string>
+ <string name="accessibility_magnification_service_settings_title">Magnification settings</string>
<!-- Title for the accessibility preference screen to enable triple-tap gesture screen magnification. [CHAR LIMIT=35] -->
<string name="accessibility_screen_magnification_gestures_title">Magnify with triple-tap</string>
<!-- Title for the accessibility preference screen to enable navigation bar screen magnification. [CHAR LIMIT=35] -->
<string name="accessibility_screen_magnification_navbar_title">Magnify with shortcut</string>
<!-- Summary for the accessibility magnification setting indicating both "Magnify with button" and "Magnify with triple-tap" are enabled [CHAR LIMIT=50] -->
<string name="accessibility_screen_magnification_state_navbar_gesture">Magnify with shortcut & triple-tap</string>
+ <!-- Title for the footer text to explain what Magnify does. [CHAR LIMIT=35] -->
+ <string name="accessibility_screen_magnification_about">About Magnify</string>
+ <!-- Title for the footer text to explain what option accessibility service does. [CHAR LIMIT=35] -->
+ <string name="accessibility_screen_option">Options</string>
<!-- Summary for the accessibility preference to enable screen magnification. [CHAR LIMIT=25] -->
<string name="accessibility_preference_magnification_summary">Zoom in on screen</string>
<!-- Short summary for Magnification gesture. Tells the user that this feature allows the user to magnify the screen by tapping 3 times. Appears in accessibility portion of setup wizard -->
@@ -6964,6 +6968,8 @@
<string name="help_uri_apps_photography" translatable="false"></string>
<!-- Help URI, manage apps wifi access [DO NOT TRANSLATE] -->
<string name="help_uri_apps_wifi_access" translatable="false"></string>
+ <!-- Help URI, manage apps that have access to all files [DO NOT TRANSLATE] -->
+ <string name="help_uri_manage_external_storage" translatable="false"></string>
<!-- Help URI, Storage [DO NOT TRANSLATE] -->
<string name="help_uri_storage" translatable="false"></string>
<!-- Help URI, Accessibility [DO NOT TRANSLATE] -->
@@ -9441,6 +9447,16 @@
<!-- Description of allowing overlay setting [CHAR LIMIT=NONE] -->
<string name="allow_overlay_description">Allow this app to display on top of other apps you\u2019re using. It may interfere with your use of those apps or change the way they seem to appear or behave.</string>
+ <!-- Manager External Storage settings title [CHAR LIMIT=30] -->
+ <string name="manage_external_storage_title">All files access</string>
+ <!-- Label for a setting which controls whether an app can manage external storage [CHAR LIMIT=45] -->
+ <string name="permit_manage_external_storage">Allow access to manage all files</string>
+ <!-- Description for a setting which controls whether an app can manage external storage
+ [CHAR LIMIT=NONE] -->
+ <string name="allow_manage_external_storage_description">Allow this app to read, modify and delete all files on this device or any connected storage volumes. If granted, app may access files without your explicit knowledge.</string>
+ <!-- Label for showing apps that can manage external storage[CHAR LIMIT=45] -->
+ <string name="filter_manage_external_storage">Can access all files</string>
+
<!-- Keyword for VR setting -->
<string name="keywords_vr_listener">vr virtual reality listener stereo helper service</string>
<!-- Main settings screen item's title to go into the overlay settings screen [CHAR LIMIT=30] -->
diff --git a/res/xml/accessibility_settings_for_setup_wizard.xml b/res/xml/accessibility_settings_for_setup_wizard.xml
index 738bb8e..1b9939a 100644
--- a/res/xml/accessibility_settings_for_setup_wizard.xml
+++ b/res/xml/accessibility_settings_for_setup_wizard.xml
@@ -15,47 +15,45 @@
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
- android:title="@string/vision_settings_title"
- android:persistent="true" >
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:persistent="true"
+ android:title="@string/vision_settings_title">
<com.android.settings.accessibility.DividerAllowedBelowPreference
android:key="vision_settings_summary"
- android:title="@string/vision_settings_description"
android:layout="@layout/preference_multiline_title"
- android:selectable="false" />
+ android:selectable="false"
+ android:title="@string/vision_settings_description" />
<Preference
- android:fragment=
- "com.android.settings.accessibility.MagnificationPreferenceFragment"
+ settings:controller="com.android.settings.accessibility.MagnificationPreferenceController"
+ android:fragment="com.android.settings.accessibility.MagnificationPreferenceFragment"
android:key="screen_magnification_preference"
- android:title="@string/accessibility_screen_magnification_title"
- android:summary="@string/accessibility_preference_magnification_summary" />
+ android:summary="@string/accessibility_preference_magnification_summary"
+ android:title="@string/accessibility_screen_magnification_title" />
<Preference
- android:fragment=
- "com.android.settings.display.FontSizePreferenceFragmentForSetupWizard"
+ android:fragment="com.android.settings.display.FontSizePreferenceFragmentForSetupWizard"
android:key="font_size_preference"
- android:title="@string/title_font_size"
- android:summary="@string/short_summary_font_size" />
+ android:summary="@string/short_summary_font_size"
+ android:title="@string/title_font_size" />
<com.android.settings.display.ScreenZoomPreference
android:fragment="com.android.settings.display.ScreenZoomPreferenceFragmentForSetupWizard"
android:key="force_density_preference"
- android:title="@string/screen_zoom_title"
- android:summary="@string/screen_zoom_short_summary" />
+ android:summary="@string/screen_zoom_short_summary"
+ android:title="@string/screen_zoom_title" />
<Preference
- android:fragment=
- "com.android.settings.accessibility.ToggleSelectToSpeakPreferenceFragmentForSetupWizard"
+ android:fragment="com.android.settings.accessibility.ToggleSelectToSpeakPreferenceFragmentForSetupWizard"
android:key="select_to_speak_preference"
- android:summary="@string/select_to_speak_summary"
- android:persistent="true" />
+ android:persistent="true"
+ android:summary="@string/select_to_speak_summary" />
<Preference
- android:fragment=
- "com.android.settings.accessibility.ToggleScreenReaderPreferenceFragmentForSetupWizard"
+ android:fragment="com.android.settings.accessibility.ToggleScreenReaderPreferenceFragmentForSetupWizard"
android:key="screen_reader_preference"
- android:summary="@string/talkback_summary"
- android:persistent="true" />
+ android:persistent="true"
+ android:summary="@string/talkback_summary" />
</PreferenceScreen>
diff --git a/res/xml/manage_external_storage_permission_details.xml b/res/xml/manage_external_storage_permission_details.xml
new file mode 100644
index 0000000..b540ff6
--- /dev/null
+++ b/res/xml/manage_external_storage_permission_details.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ android:key="manage_external_storage_permission_details"
+ android:title="@string/manage_external_storage_title">
+
+ <SwitchPreference
+ android:key="app_ops_settings_switch"
+ android:title="@string/permit_manage_external_storage"/>
+
+ <Preference
+ android:summary="@string/allow_manage_external_storage_description"
+ android:selectable="false"/>
+
+</PreferenceScreen>
diff --git a/res/xml/special_access.xml b/res/xml/special_access.xml
index 25422bf..e511d17 100644
--- a/res/xml/special_access.xml
+++ b/res/xml/special_access.xml
@@ -21,6 +21,15 @@
android:title="@string/special_access">
<Preference
+ android:key="manage_external_storage"
+ android:title="@string/manage_external_storage_title"
+ android:fragment="com.android.settings.applications.manageapplications.ManageApplications">
+ <extra
+ android:name="classname"
+ android:value="com.android.settings.Settings$ManageExternalStorageActivity" />
+ </Preference>
+
+ <Preference
android:key="high_power_apps"
android:title="@string/high_power_apps"
android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 50caf32..d5c0871 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -138,6 +138,7 @@
public static class MemorySettingsActivity extends SettingsActivity { /* empty */ }
public static class AppMemoryUsageActivity extends SettingsActivity { /* empty */ }
public static class OverlaySettingsActivity extends SettingsActivity { /* empty */ }
+ public static class ManageExternalStorageActivity extends SettingsActivity { /* empty */ }
public static class WriteSettingsActivity extends SettingsActivity { /* empty */ }
public static class ChangeWifiStateActivity extends SettingsActivity { /* empty */ }
public static class AppDrawOverlaySettingsActivity extends SettingsActivity { /* empty */ }
diff --git a/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizard.java b/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizard.java
index 64ed486..159e609 100644
--- a/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizard.java
+++ b/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizard.java
@@ -49,7 +49,6 @@
"screen_magnification_preference";
private static final String SCREEN_READER_PREFERENCE = "screen_reader_preference";
private static final String SELECT_TO_SPEAK_PREFERENCE = "select_to_speak_preference";
- private static final String FONT_SIZE_PREFERENCE = "font_size_preference";
// Package names and service names used to identify screen reader and SelectToSpeak services.
private static final String SCREEN_READER_PACKAGE_NAME = "com.google.android.marvin.talkback";
@@ -175,16 +174,11 @@
}
private static void configureMagnificationPreferenceIfNeeded(Preference preference) {
- // Some devices support only a single magnification mode. In these cases, we redirect to
- // the magnification mode's UI directly, rather than showing a PreferenceScreen with a
- // single list item.
final Context context = preference.getContext();
- if (!MagnificationPreferenceFragment.isApplicable(context.getResources())) {
- preference.setFragment(
- ToggleScreenMagnificationPreferenceFragmentForSetupWizard.class.getName());
- final Bundle extras = preference.getExtras();
- MagnificationGesturesPreferenceController
- .populateMagnificationGesturesPreferenceExtras(extras, context);
- }
+ preference.setFragment(
+ ToggleScreenMagnificationPreferenceFragmentForSetupWizard.class.getName());
+ final Bundle extras = preference.getExtras();
+ MagnificationGesturesPreferenceController
+ .populateMagnificationGesturesPreferenceExtras(extras, context);
}
}
diff --git a/src/com/android/settings/accessibility/MagnificationPreferenceController.java b/src/com/android/settings/accessibility/MagnificationPreferenceController.java
index 58eb227..8b214a2 100644
--- a/src/com/android/settings/accessibility/MagnificationPreferenceController.java
+++ b/src/com/android/settings/accessibility/MagnificationPreferenceController.java
@@ -67,14 +67,9 @@
}
private void configureMagnificationPreferenceIfNeeded() {
- // Some devices support only a single magnification mode. In these cases, we redirect to
- // the magnification mode's UI directly, rather than showing a PreferenceScreen with a
- // single list item.
- if (!MagnificationPreferenceFragment.isApplicable(mContext.getResources())) {
- mPreference.setFragment(ToggleScreenMagnificationPreferenceFragment.class.getName());
- final Bundle extras = mPreference.getExtras();
- MagnificationGesturesPreferenceController
- .populateMagnificationGesturesPreferenceExtras(extras, mContext);
- }
+ mPreference.setFragment(ToggleScreenMagnificationPreferenceFragment.class.getName());
+ final Bundle extras = mPreference.getExtras();
+ MagnificationGesturesPreferenceController
+ .populateMagnificationGesturesPreferenceExtras(extras, mContext);
}
}
diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
index 8693690..bfc2360 100644
--- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
@@ -38,17 +38,16 @@
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.RelativeLayout.LayoutParams;
-import android.widget.Switch;
import android.widget.VideoView;
import androidx.appcompat.app.AlertDialog;
import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityUtil.PreferredShortcutType;
-import com.android.settings.widget.SwitchBar;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -57,8 +56,7 @@
import java.util.StringJoiner;
public class ToggleScreenMagnificationPreferenceFragment extends
- ToggleFeaturePreferenceFragment implements SwitchBar.OnSwitchChangeListener,
- ShortcutPreference.OnClickListener {
+ ToggleFeaturePreferenceFragment implements ShortcutPreference.OnClickListener {
private static final String SETTINGS_KEY = "screen_magnification_settings";
private static final String EXTRA_SHORTCUT_TYPE = "shortcutType";
@@ -78,6 +76,8 @@
private static final TextUtils.SimpleStringSplitter sStringColonSplitter =
new TextUtils.SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
+ protected Preference mConfigWarningPreference;
+ protected VideoPreference mVideoPreference;
protected class VideoPreference extends Preference {
private ImageView mVideoBackgroundView;
private OnGlobalLayoutListener mLayoutListener;
@@ -154,25 +154,35 @@
}
}
- protected VideoPreference mVideoPreference;
- protected Preference mConfigWarningPreference;
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ getActivity().setTitle(R.string.accessibility_screen_magnification_title);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
mVideoPreference = new VideoPreference(getPrefContext());
mVideoPreference.setSelectable(false);
mVideoPreference.setPersistent(false);
mVideoPreference.setLayoutResource(R.layout.magnification_video_preference);
+ final PreferenceCategory optionCategory = new PreferenceCategory(getPrefContext());
+ optionCategory.setTitle(R.string.accessibility_screen_option);
+
+ initShortcutPreference(savedInstanceState);
+
final Preference settingsPreference = new Preference(getPrefContext());
- final String SettingsText = getString(R.string.settings_button);
- settingsPreference.setTitle(SettingsText);
+ settingsPreference.setTitle(R.string.accessibility_magnification_service_settings_title);
settingsPreference.setKey(SETTINGS_KEY);
settingsPreference.setFragment(MagnificationSettingsFragment.class.getName());
settingsPreference.setPersistent(false);
+ final PreferenceCategory aboutCategory = new PreferenceCategory(getPrefContext());
+ aboutCategory.setTitle(R.string.accessibility_screen_magnification_about);
+
mConfigWarningPreference = new Preference(getPrefContext());
mConfigWarningPreference.setSelectable(false);
mConfigWarningPreference.setPersistent(false);
@@ -182,17 +192,15 @@
final PreferenceScreen preferenceScreen = getPreferenceManager().getPreferenceScreen();
preferenceScreen.setOrderingAsAdded(false);
mVideoPreference.setOrder(0);
- settingsPreference.setOrder(1);
- mConfigWarningPreference.setOrder(2);
+ optionCategory.setOrder(1);
+ aboutCategory.setOrder(2);
preferenceScreen.addPreference(mVideoPreference);
- preferenceScreen.addPreference(settingsPreference);
- preferenceScreen.addPreference(mConfigWarningPreference);
- }
+ preferenceScreen.addPreference(optionCategory);
+ optionCategory.addPreference(mShortcutPreference);
+ optionCategory.addPreference(settingsPreference);
+ preferenceScreen.addPreference(aboutCategory);
+ aboutCategory.addPreference(mConfigWarningPreference);
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- initShortcutPreference(savedInstanceState);
return super.onCreateView(inflater, container, savedInstanceState);
}
@@ -378,11 +386,6 @@
}
@Override
- public void onSwitchChanged(Switch switchView, boolean isChecked) {
- onPreferenceToggled(mPreferenceKey, isChecked);
- }
-
- @Override
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
if (enabled && TextUtils.equals(
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
@@ -399,22 +402,8 @@
protected void onInstallSwitchBarToggleSwitch() {
super.onInstallSwitchBarToggleSwitch();
- mSwitchBar.setCheckedInternal(
- MagnificationPreferenceFragment.isChecked(getContentResolver(), mPreferenceKey));
- mSwitchBar.addOnSwitchChangeListener(this);
- }
-
- @Override
- protected void onRemoveSwitchBarToggleSwitch() {
- super.onRemoveSwitchBarToggleSwitch();
- mSwitchBar.removeOnSwitchChangeListener(this);
- }
-
- @Override
- protected void updateSwitchBarText(SwitchBar switchBar) {
- final String switchBarText = getString(R.string.accessibility_service_master_switch_title,
- getString(R.string.accessibility_screen_magnification_title));
- switchBar.setSwitchBarText(switchBarText, switchBarText);
+ // Magnify is temporary-use app which uses shortcut to magnify screen, not by toggle.
+ mSwitchBar.hide();
}
@Override
@@ -429,13 +418,6 @@
} else {
mVideoPreference.setVisible(false);
}
-
- if (arguments.containsKey(AccessibilitySettings.EXTRA_TITLE_RES)) {
- final int titleRes = arguments.getInt(AccessibilitySettings.EXTRA_TITLE_RES);
- if (titleRes > 0) {
- getActivity().setTitle(titleRes);
- }
- }
}
@Override
@@ -443,15 +425,35 @@
if (preference.getChecked()) {
// TODO(b/142531156): Replace PreferredShortcutType.SOFTWARE value with dialog shortcut
// preferred key.
+ optInMagnificationValueToSettings(getContext(), PreferredShortcutType.SOFTWARE);
+
// TODO(b/142531156): ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED need to be treated
// as special case in this file.
- optInMagnificationValueToSettings(getContext(), PreferredShortcutType.SOFTWARE);
+ if ((mPreferredShortcutType & PreferredShortcutType.SOFTWARE)
+ == PreferredShortcutType.SOFTWARE) {
+ MagnificationPreferenceFragment.setChecked(getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
+ /* isChecked= */ true);
+ }
+ if ((mPreferredShortcutType & PreferredShortcutType.TRIPLETAP)
+ == PreferredShortcutType.TRIPLETAP) {
+ MagnificationPreferenceFragment.setChecked(getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
+ /* isChecked= */ true);
+ }
} else {
// TODO(b/142531156): Replace PreferredShortcutType.SOFTWARE value with dialog shortcut
// preferred key.
+ optOutMagnificationValueFromSettings(getContext(), PreferredShortcutType.SOFTWARE);
+
// TODO(b/142531156): ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED need to be treated
// as special case in this file.
- optOutMagnificationValueFromSettings(getContext(), PreferredShortcutType.SOFTWARE);
+ MagnificationPreferenceFragment.setChecked(getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
+ /* isChecked= */ false);
+ MagnificationPreferenceFragment.setChecked(getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
+ /* isChecked= */ false);
}
}
@@ -480,10 +482,7 @@
mShortcutPreference.setTitle(R.string.accessibility_magnification_shortcut_title);
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
mShortcutPreference.setOnClickListener(this);
- // Put the shortcutPreference before videoPreference.
- mShortcutPreference.setOrder(mVideoPreference.getOrder() - 1);
// TODO(b/142530063): Check the new setting key to decide which summary should be shown.
- preferenceScreen.addPreference(mShortcutPreference);
}
private void updateShortcutPreference() {
diff --git a/src/com/android/settings/applications/AppStateAppOpsBridge.java b/src/com/android/settings/applications/AppStateAppOpsBridge.java
index 0e3ee2d..3dbdbe9 100755
--- a/src/com/android/settings/applications/AppStateAppOpsBridge.java
+++ b/src/com/android/settings/applications/AppStateAppOpsBridge.java
@@ -328,7 +328,7 @@
public boolean isPermissible() {
// defining the default behavior as permissible as long as the package requested this
// permission (this means pre-M gets approval during install time; M apps gets approval
- // during runtime.
+ // during runtime).
if (appOpMode == AppOpsManager.MODE_DEFAULT) {
return staticPermissionGranted;
}
diff --git a/src/com/android/settings/applications/AppStateManageExternalStorageBridge.java b/src/com/android/settings/applications/AppStateManageExternalStorageBridge.java
new file mode 100644
index 0000000..5a69035
--- /dev/null
+++ b/src/com/android/settings/applications/AppStateManageExternalStorageBridge.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.content.Context;
+
+import com.android.settingslib.applications.ApplicationsState;
+
+/**
+ * Retrieves information from {@link AppOpsManager} and {@link android.content.pm.PackageManager}
+ * regarding {@link AppOpsManager#OP_MANAGE_EXTERNAL_STORAGE} and
+ * {@link Manifest.permission#MANAGE_EXTERNAL_STORAGE}.
+ */
+public class AppStateManageExternalStorageBridge extends AppStateAppOpsBridge {
+ private static final int APP_OPS_OP_CODE = AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE;
+ private static final String[] PERMISSIONS = {
+ Manifest.permission.MANAGE_EXTERNAL_STORAGE
+ };
+
+ public AppStateManageExternalStorageBridge(Context context, ApplicationsState appState,
+ Callback callback) {
+ super(context, appState, callback, APP_OPS_OP_CODE, PERMISSIONS);
+ }
+
+ @Override
+ protected void updateExtraInfo(ApplicationsState.AppEntry app, String pkg, int uid) {
+ app.extraInfo = getManageExternalStoragePermState(pkg, uid);
+ }
+
+ /**
+ * Returns the MANAGE_EXTERNAL_STORAGE {@link AppStateAppOpsBridge.PermissionState} object
+ * associated with the given package and user.
+ */
+ public PermissionState getManageExternalStoragePermState(String pkg, int uid) {
+ return getPermissionInfo(pkg, uid);
+ }
+
+ /**
+ * Used by {@link com.android.settings.applications.manageapplications.AppFilterRegistry} to
+ * determine which apps get to appear on the Special App Access list.
+ */
+ public static final ApplicationsState.AppFilter FILTER_MANAGE_EXTERNAL_STORAGE =
+ new ApplicationsState.AppFilter() {
+ @Override
+ public void init() {
+ }
+
+ @Override
+ public boolean filterApp(ApplicationsState.AppEntry info) {
+ // If extraInfo != null, it means that the app has declared
+ // Manifest.permission.MANAGE_EXTERNAL_STORAGE and therefore it should appear on our
+ // list
+ return info.extraInfo != null;
+ }
+ };
+}
diff --git a/src/com/android/settings/applications/appinfo/ManageExternalStorageDetails.java b/src/com/android/settings/applications/appinfo/ManageExternalStorageDetails.java
new file mode 100644
index 0000000..63ce440
--- /dev/null
+++ b/src/com/android/settings/applications/appinfo/ManageExternalStorageDetails.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.applications.appinfo;
+
+import android.app.AppOpsManager;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.appcompat.app.AlertDialog;
+import androidx.preference.Preference;
+import androidx.preference.Preference.OnPreferenceChangeListener;
+import androidx.preference.Preference.OnPreferenceClickListener;
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.R;
+import com.android.settings.applications.AppInfoWithHeader;
+import com.android.settings.applications.AppStateAppOpsBridge.PermissionState;
+import com.android.settings.applications.AppStateManageExternalStorageBridge;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.applications.ApplicationsState.AppEntry;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+
+/**
+ * Class for displaying app info related to {@link AppOpsManager#OP_MANAGE_EXTERNAL_STORAGE}.
+ */
+public class ManageExternalStorageDetails extends AppInfoWithHeader implements
+ OnPreferenceChangeListener, OnPreferenceClickListener {
+
+ private static final String KEY_APP_OPS_SETTINGS_SWITCH = "app_ops_settings_switch";
+
+ private AppStateManageExternalStorageBridge mBridge;
+ private AppOpsManager mAppOpsManager;
+ private SwitchPreference mSwitchPref;
+ private PermissionState mPermissionState;
+ private MetricsFeatureProvider mMetricsFeatureProvider;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Context context = getActivity();
+ mBridge = new AppStateManageExternalStorageBridge(context, mState, null);
+ mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+
+ // initialize preferences
+ addPreferencesFromResource(R.xml.manage_external_storage_permission_details);
+ mSwitchPref = findPreference(KEY_APP_OPS_SETTINGS_SWITCH);
+
+ // install event listeners
+ mSwitchPref.setOnPreferenceChangeListener(this);
+
+ mMetricsFeatureProvider =
+ FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater,
+ ViewGroup container,
+ Bundle savedInstanceState) {
+ // if we don't have a package info, show a page saying this is unsupported
+ if (mPackageInfo == null) {
+ return inflater.inflate(R.layout.manage_applications_apps_unsupported, null);
+ }
+ return super.onCreateView(inflater, container, savedInstanceState);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mBridge.release();
+ }
+
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ return false;
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (preference == mSwitchPref) {
+ if (mPermissionState != null && !newValue.equals(mPermissionState.isPermissible())) {
+ setManageExternalStorageState((Boolean) newValue);
+ refreshUi();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Toggles {@link AppOpsManager#OP_MANAGE_EXTERNAL_STORAGE} for the app.
+ */
+ private void setManageExternalStorageState(boolean newState) {
+ logSpecialPermissionChange(newState, mPackageName);
+ mAppOpsManager.setMode(AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE,
+ mPackageInfo.applicationInfo.uid, mPackageName, newState
+ ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_ERRORED);
+ }
+
+ private void logSpecialPermissionChange(boolean newState, String packageName) {
+ int logCategory = newState ? SettingsEnums.APP_SPECIAL_PERMISSION_MANAGE_EXT_STRG_ALLOW
+ : SettingsEnums.APP_SPECIAL_PERMISSION_MANAGE_EXT_STRG_DENY;
+
+ mMetricsFeatureProvider.action(
+ mMetricsFeatureProvider.getAttribution(getActivity()),
+ logCategory,
+ getMetricsCategory(),
+ packageName,
+ 0 /* value */);
+ }
+
+ @Override
+ protected boolean refreshUi() {
+ if (mPackageInfo == null) {
+ return true;
+ }
+
+ mPermissionState = mBridge.getManageExternalStoragePermState(mPackageName,
+ mPackageInfo.applicationInfo.uid);
+
+ mSwitchPref.setChecked(mPermissionState.isPermissible());
+
+ // you cannot ask a user to grant you a permission you did not have!
+ mSwitchPref.setEnabled(mPermissionState.permissionDeclared);
+
+ return true;
+ }
+
+ @Override
+ protected AlertDialog createDialog(int id, int errorCode) {
+ return null;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.MANAGE_EXTERNAL_STORAGE;
+ }
+
+ /**
+ * Returns the string that states whether whether the app has access to
+ * {@link AppOpsManager#OP_MANAGE_EXTERNAL_STORAGE}.
+ * <p>This string is used in the "All files access" page that displays all apps requesting
+ * {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
+ */
+ public static CharSequence getSummary(Context context, AppEntry entry) {
+ final PermissionState state;
+ if (entry.extraInfo instanceof PermissionState) {
+ state = (PermissionState) entry.extraInfo;
+ } else {
+ state = new AppStateManageExternalStorageBridge(context, null, null)
+ .getManageExternalStoragePermState(entry.info.packageName, entry.info.uid);
+ }
+
+ return getSummary(context, state);
+ }
+
+ private static CharSequence getSummary(Context context, PermissionState state) {
+ return context.getString(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 250dce0..58907a7 100644
--- a/src/com/android/settings/applications/manageapplications/AppFilterRegistry.java
+++ b/src/com/android/settings/applications/manageapplications/AppFilterRegistry.java
@@ -20,6 +20,7 @@
import com.android.settings.R;
import com.android.settings.applications.AppStateInstallAppsBridge;
+import com.android.settings.applications.AppStateManageExternalStorageBridge;
import com.android.settings.applications.AppStateNotificationBridge;
import com.android.settings.applications.AppStateOverlayBridge;
import com.android.settings.applications.AppStatePowerBridge;
@@ -71,14 +72,15 @@
public static final int FILTER_APPS_INSTALL_SOURCES = 13;
public static final int FILTER_APP_CAN_CHANGE_WIFI_STATE = 15;
public static final int FILTER_APPS_BLOCKED = 16;
- // Next id: 17
+ public static final int FILTER_MANAGE_EXTERNAL_STORAGE = 17;
+ // Next id: 18. If you add an entry here, length of mFilters should be updated
private static AppFilterRegistry sRegistry;
private final AppFilterItem[] mFilters;
private AppFilterRegistry() {
- mFilters = new AppFilterItem[17];
+ mFilters = new AppFilterItem[18];
// High power whitelist, on
mFilters[FILTER_APPS_POWER_WHITELIST] = new AppFilterItem(
@@ -178,6 +180,11 @@
AppStateNotificationBridge.FILTER_APP_NOTIFICATION_BLOCKED,
FILTER_APPS_BLOCKED,
R.string.filter_notif_blocked_apps);
+
+ mFilters[FILTER_MANAGE_EXTERNAL_STORAGE] = new AppFilterItem(
+ AppStateManageExternalStorageBridge.FILTER_MANAGE_EXTERNAL_STORAGE,
+ FILTER_MANAGE_EXTERNAL_STORAGE,
+ R.string.filter_manage_external_storage);
}
public static AppFilterRegistry getInstance() {
@@ -204,6 +211,8 @@
return FILTER_APP_CAN_CHANGE_WIFI_STATE;
case ManageApplications.LIST_TYPE_NOTIFICATION:
return FILTER_APPS_RECENT;
+ case ManageApplications.LIST_MANAGE_EXTERNAL_STORAGE:
+ return FILTER_MANAGE_EXTERNAL_STORAGE;
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 02e42e2..d38893f 100644
--- a/src/com/android/settings/applications/manageapplications/ManageApplications.java
+++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java
@@ -88,6 +88,7 @@
import com.android.settings.SettingsActivity;
import com.android.settings.Utils;
import com.android.settings.applications.AppInfoBase;
+import com.android.settings.applications.AppStateManageExternalStorageBridge;
import com.android.settings.applications.AppStateAppOpsBridge.PermissionState;
import com.android.settings.applications.AppStateBaseBridge;
import com.android.settings.applications.AppStateInstallAppsBridge;
@@ -100,6 +101,7 @@
import com.android.settings.applications.AppStateWriteSettingsBridge;
import com.android.settings.applications.AppStorageSettings;
import com.android.settings.applications.UsageAccessDetails;
+import com.android.settings.applications.appinfo.ManageExternalStorageDetails;
import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
import com.android.settings.applications.appinfo.DrawOverlayDetails;
import com.android.settings.applications.appinfo.ExternalSourcesDetails;
@@ -224,6 +226,7 @@
public static final int LIST_TYPE_MOVIES = 10;
public static final int LIST_TYPE_PHOTOGRAPHY = 11;
public static final int LIST_TYPE_WIFI_ACCESS = 13;
+ public static final int LIST_MANAGE_EXTERNAL_STORAGE = 14;
// List types that should show instant apps.
public static final Set<Integer> LIST_TYPES_WITH_INSTANT = new ArraySet<>(Arrays.asList(
@@ -311,6 +314,9 @@
} else if (className.equals(Settings.ChangeWifiStateActivity.class.getName())) {
mListType = LIST_TYPE_WIFI_ACCESS;
screenTitle = R.string.change_wifi_state_title;
+ } else if (className.equals(Settings.ManageExternalStorageActivity.class.getName())) {
+ mListType = LIST_MANAGE_EXTERNAL_STORAGE;
+ screenTitle = R.string.manage_external_storage_title;
} else if (className.equals(Settings.NotificationAppListActivity.class.getName())) {
mListType = LIST_TYPE_NOTIFICATION;
mUsageStatsManager = IUsageStatsManager.Stub.asInterface(
@@ -538,6 +544,8 @@
return SettingsEnums.MANAGE_EXTERNAL_SOURCES;
case LIST_TYPE_WIFI_ACCESS:
return SettingsEnums.CONFIGURE_WIFI;
+ case LIST_MANAGE_EXTERNAL_STORAGE:
+ return SettingsEnums.MANAGE_EXTERNAL_STORAGE;
default:
return SettingsEnums.PAGE_UNKNOWN;
}
@@ -640,6 +648,10 @@
startAppInfoFragment(ChangeWifiStateDetails.class,
R.string.change_wifi_state_title);
break;
+ case LIST_MANAGE_EXTERNAL_STORAGE:
+ startAppInfoFragment(ManageExternalStorageDetails.class,
+ R.string.manage_external_storage_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.
@@ -713,6 +725,8 @@
return R.string.help_uri_apps_photography;
case LIST_TYPE_WIFI_ACCESS:
return R.string.help_uri_apps_wifi_access;
+ case LIST_MANAGE_EXTERNAL_STORAGE:
+ return R.string.help_uri_manage_external_storage;
default:
case LIST_TYPE_MAIN:
return R.string.help_uri_apps;
@@ -1031,6 +1045,8 @@
mExtraInfoBridge = new AppStateInstallAppsBridge(mContext, mState, this);
} else if (mManageApplications.mListType == LIST_TYPE_WIFI_ACCESS) {
mExtraInfoBridge = new AppStateChangeWifiStateBridge(mContext, mState, this);
+ } else if (mManageApplications.mListType == LIST_MANAGE_EXTERNAL_STORAGE) {
+ mExtraInfoBridge = new AppStateManageExternalStorageBridge(mContext, mState, this);
} else {
mExtraInfoBridge = null;
}
@@ -1486,6 +1502,9 @@
case LIST_TYPE_WIFI_ACCESS:
holder.setSummary(ChangeWifiStateDetails.getSummary(mContext, entry));
break;
+ case LIST_MANAGE_EXTERNAL_STORAGE:
+ holder.setSummary(ManageExternalStorageDetails.getSummary(mContext, entry));
+ break;
default:
holder.updateSizeText(entry, mManageApplications.mInvalidSizeStr, mWhichSize);
break;
diff --git a/src/com/android/settings/network/telephony/DisabledSubscriptionController.java b/src/com/android/settings/network/telephony/DisabledSubscriptionController.java
index cd51735..e6cace4 100644
--- a/src/com/android/settings/network/telephony/DisabledSubscriptionController.java
+++ b/src/com/android/settings/network/telephony/DisabledSubscriptionController.java
@@ -73,7 +73,7 @@
return;
}
// TODO b/135222940: re-evaluate whether to use mSubscriptionManager#isSubscriptionEnabled
- mCategory.setVisible(mSubscriptionManager.isActiveSubId(mSubId));
+ mCategory.setVisible(mSubscriptionManager.isActiveSubscriptionId(mSubId));
}
@Override
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSwitchController.java b/src/com/android/settings/network/telephony/MobileNetworkSwitchController.java
index 9d5c7cb..a498d47 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSwitchController.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkSwitchController.java
@@ -78,7 +78,7 @@
mSwitchBar.addOnSwitchChangeListener((switchView, isChecked) -> {
// TODO b/135222940: re-evaluate whether to use
// mSubscriptionManager#isSubscriptionEnabled
- if (mSubscriptionManager.isActiveSubId(mSubId) != isChecked
+ if (mSubscriptionManager.isActiveSubscriptionId(mSubId) != isChecked
&& (!mSubscriptionManager.setSubscriptionEnabled(mSubId, isChecked))) {
mSwitchBar.setChecked(!isChecked);
}
@@ -106,7 +106,7 @@
mSwitchBar.hide();
} else {
mSwitchBar.show();
- mSwitchBar.setChecked(mSubscriptionManager.isActiveSubId(mSubId));
+ mSwitchBar.setChecked(mSubscriptionManager.isActiveSubscriptionId(mSubId));
}
}
diff --git a/src/com/android/settings/sim/SimSelectNotification.java b/src/com/android/settings/sim/SimSelectNotification.java
index 0b7e652..ae36f35 100644
--- a/src/com/android/settings/sim/SimSelectNotification.java
+++ b/src/com/android/settings/sim/SimSelectNotification.java
@@ -103,7 +103,7 @@
SubscriptionManager subscriptionManager = ((SubscriptionManager) context.getSystemService(
Context.TELEPHONY_SUBSCRIPTION_SERVICE));
- if (!subscriptionManager.isActiveSubId(subId)) {
+ if (!subscriptionManager.isActiveSubscriptionId(subId)) {
Log.w(TAG, "onEnableMmsDataRequest invalid sub ID " + subId);
return;
}
diff --git a/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragment.java b/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragment.java
index d180df7..c925e7b 100644
--- a/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragment.java
+++ b/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragment.java
@@ -62,12 +62,6 @@
public class AddAppNetworksFragment extends InstrumentedFragment {
public static final String TAG = "AddAppNetworksFragment";
- // Security types of a requested or saved network.
- private static final String SECURITY_NO_PASSWORD = "nopass";
- private static final String SECURITY_WEP = "wep";
- private static final String SECURITY_WPA_PSK = "wpa";
- private static final String SECURITY_SAE = "sae";
-
// Possible result values in each item of the returned result list, which is used
// to inform the caller APP the processed result of each specified network.
@VisibleForTesting
@@ -269,25 +263,9 @@
}
}
- /**
- * Classify security type into following types:
- * 1. {@Code SECURITY_NO_PASSWORD}: No password network or OWE network.
- * 2. {@Code SECURITY_WEP}: Traditional WEP encryption network.
- * 3. {@Code SECURITY_WPA_PSK}: WPA/WPA2 preshare key type.
- * 4. {@Code SECURITY_SAE}: SAE type network.
- */
- private String getSecurityType(WifiConfiguration config) {
- if (config.allowedKeyManagement.get(KeyMgmt.SAE)) {
- return SECURITY_SAE;
- }
- if (config.allowedKeyManagement.get(KeyMgmt.OWE)) {
- return SECURITY_NO_PASSWORD;
- }
- if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK) || config.allowedKeyManagement.get(
- KeyMgmt.WPA2_PSK)) {
- return SECURITY_WPA_PSK;
- }
- return (config.wepKeys[0] == null) ? SECURITY_NO_PASSWORD : SECURITY_WEP;
+ private String getWepKey(WifiConfiguration config) {
+ return (config.wepTxKeyIndex >= 0 && config.wepTxKeyIndex < config.wepKeys.length)
+ ? config.wepKeys[config.wepTxKeyIndex] : null;
}
/**
@@ -306,41 +284,40 @@
boolean foundInSavedList;
int networkPositionInBundle = 0;
- for (WifiConfiguration specifiecConfig : mAllSpecifiedNetworksList) {
+ for (WifiConfiguration specifiedConfig : mAllSpecifiedNetworksList) {
foundInSavedList = false;
- final String displayedSsid = removeDoubleQuotes(specifiecConfig.SSID);
- final String ssidWithQuotation = addQuotationIfNeeded(specifiecConfig.SSID);
- final String securityType = getSecurityType(specifiecConfig);
+ final String displayedSsid = removeDoubleQuotes(specifiedConfig.SSID);
+ final String ssidWithQuotation = addQuotationIfNeeded(specifiedConfig.SSID);
+ final int authType = specifiedConfig.getAuthType();
for (WifiConfiguration privilegedWifiConfiguration : savedWifiConfigurations) {
- final String savedSecurityType = getSecurityType(privilegedWifiConfiguration);
-
// If SSID or security type is different, should be new network or need to be
// updated network.
if (!ssidWithQuotation.equals(privilegedWifiConfiguration.SSID)
- || !securityType.equals(savedSecurityType)) {
+ || authType != privilegedWifiConfiguration.getAuthType()) {
continue;
}
// If specified network and saved network have same security types, we'll check
// more information according to their security type to judge if they are same.
- switch (securityType) {
- case SECURITY_NO_PASSWORD:
+ switch (authType) {
+ case KeyMgmt.NONE:
+ final String wep = getWepKey(specifiedConfig);
+ final String savedWep = getWepKey(privilegedWifiConfiguration);
+ foundInSavedList = TextUtils.equals(wep, savedWep);
+ break;
+ case KeyMgmt.OWE:
foundInSavedList = true;
break;
- case SECURITY_WEP:
- if (specifiecConfig.wepKeys[0].equals(
- privilegedWifiConfiguration.wepKeys[0])) {
- foundInSavedList = true;
- }
- break;
- case SECURITY_WPA_PSK:
- case SECURITY_SAE:
- if (specifiecConfig.preSharedKey.equals(
+ case KeyMgmt.WPA_PSK:
+ case KeyMgmt.WPA2_PSK:
+ case KeyMgmt.SAE:
+ if (specifiedConfig.preSharedKey.equals(
privilegedWifiConfiguration.preSharedKey)) {
foundInSavedList = true;
}
break;
+ // TODO: Check how to judge enterprise type.
default:
break;
}
@@ -353,7 +330,7 @@
} else {
// Prepare to add to UI list to show to user
UiConfigurationItem uiConfigurationIcon = new UiConfigurationItem(displayedSsid,
- specifiecConfig, networkPositionInBundle);
+ specifiedConfig, networkPositionInBundle);
mUiToRequestedList.add(uiConfigurationIcon);
}
networkPositionInBundle++;
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/ManageExternalStorageDetailsTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/ManageExternalStorageDetailsTest.java
new file mode 100644
index 0000000..ed85c01
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/ManageExternalStorageDetailsTest.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.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.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.AppOpsManager;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.applications.AppStateAppOpsBridge.PermissionState;
+import com.android.settings.applications.AppStateManageExternalStorageBridge;
+import com.android.settings.testutils.shadow.ShadowUserManager;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.HashMap;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowUserManager.class})
+public class ManageExternalStorageDetailsTest {
+
+ @Mock
+ private AppOpsManager mAppOpsManager;
+ @Mock
+ private SwitchPreference mSwitchPref;
+ @Mock
+ private MetricsFeatureProvider mMetricsFeatureProvider;
+ @Mock
+ private AppStateManageExternalStorageBridge mBridge;
+
+ private ManageExternalStorageDetails mFragment;
+
+ private final HashMap<String, Integer> mPkgToOpModeMap = new HashMap<>();
+ private final HashMap<Integer, Integer> mUidToOpModeMap = new HashMap<>();
+
+ @Before
+ public void setUp() {
+ // Reset the global trackers
+ mPkgToOpModeMap.clear();
+ mUidToOpModeMap.clear();
+
+ //Start the mockin'
+ MockitoAnnotations.initMocks(this);
+
+ mFragment = new ManageExternalStorageDetails();
+ ReflectionHelpers.setField(mFragment, "mAppOpsManager", mAppOpsManager);
+ ReflectionHelpers.setField(mFragment, "mSwitchPref", mSwitchPref);
+ ReflectionHelpers.setField(mFragment, "mBridge", mBridge);
+ ReflectionHelpers.setField(mFragment, "mMetricsFeatureProvider",
+ mMetricsFeatureProvider);
+
+ mockAppOpsOperations();
+ }
+
+ @Test
+ public void onPreferenceChange_enableManageExternalStorage_shouldTriggerAppOpsManager() {
+ // Inject mock package details
+ final int mockUid = 23333;
+ final String mockPkgName = "com.mock.pkg.1";
+ PackageInfo pkgInfo = mock(PackageInfo.class);
+ pkgInfo.applicationInfo = new ApplicationInfo();
+ pkgInfo.applicationInfo.uid = mockUid;
+
+ ReflectionHelpers.setField(mFragment, "mPackageInfo", pkgInfo);
+ ReflectionHelpers.setField(mFragment, "mPackageName", mockPkgName);
+
+ // Set the initial state to be disabled
+ injectPermissionState(false);
+
+ // Simulate a preference change
+ mFragment.onPreferenceChange(mSwitchPref, /* newValue */ true);
+
+ // Verify that mAppOpsManager was called to allow the app-op
+ verify(mAppOpsManager, times(1))
+ .setMode(anyInt(), anyInt(), nullable(String.class), anyInt());
+ assertThat(mPkgToOpModeMap).containsExactly(mockPkgName, AppOpsManager.MODE_ALLOWED);
+ assertThat(mUidToOpModeMap).containsExactly(mockUid, AppOpsManager.MODE_ALLOWED);
+
+ // Verify the mSwitchPref was enabled
+ ArgumentCaptor<Boolean> acSetEnabled = ArgumentCaptor.forClass(Boolean.class);
+ verify(mSwitchPref, times(1)).setEnabled(acSetEnabled.capture());
+ assertThat(acSetEnabled.getAllValues()).containsExactly(true);
+
+ // Verify that mSwitchPref was toggled to on
+ ArgumentCaptor<Boolean> acSetChecked = ArgumentCaptor.forClass(Boolean.class);
+ verify(mSwitchPref, times(1)).setChecked(acSetChecked.capture());
+ assertThat(acSetChecked.getAllValues()).containsExactly(true);
+ }
+
+ @Test
+ public void onPreferenceChange_disableManageExternalStorage_shouldTriggerAppOpsManager() {
+ // Inject mock package details
+ final int mockUid = 24444;
+ final String mockPkgName = "com.mock.pkg.2";
+ PackageInfo pkgInfo = mock(PackageInfo.class);
+ pkgInfo.applicationInfo = new ApplicationInfo();
+ pkgInfo.applicationInfo.uid = mockUid;
+
+ ReflectionHelpers.setField(mFragment, "mPackageInfo", pkgInfo);
+ ReflectionHelpers.setField(mFragment, "mPackageName", mockPkgName);
+
+ // Set the initial state to be enabled
+ injectPermissionState(true);
+
+ // Simulate a preference change
+ mFragment.onPreferenceChange(mSwitchPref, /* newValue */ false);
+
+ // Verify that mAppOpsManager was called to deny the app-op
+ verify(mAppOpsManager, times(1))
+ .setMode(anyInt(), anyInt(), nullable(String.class), anyInt());
+ assertThat(mPkgToOpModeMap).containsExactly(mockPkgName, AppOpsManager.MODE_ERRORED);
+ assertThat(mUidToOpModeMap).containsExactly(mockUid, AppOpsManager.MODE_ERRORED);
+
+ // Verify the mSwitchPref was enabled
+ ArgumentCaptor<Boolean> acSetEnabled = ArgumentCaptor.forClass(Boolean.class);
+ verify(mSwitchPref, times(1)).setEnabled(acSetEnabled.capture());
+ assertThat(acSetEnabled.getAllValues()).containsExactly(true);
+
+ // Verify that mSwitchPref was toggled to off
+ ArgumentCaptor<Boolean> acSetChecked = ArgumentCaptor.forClass(Boolean.class);
+ verify(mSwitchPref, times(1)).setChecked(acSetChecked.capture());
+ assertThat(acSetChecked.getAllValues()).containsExactly(false);
+ }
+
+ private void injectPermissionState(boolean enabled) {
+ PermissionState state = new PermissionState(null, null);
+ state.permissionDeclared = true;
+ state.appOpMode = enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_ERRORED;
+ ReflectionHelpers.setField(mFragment, "mPermissionState", state);
+ }
+
+ private void mockAppOpsOperations() {
+ Answer<Void> answerSetMode = invocation -> {
+ int code = invocation.getArgument(0);
+ int uid = invocation.getArgument(1);
+ String packageName = invocation.getArgument(2);
+ int mode = invocation.getArgument(3);
+
+ if (code != AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE) {
+ return null;
+ }
+
+ mPkgToOpModeMap.put(packageName, mode);
+ mUidToOpModeMap.put(uid, mode);
+
+ return null;
+ };
+
+ doAnswer(answerSetMode).when(mAppOpsManager)
+ .setMode(anyInt(), anyInt(), nullable(String.class), anyInt());
+
+ Answer<PermissionState> answerPermState = invocation -> {
+ String packageName = invocation.getArgument(0);
+ PermissionState res = new PermissionState(packageName, null);
+ res.permissionDeclared = false;
+
+ if (mPkgToOpModeMap.containsKey(packageName)) {
+ res.permissionDeclared = true;
+ res.appOpMode = mPkgToOpModeMap.get(packageName);
+ }
+ return res;
+ };
+
+ doAnswer(answerPermState).when(mBridge)
+ .getManageExternalStoragePermState(nullable(String.class), anyInt());
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/network/telephony/DisabledSubscriptionControllerTest.java b/tests/robotests/src/com/android/settings/network/telephony/DisabledSubscriptionControllerTest.java
index f25ffa4..38223b8 100644
--- a/tests/robotests/src/com/android/settings/network/telephony/DisabledSubscriptionControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/telephony/DisabledSubscriptionControllerTest.java
@@ -69,32 +69,32 @@
@Test
public void displayPreference_subscriptionEnabled_categoryIsVisible() {
- doReturn(true).when(mSubscriptionManager).isActiveSubId(SUB_ID);
+ doReturn(true).when(mSubscriptionManager).isActiveSubscriptionId(SUB_ID);
mController.displayPreference(mScreen);
assertThat(mCategory.isVisible()).isTrue();
}
@Test
public void displayPreference_subscriptionDisabled_categoryIsNotVisible() {
- doReturn(false).when(mSubscriptionManager).isActiveSubId(SUB_ID);
+ doReturn(false).when(mSubscriptionManager).isActiveSubscriptionId(SUB_ID);
mController.displayPreference(mScreen);
assertThat(mCategory.isVisible()).isFalse();
}
@Test
public void onSubscriptionsChanged_subscriptionBecomesDisabled_categoryIsNotVisible() {
- doReturn(true).when(mSubscriptionManager).isActiveSubId(SUB_ID);
+ doReturn(true).when(mSubscriptionManager).isActiveSubscriptionId(SUB_ID);
mController.displayPreference(mScreen);
- doReturn(false).when(mSubscriptionManager).isActiveSubId(SUB_ID);
+ doReturn(false).when(mSubscriptionManager).isActiveSubscriptionId(SUB_ID);
mController.onSubscriptionsChanged();
assertThat(mCategory.isVisible()).isFalse();
}
@Test
public void onSubscriptionsChanged_subscriptionBecomesEnabled_categoryIsVisible() {
- doReturn(false).when(mSubscriptionManager).isActiveSubId(SUB_ID);
+ doReturn(false).when(mSubscriptionManager).isActiveSubscriptionId(SUB_ID);
mController.displayPreference(mScreen);
- doReturn(true).when(mSubscriptionManager).isActiveSubId(SUB_ID);
+ doReturn(true).when(mSubscriptionManager).isActiveSubscriptionId(SUB_ID);
mController.onSubscriptionsChanged();
assertThat(mCategory.isVisible()).isTrue();
}
diff --git a/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkSwitchControllerTest.java b/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkSwitchControllerTest.java
index 04e3df8..ba839c6 100644
--- a/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkSwitchControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkSwitchControllerTest.java
@@ -113,7 +113,7 @@
@Test
public void displayPreference_oneEnabledSubscription_switchBarNotHidden() {
- doReturn(true).when(mSubscriptionManager).isActiveSubId(mSubId);
+ doReturn(true).when(mSubscriptionManager).isActiveSubscriptionId(mSubId);
SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(mSubscription));
mController.displayPreference(mScreen);
assertThat(mSwitchBar.isShowing()).isTrue();
@@ -121,7 +121,7 @@
@Test
public void displayPreference_oneDisabledSubscription_switchBarNotHidden() {
- doReturn(false).when(mSubscriptionManager).isActiveSubId(mSubId);
+ doReturn(false).when(mSubscriptionManager).isActiveSubscriptionId(mSubId);
SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(mSubscription));
mController.displayPreference(mScreen);
assertThat(mSwitchBar.isShowing()).isTrue();
@@ -129,7 +129,7 @@
@Test
public void displayPreference_subscriptionEnabled_switchIsOn() {
- when(mSubscriptionManager.isActiveSubId(mSubId)).thenReturn(true);
+ when(mSubscriptionManager.isActiveSubscriptionId(mSubId)).thenReturn(true);
mController.displayPreference(mScreen);
assertThat(mSwitchBar.isShowing()).isTrue();
assertThat(mSwitchBar.isChecked()).isTrue();
@@ -137,7 +137,7 @@
@Test
public void displayPreference_subscriptionDisabled_switchIsOff() {
- when(mSubscriptionManager.isActiveSubId(mSubId)).thenReturn(false);
+ when(mSubscriptionManager.isActiveSubscriptionId(mSubId)).thenReturn(false);
mController.displayPreference(mScreen);
assertThat(mSwitchBar.isShowing()).isTrue();
assertThat(mSwitchBar.isChecked()).isFalse();
@@ -145,7 +145,7 @@
@Test
public void switchChangeListener_fromEnabledToDisabled_setSubscriptionEnabledCalledCorrectly() {
- when(mSubscriptionManager.isActiveSubId(mSubId)).thenReturn(true);
+ when(mSubscriptionManager.isActiveSubscriptionId(mSubId)).thenReturn(true);
mController.displayPreference(mScreen);
assertThat(mSwitchBar.isShowing()).isTrue();
assertThat(mSwitchBar.isChecked()).isTrue();
@@ -157,7 +157,7 @@
public void switchChangeListener_fromEnabledToDisabled_setSubscriptionEnabledFailed() {
when(mSubscriptionManager.setSubscriptionEnabled(eq(mSubId), anyBoolean()))
.thenReturn(false);
- when(mSubscriptionManager.isActiveSubId(mSubId)).thenReturn(true);
+ when(mSubscriptionManager.isActiveSubscriptionId(mSubId)).thenReturn(true);
mController.displayPreference(mScreen);
assertThat(mSwitchBar.isShowing()).isTrue();
assertThat(mSwitchBar.isChecked()).isTrue();
@@ -168,7 +168,7 @@
@Test
public void switchChangeListener_fromDisabledToEnabled_setSubscriptionEnabledCalledCorrectly() {
- when(mSubscriptionManager.isActiveSubId(mSubId)).thenReturn(false);
+ when(mSubscriptionManager.isActiveSubscriptionId(mSubId)).thenReturn(false);
mController.displayPreference(mScreen);
assertThat(mSwitchBar.isShowing()).isTrue();
assertThat(mSwitchBar.isChecked()).isFalse();
diff --git a/tests/robotests/src/com/android/settings/sim/SimSelectNotificationTest.java b/tests/robotests/src/com/android/settings/sim/SimSelectNotificationTest.java
index 9b53636..8f861eb 100644
--- a/tests/robotests/src/com/android/settings/sim/SimSelectNotificationTest.java
+++ b/tests/robotests/src/com/android/settings/sim/SimSelectNotificationTest.java
@@ -117,7 +117,7 @@
when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
when(mTelephonyManager.isDataEnabledForApn(TYPE_MMS)).thenReturn(false);
- when(mSubscriptionManager.isActiveSubId(mSubId)).thenReturn(true);
+ when(mSubscriptionManager.isActiveSubscriptionId(mSubId)).thenReturn(true);
when(mSubscriptionManager.getActiveSubscriptionInfo(mSubId)).thenReturn(mSubInfo);
when(mSubInfo.getDisplayName()).thenReturn(mFakeDisplayName);
when(mContext.getResources()).thenReturn(mResources);