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

Commit 02aefee5 authored by Jeff Sharkey's avatar Jeff Sharkey Committed by Android (Google) Code Review
Browse files

Merge "Unify media permissions enforcement in framework."

parents 362f85e2 859856d8
Loading
Loading
Loading
Loading
+117 −0
Original line number Diff line number Diff line
@@ -16,7 +16,21 @@

package android.os.storage;

import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.READ_MEDIA_AUDIO;
import static android.Manifest.permission.READ_MEDIA_IMAGES;
import static android.Manifest.permission.READ_MEDIA_VIDEO;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.OP_READ_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.OP_READ_MEDIA_AUDIO;
import static android.app.AppOpsManager.OP_READ_MEDIA_IMAGES;
import static android.app.AppOpsManager.OP_READ_MEDIA_VIDEO;
import static android.app.AppOpsManager.OP_WRITE_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.OP_WRITE_MEDIA_AUDIO;
import static android.app.AppOpsManager.OP_WRITE_MEDIA_IMAGES;
import static android.app.AppOpsManager.OP_WRITE_MEDIA_VIDEO;
import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;

import android.annotation.BytesLong;
import android.annotation.IntDef;
@@ -33,6 +47,7 @@ import android.annotation.WorkerThread;
import android.app.Activity;
import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -1635,6 +1650,108 @@ public class StorageManager {
        }
    }

    /**
     * Check that given app holds both permission and appop.
     *
     * @return {@code null} if the permission and appop are held, otherwise
     *         returns a string indicating why access was denied.
     */
    private boolean checkPermissionAndAppOp(boolean enforce, int pid, int uid, String packageName,
            String permission, int op) {
        if (mContext.checkPermission(permission, pid, uid) != PERMISSION_GRANTED) {
            if (enforce) {
                throw new SecurityException(
                        "Permission " + permission + " denied for package " + packageName);
            } else {
                return false;
            }
        }

        final AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
        final int mode = appOps.noteOpNoThrow(op, uid, packageName);
        switch (mode) {
            case AppOpsManager.MODE_ALLOWED:
                return true;
            case AppOpsManager.MODE_DEFAULT:
            case AppOpsManager.MODE_IGNORED:
            case AppOpsManager.MODE_ERRORED:
                if (enforce) {
                    throw new SecurityException("Op " + AppOpsManager.opToName(op) + " "
                            + AppOpsManager.modeToName(mode) + " for package " + packageName);
                } else {
                    return false;
                }
            default:
                throw new IllegalStateException(
                        AppOpsManager.opToName(op) + " has unknown mode "
                                + AppOpsManager.modeToName(mode));
        }
    }

    // Callers must hold both the old and new permissions, so that we can
    // handle obscure cases like when an app targets Q but was installed on
    // a device that was originally running on P before being upgraded to Q.

    /** {@hide} */
    public boolean checkPermissionReadAudio(boolean enforce,
            int pid, int uid, String packageName) {
        if (!checkPermissionAndAppOp(enforce, pid, uid, packageName,
                READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE)) return false;
        if (!checkPermissionAndAppOp(enforce, pid, uid, packageName,
                READ_MEDIA_AUDIO, OP_READ_MEDIA_AUDIO)) return false;
        return true;
    }

    /** {@hide} */
    public boolean checkPermissionWriteAudio(boolean enforce,
            int pid, int uid, String packageName) {
        if (!checkPermissionAndAppOp(enforce, pid, uid, packageName,
                WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE)) return false;
        if (!checkPermissionAndAppOp(enforce, pid, uid, packageName,
                READ_MEDIA_AUDIO, OP_WRITE_MEDIA_AUDIO)) return false;
        return true;
    }

    /** {@hide} */
    public boolean checkPermissionReadVideo(boolean enforce,
            int pid, int uid, String packageName) {
        if (!checkPermissionAndAppOp(enforce, pid, uid, packageName,
                READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE)) return false;
        if (!checkPermissionAndAppOp(enforce, pid, uid, packageName,
                READ_MEDIA_VIDEO, OP_READ_MEDIA_VIDEO)) return false;
        return true;
    }

    /** {@hide} */
    public boolean checkPermissionWriteVideo(boolean enforce,
            int pid, int uid, String packageName) {
        if (!checkPermissionAndAppOp(enforce, pid, uid, packageName,
                WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE)) return false;
        if (!checkPermissionAndAppOp(enforce, pid, uid, packageName,
                READ_MEDIA_VIDEO, OP_WRITE_MEDIA_VIDEO)) return false;
        return true;
    }

    /** {@hide} */
    public boolean checkPermissionReadImages(boolean enforce,
            int pid, int uid, String packageName) {
        if (!checkPermissionAndAppOp(enforce, pid, uid, packageName,
                READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE)) return false;
        if (!checkPermissionAndAppOp(enforce, pid, uid, packageName,
                READ_MEDIA_IMAGES, OP_READ_MEDIA_IMAGES)) return false;
        return true;
    }

    /** {@hide} */
    public boolean checkPermissionWriteImages(boolean enforce,
            int pid, int uid, String packageName) {
        if (!checkPermissionAndAppOp(enforce, pid, uid, packageName,
                WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE)) return false;
        if (!checkPermissionAndAppOp(enforce, pid, uid, packageName,
                READ_MEDIA_IMAGES, OP_WRITE_MEDIA_IMAGES)) return false;
        return true;
    }

    /** {@hide} */
    @VisibleForTesting
    public @NonNull ParcelFileDescriptor openProxyFileDescriptor(
+7 −19
Original line number Diff line number Diff line
@@ -29,7 +29,6 @@ import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.content.res.AssetFileDescriptor;
@@ -40,7 +39,6 @@ import android.os.Environment;
import android.os.FileUtils;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -597,22 +595,12 @@ public class RingtoneManager {

    @UnsupportedAppUsage
    private Cursor getMediaRingtones(Context context) {
        if (PackageManager.PERMISSION_GRANTED != context.checkPermission(
                android.Manifest.permission.READ_EXTERNAL_STORAGE,
                Process.myPid(), Process.myUid())) {
            Log.w(TAG, "No READ_EXTERNAL_STORAGE permission, ignoring ringtones on ext storage");
            return null;
        }
         // Get the external media cursor. First check to see if it is mounted.
        final String status = Environment.getExternalStorageState();
        
        return (status.equals(Environment.MEDIA_MOUNTED) ||
                    status.equals(Environment.MEDIA_MOUNTED_READ_ONLY))
                ? query(
        // MediaStore now returns ringtones on other storage devices, even when
        // we don't have storage or audio permissions
        return query(
                MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, MEDIA_COLUMNS,
                constructBooleanTrueWhereClause(mFilterColumns), null,
                    MediaStore.Audio.Media.DEFAULT_SORT_ORDER, context)
                : null;
                MediaStore.Audio.Media.DEFAULT_SORT_ORDER, context);
    }

    private void setFilterColumnsList(int type) {
+4 −2
Original line number Diff line number Diff line
@@ -33,8 +33,10 @@
        android:protectionLevel="signature" />

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <!-- Used to read wallpaper -->
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />

    <!-- Used to read storage for all users -->
    <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
+3 −16
Original line number Diff line number Diff line
@@ -77,6 +77,7 @@ import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.service.wallpaper.IWallpaperConnection;
import android.service.wallpaper.IWallpaperEngine;
import android.service.wallpaper.IWallpaperService;
@@ -2089,28 +2090,14 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
        }
    }

    private void enforceCallingOrSelfPermissionAndAppOp(String permission, final String callingPkg,
            final int callingUid, String message) {
        mContext.enforceCallingOrSelfPermission(permission, message);

        final String opName = AppOpsManager.permissionToOp(permission);
        if (opName != null) {
            final int appOpMode = mAppOpsManager.noteOp(opName, callingUid, callingPkg);
            if (appOpMode != AppOpsManager.MODE_ALLOWED) {
                throw new SecurityException(
                        message + ": " + callingPkg + " is not allowed to " + permission);
            }
        }
    }

    @Override
    public ParcelFileDescriptor getWallpaper(String callingPkg, IWallpaperManagerCallback cb,
            final int which, Bundle outParams, int wallpaperUserId) {
        final int hasPrivilege = mContext.checkCallingOrSelfPermission(
                android.Manifest.permission.READ_WALLPAPER_INTERNAL);
        if (hasPrivilege != PackageManager.PERMISSION_GRANTED) {
            enforceCallingOrSelfPermissionAndAppOp(android.Manifest.permission.READ_EXTERNAL_STORAGE,
                    callingPkg, Binder.getCallingUid(), "read wallpaper");
            mContext.getSystemService(StorageManager.class).checkPermissionReadImages(true,
                    Binder.getCallingPid(), Binder.getCallingUid(), callingPkg);
        }

        wallpaperUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),