Merge "Unify media permissions enforcement in framework."
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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,24 +595,14 @@ 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.Audio.Media.EXTERNAL_CONTENT_URI, MEDIA_COLUMNS,
|
||||
constructBooleanTrueWhereClause(mFilterColumns), null,
|
||||
MediaStore.Audio.Media.DEFAULT_SORT_ORDER, context)
|
||||
: null;
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
private void setFilterColumnsList(int type) {
|
||||
List<String> columns = mFilterColumns;
|
||||
columns.clear();
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user