androidQ sd卡权限使用详解

网友投稿 977 2023-05-22

androidQ sd卡权限使用详解

androidQ sd卡权限使用详解

默认情况下,如果应用以 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 (EYGOzZvmUNABLE_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启动http://时首先进行权限变化监听:

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" />

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

android:label="@string/permlab_mediaLocation"

android:description="@string/permdesc_mediaLocation"

android:protectionLevel="dangerous" />

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:idea复制module(项目)并在一个窗口展示的教程详解
下一篇:AndroidQ沙盒机制之分区存储适配
相关文章

 发表评论

暂时没有评论,来抢沙发吧~