Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 850cd699 authored by Nate Myren's avatar Nate Myren Committed by Android (Google) Code Review
Browse files

Merge "Add storage allowed categories"

parents 5d0e2b41 940ea37f
Loading
Loading
Loading
Loading
+13 −1
Original line number Diff line number Diff line
@@ -358,6 +358,12 @@
    <!-- Header for permissions/apps that are granted only when in the foreground [CHAR LIMIT=40] -->
    <string name="allowed_foreground_header">Allowed only while in use</string>

    <!-- Subtitle for scoped storage permissions [CHAR LIMIT=40] -->
    <string name="allowed_storage_scoped">Allowed for media only</string>

    <!-- Subtitle for full storage permissions [CHAR LIMIT=40] -->
    <string name="allowed_storage_full">Allowed for all files</string>

    <!-- Header for permissions/apps which need to ask next time they want to use the permission. Note: Permission may or may not be currently granted [CHAR LIMIT=60] -->
    <string name="ask_header">Ask every time</string>

@@ -409,6 +415,12 @@
    <!-- Label when no apps have been granted a given permission [CHAR LIMIT=none] -->
    <string name="no_apps_allowed">No apps allowed</string>

    <!-- Label when no apps have been denied a given permission [CHAR LIMIT=none] -->
    <string name="no_apps_allowed_full">No apps allowed for all files</string>

    <!-- Label when no apps have been denied a given permission [CHAR LIMIT=none] -->
    <string name="no_apps_allowed_scoped">No apps allowed for media only</string>

    <!-- Label when no apps have been denied a given permission [CHAR LIMIT=none] -->
    <string name="no_apps_denied">No apps denied</string>

@@ -659,7 +671,7 @@ Allow <xliff:g id="app_name" example="Gmail">%4$s</xliff:g> to upload a bug repo

    <!-- Message shown to the user when the apps requests permission from this group. Shows in the isolated storage case. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
    <string name="permgrouprequest_storage_isolated">Allow
        &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access photos and media on your device?</string>
        &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to read this device\u2019s media files?</string>


    <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
+8 −0
Original line number Diff line number Diff line
@@ -17,6 +17,14 @@
<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android">

    <PreferenceCategory
        android:key="allowed_storage_full"
        android:title="@string/allowed_storage_full" />

    <PreferenceCategory
        android:key="allowed_storage_scoped"
        android:title="@string/allowed_storage_scoped" />

    <PreferenceCategory
        android:key="allowed"
        android:title="@string/allowed_header" />
+76 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.permissioncontroller.permission.data

import android.Manifest.permission_group.STORAGE
import android.app.AppOpsManager
import android.app.AppOpsManager.MODE_IGNORED
import android.app.AppOpsManager.OPSTR_LEGACY_STORAGE
import android.app.Application
import android.os.Build
import android.os.UserHandle
import kotlinx.coroutines.Job

/**
 * A liveData which tracks all packages in the system which have full file permissions, as
 * represented by the OPSTR_LEGACY_STORAGE app op, not just media-only storage permissions.
 *
 * @param app The current application
 */
class FullStoragePermissionAppsLiveData(private val app: Application) :
    SmartAsyncMediatorLiveData<List<Pair<String, UserHandle>>>() {

    private val standardPermGroupsPackagesLiveData =
        PermGroupPackagesUiInfoRepository.getAllStandardPermGroupsPackagesLiveData(app)

    init {
        addSource(standardPermGroupsPackagesLiveData) {
            updateAsync()
        }
    }

    override suspend fun loadDataAndPostValue(job: Job) {
        val storagePackages = standardPermGroupsPackagesLiveData.value?.get(STORAGE) ?: return
        val appOpsManager = app.getSystemService(AppOpsManager::class.java) ?: return

        val legacyPackages = mutableListOf<Pair<String, UserHandle>>()
        for ((user, packageInfoList) in UserPackageInfosRepository.getAllPackageInfosLiveData(app)
            .value ?: emptyMap()) {
            val userPackages = packageInfoList.filter {
                storagePackages.contains(it.packageName to user)
            }

            for (packageInfo in userPackages) {
                val sdk = packageInfo.targetSdkVersion
                if (sdk < Build.VERSION_CODES.P) {
                    legacyPackages.add(packageInfo.packageName to user)
                } else if (sdk < Build.VERSION_CODES.R &&
                    appOpsManager.unsafeCheckOpNoThrow(OPSTR_LEGACY_STORAGE, packageInfo.uid,
                        packageInfo.packageName) != MODE_IGNORED) {
                    legacyPackages.add(packageInfo.packageName to user)
                }
            }
        }

        postValue(legacyPackages)
    }

    override fun onActive() {
        super.onActive()
        updateAsync()
    }
}
 No newline at end of file
+4 −2
Original line number Diff line number Diff line
@@ -469,8 +469,10 @@ public final class AppPermissionGroup implements Comparable<AppPermissionGroup>
            mIconResId = R.drawable.ic_perm_device_info;
        }

        mIsNonIsolatedStorage = mAppOps.unsafeCheckOpNoThrow(OPSTR_LEGACY_STORAGE,
                        packageInfo.applicationInfo.uid, packageInfo.packageName) == MODE_ALLOWED;
        mIsNonIsolatedStorage = targetSDK < Build.VERSION_CODES.P
                || (targetSDK < Build.VERSION_CODES.R
                && mAppOps.unsafeCheckOpNoThrow(OPSTR_LEGACY_STORAGE,
                        packageInfo.applicationInfo.uid, packageInfo.packageName) == MODE_ALLOWED);
    }

    public boolean doesSupportRuntimePermissions() {
+61 −15
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import static com.android.permissioncontroller.permission.ui.Category.ALLOWED_FO
import static com.android.permissioncontroller.permission.ui.Category.ASK;
import static com.android.permissioncontroller.permission.ui.Category.DENIED;

import android.Manifest;
import android.app.ActionBar;
import android.content.Context;
import android.content.Intent;
@@ -73,6 +74,8 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader {
    private static final String KEY_FOOTER = "_footer";
    private static final String KEY_EMPTY = "_empty";
    private static final String LOG_TAG = "PermissionAppsFragment";
    private static final String STORAGE_ALLOWED_FULL = "allowed_storage_full";
    private static final String STORAGE_ALLOWED_SCOPED = "allowed_storage_scoped";
    private static final int SHOW_LOAD_DELAY_MS = 200;

    private static final String SHOW_SYSTEM_KEY = PermissionAppsFragment.class.getName()
@@ -224,10 +227,20 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader {
    }

    private void onPackagesLoaded(Map<Category, List<Pair<String, UserHandle>>> categories) {
        boolean isStorage = mPermGroupName.equals(Manifest.permission_group.STORAGE);
        if (getPreferenceScreen() == null) {
            addPreferencesFromResource(R.xml.allowed_denied);
            // Hide allowed foreground label by default, to avoid briefly showing it before updating
            findPreference(ALLOWED_FOREGROUND.getCategoryName()).setVisible(false);

            // If this is the storage permission, hide the allowed category, and show the storage
            // specific allowed categories
            if (isStorage) {
                findPreference(ALLOWED.getCategoryName()).setVisible(false);
            } else {
                findPreference(STORAGE_ALLOWED_FULL).setVisible(false);
                findPreference(STORAGE_ALLOWED_SCOPED).setVisible(false);
            }
        }
        Context context = getPreferenceManager().getContext();

@@ -237,12 +250,13 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader {

        Map<String, Preference> existingPrefs = new ArrayMap<>();

        for (Category grantCategory : categories.keySet()) {
            PreferenceCategory category = findPreference(grantCategory.getCategoryName());
        for (int i = 0; i < getPreferenceScreen().getPreferenceCount(); i++) {
            PreferenceCategory category = (PreferenceCategory)
                    getPreferenceScreen().getPreference(i);
            category.setOrderingAsAdded(true);
            int numPreferences = category.getPreferenceCount();
            for (int i = 0; i < numPreferences; i++) {
                Preference preference = category.getPreference(i);
            for (int j = 0; j < numPreferences; j++) {
                Preference preference = category.getPreference(j);
                existingPrefs.put(preference.getKey(), preference);
            }
            category.removeAll();
@@ -262,7 +276,10 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader {
            List<Pair<String, UserHandle>> packages = categories.get(grantCategory);
            PreferenceCategory category = findPreference(grantCategory.getCategoryName());

            if (packages.size() == 0) {

            // If this category is empty, and this isn't the "allowed" category of the storage
            // permission, set up the empty preference.
            if (packages.size() == 0 && (!isStorage || !grantCategory.equals(ALLOWED))) {
                Preference empty = new Preference(context);
                empty.setSelectable(false);
                empty.setKey(category.getKey() + KEY_EMPTY);
@@ -289,6 +306,12 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader {

                String key = user + packageName;

                if (isStorage && grantCategory.equals(ALLOWED)) {
                    category = mViewModel.shouldUseFullStorageString(packageName, user)
                            ? findPreference(STORAGE_ALLOWED_FULL)
                            : findPreference(STORAGE_ALLOWED_SCOPED);
                }

                Preference existingPref = existingPrefs.get(key);
                if (existingPref != null) {
                    if (existingPref instanceof SmartIconLoadPackagePermissionPreference) {
@@ -316,15 +339,29 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader {
                }
            }

            KotlinUtils.INSTANCE.sortPreferenceGroup(category, false,
                    (Preference lhs, Preference rhs) -> {
                        int result = mCollator.compare(lhs.getTitle().toString(),
                                rhs.getTitle().toString());
                        if (result == 0) {
                            result = lhs.getKey().compareTo(rhs.getKey());
            if (isStorage && grantCategory.equals(ALLOWED)) {
                PreferenceCategory full = findPreference(STORAGE_ALLOWED_FULL);
                PreferenceCategory scoped = findPreference(STORAGE_ALLOWED_SCOPED);
                if (full.getPreferenceCount() == 0) {
                    Preference empty = new Preference(context);
                    empty.setSelectable(false);
                    empty.setKey(STORAGE_ALLOWED_FULL + KEY_EMPTY);
                    empty.setTitle(getString(R.string.no_apps_allowed_full));
                    full.addPreference(empty);
                }

                if (scoped.getPreferenceCount() == 0) {
                    Preference empty = new Preference(context);
                    empty.setSelectable(false);
                    empty.setKey(STORAGE_ALLOWED_FULL + KEY_EMPTY);
                    empty.setTitle(getString(R.string.no_apps_allowed_scoped));
                    scoped.addPreference(empty);
                }
                KotlinUtils.INSTANCE.sortPreferenceGroup(full, false, this::comparePreference);
                KotlinUtils.INSTANCE.sortPreferenceGroup(scoped, false, this::comparePreference);
            } else {
                KotlinUtils.INSTANCE.sortPreferenceGroup(category, false, this::comparePreference);
            }
                        return result;
                    });

            mViewModel.setCreationLogged(true);
        }
@@ -332,6 +369,15 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader {
        setLoading(false /* loading */, true /* animate */);
    }

    private int comparePreference(Preference lhs, Preference rhs) {
        int result = mCollator.compare(lhs.getTitle().toString(),
                rhs.getTitle().toString());
        if (result == 0) {
            result = lhs.getKey().compareTo(rhs.getKey());
        }
        return result;
    }

    private void logPermissionAppsFragmentCreated(String packageName, UserHandle user, long viewId,
            boolean isAllowed, boolean isAllowedForeground, boolean isDenied) {
        long sessionId = getArguments().getLong(EXTRA_SESSION_ID, 0);
Loading