默认情况下,如果应用以 Android Q 为目标平台,则在访问外部存储设备中的文件时会进入过滤视图。应用可以使用 Context.getExternalFilesDir() 将专用于自己的文件存储在特定于自己的目录中。

1. 临时停用分区存储行为:

以 Android 9(API 级别 28)或更低版本为目标平台。

如果您以 Android Q 为目标平台,请在应用的清单文件中将 requestLegacyExternalStorage 的值设为 true。

...

2. 如何实现隔离存储:

2.1 ApplicationInfo新增PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE标记

PackageParser.java:

if (sa.getBoolean(

R.styleable.AndroidManifestApplication_requestLegacyExternalStorage,

owner.applicationInfo.targetSdkVersion < Build.VERSION_CODES.Q)) {

ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE;

}

ApplicationInfo.java:

public boolean hasRequestedLegacyExternalStorage() {

return (privateFlags & PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE) != 0;

}

2.2 grantRuntimePermission()重新挂载视图

apk启动时默认挂载runtime/default视图,grantRuntimePermission()时如果是READ_EXTERNAL_STORAGE或WRITE_EXTERNAL_STORAGE,则会获取挂载模式重新挂载对应视图。

PermissionManagerService.java:

private void grantRuntimePermission(String permName, String packageName, boolean overridePolicy,

int callingUid, final int userId, PermissionCallback callback) {

......

if (READ_EXTERNAL_STORAGE.equals(permName)

|| WRITE_EXTERNAL_STORAGE.equals(permName)) {

final long token = Binder.clearCallingIdentity();

try {

if (mUserManagerInt.isUserInitialized(userId)) {

StorageManagerInternal storageManagerInternal = LocalServices.getService(

StorageManagerInternal.class);

storageManagerInternal.onExternalStoragePolicyChanged(uid, packageName);

}

} finally {

Binder.restoreCallingIdentity(token);

}

}

}

获取挂载模式这块android10有修改,没有设置Legacy标志的话,总是获取default挂载模式,没有读写权限。

android 10会设置属性[persist.sys.isolated_storage]: [true],因此走到if(ENABLE_ISOLATED_STORAGE)中的getMountMode()。

public static boolean hasIsolatedStorage() {

//[persist.sys.isolated_storage]: [true]

//[sys.isolated_storage_snapshot]: [true]

return SystemProperties.getBoolean("sys.isolated_storage_snapshot",

SystemProperties.getBoolean("persist.sys.isolated_storage", true));

}

private static final boolean ENABLE_ISOLATED_STORAGE = StorageManager.hasIsolatedStorage();

public int getExternalStorageMountMode(int uid, String packageName) {

+ //android 10新增逻辑

+ if (ENABLE_ISOLATED_STORAGE) {

+ return getMountMode(uid, packageName);

+ }

......

int mountMode = Integer.MAX_VALUE;

for (ExternalStorageMountPolicy policy : mPolicies) {

final int policyMode = policy.getMountMode(uid, packageName);

if (policyMode == Zygote.MOUNT_EXTERNAL_NONE) {

return Zygote.MOUNT_EXTERNAL_NONE;

}

mountMode = Math.min(mountMode, policyMode);

}

if (mountMode == Integer.MAX_VALUE) {

return Zygote.MOUNT_EXTERNAL_NONE;

}

return mountMode;

}

正常模式下hasLegacy=false,走到if判断的DEFAULT分支;legacy模式hasLegacy=true,与之前保持一致,有write权限就走到WRITE模式分支。

private int getMountModeInternal(int uid, String packageName) {

try {

......

final boolean hasRead = StorageManager.checkPermissionAndCheckOp(mContext, false, 0,

uid, packageName, READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE);

final boolean hasWrite = StorageManager.checkPermissionAndCheckOp(mContext, false, 0,

uid, packageName, WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE);

......

final boolean hasLegacy = mIAppOpsService.checkOperation(OP_LEGACY_STORAGE,

uid, packageName) == MODE_ALLOWED;

if (hasLegacy && hasWrite) {

return Zygote.MOUNT_EXTERNAL_WRITE;

} else if (hasLegacy && hasRead) {

return Zygote.MOUNT_EXTERNAL_READ;

} else {

return Zygote.MOUNT_EXTERNAL_DEFAULT;

}

} catch (RemoteException e) {

// Should not happen

}

return Zygote.MOUNT_EXTERNAL_NONE;

}

2.3 Legacy Storage属性对权限的影响

安装apk时,就会根据requestLegacyExternalStorage属性来对ops state进行设置,修改OP_LEGACY_STORAGE的默认状态。

//Q 正常模式

LEGACY_STORAGE: mode=ignore

//Q legacy模式

LEGACY_STORAGE: mode=allow

PermissionPolicyService启动时首先进行权限变化监听:

public void onStart() {

permManagerInternal.addOnRuntimePermissionStateChangedListener(

this::synchronizePackagePermissionsAndAppOpsAsyncForUser);

}

private void synchronizePackagePermissionsAndAppOpsAsyncForUser(@NonNull String packageName,

@UserIdInt int changedUserId) {

if (isStarted(changedUserId)) {

synchronized (mLock) {

if (mIsPackageSyncsScheduled.add(new Pair<>(packageName, changedUserId))) {

FgThread.getHandler().sendMessage(PooledLambda.obtainMessage(

PermissionPolicyService

::synchronizePackagePermissionsAndAppOpsForUser,

this, packageName, changedUserId));

}

......

}

}

}

APK安装时,会根据requestLegacyExternalStorage属性来通知storage权限变化,调用关系如下:

//调用关系:

1.PackageManagerService.java:

installPackagesLI()

commitPackagesLocked()

updateSettingsLI()

updateSettingsInternalLI()

2.PermissionManagerService.java:

mPermissionManager.updatePermissions()

restorePermissionState()

//关键代码:

private void restorePermissionState(@NonNull PackageParser.Package pkg, boolean replace,

@Nullable String packageOfInterest, @Nullable PermissionCallback callback) {

......

//判断requestLegacyExternalStorage属性

updatedUserIds = checkIfLegacyStorageOpsNeedToBeUpdated(pkg, replace, updatedUserIds);

......

for (int userId : updatedUserIds) {

notifyRuntimePermissionStateChanged(pkg.packageName, userId);

}

}

最终调用到PermissionPolicyService的监听函数synchronizePackagePermissionsAndAppOpsForUser(),进行默认权限获取和设置。

当apk安装时,声明了requestLegacyExternalStorage="true"属性,并且声明了READ_EXTERNAL_STORAGE、WRITE_EXTERNAL_STORAGE,那么addOpIfRestricted()就会将LEGACY_STORAGE设置为allow模式。

//调用关系:

synchronizePackagePermissionsAndAppOpsForUser():

synchroniser.addPackage()

addOpIfRestricted()//LEGACY_STORAGE加入到mOpsToAllow

synchroniser.syncPackages()

setUidModeAllowed()

setUidMode()//设置LEGACY_STORAGE为allow

//关键代码:

private void addOpIfRestricted(@NonNull PermissionInfo permissionInfo,

@NonNull PackageInfo pkg) {

......

//forPermission()会根据requestLegacyExternalStorage的值进行返回

final SoftRestrictedPermissionPolicy policy =

SoftRestrictedPermissionPolicy.forPermission(mContext, pkg.applicationInfo,

mContext.getUser(), permission);

final int op = policy.resolveAppOp();

if (op != OP_NONE) {

switch (policy.getDesiredOpMode()) {

case MODE_DEFAULT:

mOpsToDefault.add(new OpToChange(uid, pkg.packageName, op));

break;

case MODE_ALLOWED:

//在声明READ_EXTERNAL_STORAGE权限下,会将LEGACY_STORAGE加入到mOpsToAllow

if (policy.shouldSetAppOpIfNotDefault()) {

mOpsToAllow.add(new OpToChange(uid, pkg.packageName, op));

} else {

mOpsToAllowIfDefault.add(

new OpToChange(uid, pkg.packageName, op));

}

break;

......

}

public static @NonNull SoftRestrictedPermissionPolicy forPermission(@NonNull Context context,

@Nullable ApplicationInfo appInfo, @Nullable UserHandle user,

@NonNull String permission) {

switch (permission) {

case READ_EXTERNAL_STORAGE: {

if (appInfo != null) {

boolean hasAnyRequestedLegacyExternalStorage =

appInfo.hasRequestedLegacyExternalStorage();

hasRequestedLegacyExternalStorage = hasAnyRequestedLegacyExternalStorage;

}

return new SoftRestrictedPermissionPolicy() {

@Override

public int getDesiredOpMode() {

if (applyRestriction) {

return MODE_DEFAULT;

} else if (hasRequestedLegacyExternalStorage) {

//声明了requestLegacyExternalStorage就返回allow

return MODE_ALLOWED;

} else {

return MODE_IGNORED;

}

}

@Override

public boolean shouldSetAppOpIfNotDefault() {

return getDesiredOpMode() != MODE_IGNORED;

}

};

}

3. sdcard路径权限说明:

rwx:421,umask默认为八进制022(----w--w-)

/mnt/runtime/default的gid为1015,也就是sdcard_rw;mask 为6,八进制006,group sdcard_rw可读写,也就是other没有rw权限

/mnt/runtime/read的gid为9997,也就是everybody;mask 为23,八进制027,group everybody可读、不可写,other没有读写执行权限

/mnt/runtime/write的gid为9997,也就是everybody;mask 为7,八进制007,group everybody可读写,other没有读写可执行权限

/data/media on /mnt/runtime/default/emulated type sdcardfs (rw,nosuid,nodev,noexec,noatime,fsuid=1023,fsgid=1023,gid=1015,multiuser,mask=6,derive_gid,default_normal)

/data/media on /mnt/runtime/read/emulated type sdcardfs (rw,nosuid,nodev,noexec,noatime,fsuid=1023,fsgid=1023,gid=9997,multiuser,mask=23,derive_gid,default_normal)

/data/media on /mnt/runtime/write/emulated type sdcardfs (rw,nosuid,nodev,noexec,noatime,fsuid=1023,fsgid=1023,gid=9997,multiuser,mask=7,derive_gid,default_normal)

/mnt/runtime/default:

drwxrwx--x 3 root sdcard_rw 4096 2018-12-18 03:41 Android

drwxrwx--x 3 root sdcard_rw 4096 2018-12-18 06:11 DCIM

/mnt/runtime/read:

drwxr-x--- 3 root everybody 4096 2018-12-18 03:41 Android

drwxr-x--- 3 root everybody 4096 2018-12-18 06:11 DCIM

/mnt/runtime/write:

drwxrwx--- 3 root everybody 4096 2018-12-18 03:41 Android

drwxrwx--- 3 root everybody 4096 2018-12-18 06:11 DCIM

/sdcard/Android/data:

drwxrwx--- 4 u0_a64 everybody 4096 2018-12-18 06:11 com.android.camera2

drwxrwx--- 3 u0_a15 everybody 4096 2018-12-18 03:41 com.google.android.gms

drwxrwx--- 4 u0_a84 everybody 4096 2018-12-18 03:41 com.google.android.youtube

4. sdcard文件存储示例:

4.1 getExternalFilesDir()随卸载而删除

///storage/emulated/0/Android/data/com.xx.xx/files

File file = File(context.getExternalFilesDir(null), "test.txt");

4.2 媒体文件

媒体文件使用MediaStore操作,卸载后不会删除。

访问其他应用生成的照片、视频、音频,需要READ_EXTERNAL_STORAGE权限。

4.3 存储访问框架(SAF)

访问其他应用创建的文件,例如"Download"目录,必须使用存储访问框架,用户通过框架选择特定文件。

4.4 照片中的位置信息

需要ACCESS_MEDIA_LOCATION权限,才能获取元数据中的位置信息。

android:permissionGroup="android.permission-group.UNDEFINED"

android:label="@string/permlab_mediaLocation"

android:description="@string/permdesc_mediaLocation"

android:protectionLevel="dangerous" />

到此这篇关于androidQ sd卡权限使用详解的文章就介绍到这了,更多相关androidQ sd卡权限内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2020-06-22

android sdcardfs 权限,androidQ sd卡权限使用详解相关推荐

  1. android 请求sd卡权限,androidQ sd卡权限使用详解

    默认情况下,如果应用以 Android Q 为目标平台,则在访问外部存储设备中的文件时会进入过滤视图.应用可以使用 Context.getExternalFilesDir() 将专用于自己的文件存储在 ...

  2. 【SD卡】关于DJYOS下SD卡驱动开发详解

    关于DJYOS下SD卡驱动开发详解 王建忠 2011/6/21 1      开发环境及说明 硬件平台:tq2440(CPU: s3c2440) 操作系统:DJYOS1.0.0 1.1    说明 T ...

  3. android 6.0 sd卡读写权限,Android 6.0 读写SD卡权限问题

    一.问题描述## Android 6.0 下默认存储SD卡,使用原生FMRadio生成的文件保存到内部存储中 [预置条件]插入T卡[操作步骤]设置>存储设备和USB>选择SD卡为默认存储& ...

  4. android 5.1.1 sd卡权限,android - 适用于Android 5.1.1及更高版本的Cordova的外部存储路径(SD卡) - 堆栈内存溢出...

    从Android 5.0开始,外部(可移动)SD的位置不再是固定路径. 而是在路径中使用SD卡的序列号. 例如,在运行Android 7.1.1的Samsung Galaxy S4上,物理外部可移动S ...

  5. STM32利用SPI读写SD卡的程序详解

    STM32利用SPI读写SD卡的一些程序详解 关于SD卡的基础知识这里不做过多陈述,如果有对这方面感兴趣的朋友可以直接百度一下,有很多讲SD卡的文章,这里主要是针对SD卡的读写程序实现做一些详细说明. ...

  6. 【科普贴】SD卡接口协议详解

    一. SD卡简介 SD卡是Secure Digital Card,直译就是"安全数字卡",最早由松下(Panasonic)闪迪(SanDisk)东芝(Toshiba)三家公司发起. ...

  7. linux sd卡驱动视频,详解linux 驱动编写(sd卡驱动)

    随着sd卡的流行,sd卡在嵌入式设备上使用的场景也越来越多.那下面我们可以看一下,linux驱动框架上是怎么处理sd卡驱动的? 1.代码目录地址 drivers/mmc 2.基本结构 从mmc的代码结 ...

  8. android sd卡名称,科普详解Android系统SD卡各类文件夹名称

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 15.moji:墨迹天气的缓存目录. 16.MusicFolders:poweramp产生的缓存文件夹. 17.openfeint:openfeint的缓 ...

  9. android.process.media+sd,android P系统WRITE_MEDIA_STORAGE添加读写SD卡权限

    由于android P 版本google把android.Permission.WRITE_MEDIA_STORAGE中sd卡的读写权限移除了,导致系统文件管理中的文件无法直接复制到SD卡,所以需要把 ...

最新文章

  1. 工程师必读 微软如何部署Exchange2010
  2. 【人脸识别】初识人脸识别
  3. Linux 下搭建 Java Web 开发环境
  4. QML基础类型之color
  5. android 仿ios timepicker,android:TimePicker仿照IOS時間選擇器,可自定義選擇器
  6. golang切片内存应用技巧
  7. nginx windows启动停止_Nginx之3抛砖引玉 - (目录索引)
  8. Mybatis中DAO层接口没有写实现类,Mapper中的方法和DAO接口方法是怎么绑定到一起的...
  9. 【渝粤教育】国家开放大学2018年春季 8618-22T燃气行业规范 参考试题
  10. 深度学习基础之三分钟轻松搞明白tensor到底是个啥!看不懂的话我倒立洗头~~
  11. 基于华为云服务的人脸识别功能实现
  12. 大学计算机习题汇总及答案
  13. 计算机设备型号和序列号,怎么查看计算机型号_怎么查看计算机序列号
  14. tl494cn逆变器电路图_TL494制作的400W大功率稳压逆变器电路图
  15. 如何快速把多张图片调成统一尺寸?
  16. 揭秘!2周实现上云上市,阿里云SaaS上云工具包如何打造新云梯?
  17. metricbeat mysql_Metricbeat 参考指南(目录)
  18. 深度学习(三)denoise autoencoder的Python实现
  19. 计算机技术与维修结课论文,计算机维护技术结课论文.doc
  20. C语言关于字符串和字符数组的题目(差别)

热门文章

  1. fink sql 读取 kafka 的数据写到 kafka
  2. 八爪鱼爬取搜索引擎链接列表后如何循环点击每个列表里的特定元素呢?
  3. MBD 技术的发展历程
  4. 简单求一个数字尾部零的个数
  5. html cookie设置不过期,HTML5学习之关于Cookie的expires过期时间无效分析
  6. 求数组中逆序对的个数
  7. 要不要启用苹果wapi_M1版MacBook要不要买?李楠称苹果后续有大招
  8. 魔众题库系统 v6.4.0 题目导入优化 自动阅卷优化 后台菜单快捷搜索
  9. Python爬虫基本流程(自用)
  10. 精通微服务开发?来聊聊这些问题