ファイル アクセス許可は、Android システムにおいて非常に複雑かつ重要な内容であり、アプリケーションがアクセスして操作できるファイルとディレクトリを決定します。Android システムには、ファイル アクセス許可に対する厳格な制限と管理があります。Android システムのバージョンは常に更新されているため、ファイル アクセス許可にも変更や変更があり、ニーズに合わせて理解して学習する必要があります。この内容は非常に複雑で複雑です。。この記事では、Android システムの概念、分類、アプリケーション方法、フレームワーク層のソース コードの場所と主要な機能、および Android 11、12、13 でのファイル アクセス許可の変更と適応方法について簡単に説明します。
一連の記事:
Android システムのファイル アクセス許可に関する注意事項
Android システムは /sys/ ディレクトリのアクセス許可と UID と GID を理解しますか?
Android システムのアプリケーションのストレージ パスと権限
Android システムのカスタム システムとアプリケーションの権限
Android システムの AppOps は、デフォルトでアプリケーションに対応する権限を付与
Android システムの権限グループの管理と互換性
参考研究:
Google Android データとファイル ストレージの概要Android 11 system_server Android10 が /sdcard/、/storage/emulated/0/ ファイルを読み取れない問題を解決するための
SDCARD の読み書きAndroid 9.0 の外部 SD カードに関するディスカッション Android 9.0 の外部 SD カードの読み書き権限と SD カードのマウントAndroid 9.0 質問Android システムは /sys/ ディレクトリのアクセス許可と UID と GID を理解しますか? Android 11/12/13 での android/data および obb コンテンツの変更の経験記録Android 13 の機能と変更リスト | Android Developers | Android Developers Android 11 はすべてのファイル権限の管理を許可するように適用されますAndroid11 で開くのに失敗しました: EACCES (権限が拒否されました)ファイル処理時)質問
Android 外部ストレージのアクセス許可の変更と適応
Android 10 バージョン以降、通常のアプリケーションはシステム /sdcard/、/mnt/ 下の SD カード、および /mnt/ 下の USB ディスクを直接読み取ることができなくなりました。これは、Android 10 では、アプリが他のアプリのデータを読み取らないように設計されたパーティション分割されたストレージ メカニズムが導入されているためです。各アプリケーションには独自のストレージ領域が必要であり、許可なしにパブリック ディレクトリにアクセスすることはできません。アプリケーションはデータをリクエストするときに権限チェックを通過する必要があります。
システムアプリケーションと権限
アプリケーションがシステム プラットフォームの署名または設定に変更されandroid:sharedUserId="android.uid.system"
、対応する読み取りおよび書き込み権限が追加された場合、システム /sdcard/、/mnt/ の下の SD カード、および /mnt/ の下の USB ディスクの読み取りおよび書き込みが可能になります。これは、そのようなアプリケーションはシステムと同じ署名を持ち、android:sharedUserId="android.uid.system"
システムの UID を共有してシステムのコア機能やデータにアクセスするために使用できるためです。
変更によりandroid:sharedUserId="android.uid.system"
、アプリケーションの UID をシステムの UID に変更し、一部のディレクトリにアクセスできるようにすることができます。これは、システムの UID が 1000 であり、最も高い権限を持っているためです。
このファイルは、Android ソース コードの system/core/include/private/android_filesystem_config.h パスにあります。
#define AID_ROOT 0 /* traditional unix root user */
/* The following are for LTP and should only be used for testing */
#define AID_DAEMON 1 /* traditional unix daemon owner */
#define AID_BIN 2 /* traditional unix binaries owner */
#define AID_SYSTEM 1000 /* system server */
。。。。。。。。。。
Android のソース コードと権限の検出
Android システムのファイル アクセス許可により、アプリケーションがアクセスして操作できるファイルとディレクトリが決まります。Android システムには、いくつかの主要なクラスやメソッドを含む、ファイル アクセス許可に対する厳格な制限と管理があります。
Android システムのファイル アクセス許可のソース コードの場所と主要な機能は、さまざまな機能とモジュールに従って区別できます。主なものは次のとおりです。
StorageManagerService.java
- xref:
http://aospxref.com/android-13.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/StorageManagerService.java
: このクラスは、外部ストレージ デバイスのマウントとアンマウントの管理、およびストレージ領域の割り当てと再利用を担当します。これはシステム サービスであり、そのインスタンスは Context.getSystemService(Context.STORAGE_SERVICE) を通じて取得できます。次のようないくつかの主要なメソッドが提供されます。
マウント(文字列volId):
// 这个方法是 StorageManagerService 类的一个公开方法,用于挂载指定 ID 的外部存储设备卷
2309 @Override
2310 public void mount(String volId) {
2311 // 检查调用者是否有 MOUNT_UNMOUNT_FILESYSTEMS 权限,如果没有,抛出 SecurityException 异常
2312 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
2313
2314 // 根据卷的 ID 找到对应的 VolumeInfo 对象,如果找不到,抛出 IllegalArgumentException 异常
2315 final VolumeInfo vol = findVolumeByIdOrThrow(volId);
2316 // 判断是否允许挂载该卷,如果不允许,抛出 SecurityException 异常
2317 if (isMountDisallowed(vol)) {
2318 throw new SecurityException("Mounting " + volId + " restricted by policy");
2319 }
2320
2321 // 调用私有的 mount 方法,传入 VolumeInfo 对象,用于执行挂载操作
2322 mount(vol);
2323 }
2324
2325 // 这个方法是 StorageManagerService 类的一个私有方法,用于挂载指定的 VolumeInfo 对象
2326 private void mount(VolumeInfo vol) {
2327 try {
2328 // TODO(b/135341433): Remove cautious logging when FUSE is stable
2329 // 记录日志,表示正在挂载卷
2330 Slog.i(TAG, "Mounting volume " + vol);
2331 // 调用 mVold 的 mount 方法,传入卷的 ID、挂载标志、挂载用户 ID 和一个回调对象
2332 mVold.mount(vol.id, vol.mountFlags, vol.mountUserId, new IVoldMountCallback.Stub() {
2333 // 这个回调对象实现了 IVoldMountCallback 接口,它有一个 onVolumeChecking 方法,用于在挂载前对卷进行检查
2334 @Override
2335 public boolean onVolumeChecking(FileDescriptor fd, String path,
2336 String internalPath) {
2337 // 将卷的路径和内部路径设置为传入的参数值
2338 vol.path = path;
2339 vol.internalPath = internalPath;
2340 // 将文件描述符封装成一个 ParcelFileDescriptor 对象
2341 ParcelFileDescriptor pfd = new ParcelFileDescriptor(fd);
2342 try {
2343 // 调用 mStorageSessionController 的 onVolumeMount 方法,传入该对象和卷的信息
2344 mStorageSessionController.onVolumeMount(pfd, vol);
2345 // 如果 onVolumeMount 方法成功返回,表示挂载成功,返回 true
2346 return true;
2347 } catch (ExternalStorageServiceException e) {
2348 // 如果 onVolumeMount 方法抛出 ExternalStorageServiceException 异常,表示挂载失败,返回 false,并在一定时间后重新尝试挂载
2349 Slog.e(TAG, "Failed to mount volume " + vol, e);
2350
2351 int nextResetSeconds = FAILED_MOUNT_RESET_TIMEOUT_SECONDS;
2352 Slog.i(TAG, "Scheduling reset in " + nextResetSeconds + "s");
2353 mHandler.removeMessages(H_RESET);
2354 mHandler.sendMessageDelayed(mHandler.obtainMessage(H_RESET),
2355 TimeUnit.SECONDS.toMillis(nextResetSeconds));
2356 return false;
2357 } finally {
2358 // 无论成功或失败,都要关闭文件描述符对象
2359 try {
2360 pfd.close();
2361 } catch (Exception e) {
2362 Slog.e(TAG, "Failed to close FUSE device fd", e);
2363 }
2364 }
2365 }
2366 });
2367 // 记录日志,表示已经挂载卷
2368 Slog.i(TAG, "Mounted volume " + vol);
2369 } catch (Exception e) {
2370 // 如果发生其他异常,记录错误日志
2371 Slog.wtf(TAG, e);
2372 }
2373 }
アンマウント(文字列 volId):
// 这个方法是 StorageManagerService 类的一个公开方法,用于卸载指定 ID 的外部存储设备卷
2377 @Override
2378 public void unmount(String volId) {
2379 // 检查调用者是否有 MOUNT_UNMOUNT_FILESYSTEMS 权限,如果没有,抛出 SecurityException 异常
2380 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
2381
2382 // 根据卷的 ID 找到对应的 VolumeInfo 对象,如果找不到,抛出 IllegalArgumentException 异常
2383 final VolumeInfo vol = findVolumeByIdOrThrow(volId);
2384 // 调用私有的 unmount 方法,传入 VolumeInfo 对象,用于执行卸载操作
2385 unmount(vol);
2386 }
2387
// 这个方法是 StorageManagerService 类的一个私有方法,用于卸载指定的 VolumeInfo 对象
2389 private void unmount(VolumeInfo vol) {
2390 try {
2391 try {
2392 // 判断卷的类型是否为 VolumeInfo.TYPE_PRIVATE,如果是,调用 mInstaller 的 onPrivateVolumeRemoved 方法,传入卷的 UUID,用于移除私有卷的镜像数据
2393 if (vol.type == VolumeInfo.TYPE_PRIVATE) {
2394 mInstaller.onPrivateVolumeRemoved(vol.getFsUuid());
2395 }
2396 } catch (Installer.InstallerException e) {
2397 // 如果 onPrivateVolumeRemoved 方法抛出 Installer.InstallerException 异常,记录错误日志
2398 Slog.e(TAG, "Failed unmount mirror data", e);
2399 }
2400 // 调用 mVold 的 unmount 方法,传入卷的 ID,用于卸载卷
2401 mVold.unmount(vol.id);
2402 // 调用 mStorageSessionController 的 onVolumeUnmount 方法,传入卷的信息,用于处理卸载后的逻辑
2403 mStorageSessionController.onVolumeUnmount(vol);
2404 } catch (Exception e) {
2405 // 如果发生其他异常,记录错误日志
2406 Slog.wtf(TAG, e);
2407 }
2408 }
getボリューム():
// 这个方法是 StorageManagerService 类的一个公开方法,用于获取所有已挂载的外部存储设备卷的列表
4001 @Override
4002 public VolumeInfo[] getVolumes(int flags) {
4003 // 同步锁定 mLock 对象,用于保证线程安全
4004 synchronized (mLock) {
4005 // 创建一个 VolumeInfo 类型的数组,大小为 mVolumes 集合的大小
4006 final VolumeInfo[] res = new VolumeInfo[mVolumes.size()];
4007 // 遍历 mVolumes 集合,将每个元素复制到数组中
4008 for (int i = 0; i < mVolumes.size(); i++) {
4009 res[i] = mVolumes.valueAt(i);
4010 }
4011 // 返回数组
4012 return res;
4013 }
4014 }
getAllocatableBytes(String volumeUuid, int flags, String CallingPackage):
// 这个方法是 StorageManagerService 类的一个公开方法,用于获取指定 UUID 的外部存储设备卷上可分配给应用程序的字节数
4082 @Override
4083 public long getAllocatableBytes(String volumeUuid, int flags, String callingPackage) {
4084 // 调整分配标志,根据调用者的 UID 和包名,判断是否需要添加 FLAG_ALLOCATE_AGGRESSIVE 标志
4085 flags = adjustAllocateFlags(flags, Binder.getCallingUid(), callingPackage);
4086
4087 // 获取 StorageManager 和 StorageStatsManager 的实例,用于操作存储相关的数据
4088 final StorageManager storage = mContext.getSystemService(StorageManager.class);
4089 final StorageStatsManager stats = mContext.getSystemService(StorageStatsManager.class);
4090 // 保存当前的调用标识,并清除调用者的身份,用于跨进程调用
4091 final long token = Binder.clearCallingIdentity();
4092 try {
4093 // 一般情况下,应用程序可以分配尽可能多的空间,除非超过了最小缓存空间或低磁盘警告空间的限制。为了避免用户混淆,这个逻辑应该和 getFreeBytes() 方法保持一致
4094 // 根据卷的 UUID 找到对应的文件路径
4095 final File path = storage.findPathForUuid(volumeUuid);
4096
4097 // 初始化可用空间、低保留空间、满保留空间和可清除缓存空间为 0
4098 long usable = 0;
4099 long lowReserved = 0;
4100 long fullReserved = 0;
4101 long cacheClearable = 0;
4102
4103 // 如果没有设置 FLAG_ALLOCATE_CACHE_ONLY 标志,表示可以分配非缓存空间
4104 if ((flags & StorageManager.FLAG_ALLOCATE_CACHE_ONLY) == 0) {
4105 // 获取文件路径上的可用空间大小
4106 usable = path.getUsableSpace();
4107 // 获取文件路径上的低磁盘警告空间大小
4108 lowReserved = storage.getStorageLowBytes(path);
4109 // 获取文件路径上的磁盘满空间大小
4110 fullReserved = storage.getStorageFullBytes(path);
4111 }
4112
4113 // 如果没有设置 FLAG_ALLOCATE_NON_CACHE_ONLY 标志,并且卷支持配额功能,表示可以分配缓存空间
4114 if ((flags & StorageManager.FLAG_ALLOCATE_NON_CACHE_ONLY) == 0
4115 && stats.isQuotaSupported(volumeUuid)) {
4116 // 获取卷上的缓存总大小
4117 final long cacheTotal = stats.getCacheBytes(volumeUuid);
4118 // 获取卷上的缓存保留大小
4119 final long cacheReserved = storage.getStorageCacheBytes(path, flags);
4120 // 计算卷上的可清除缓存大小,即缓存总大小减去缓存保留大小,如果为负数,则取 0
4121 cacheClearable = Math.max(0, cacheTotal - cacheReserved);
4122 }
4123
4124 // 如果设置了 FLAG_ALLOCATE_AGGRESSIVE 标志,表示可以分配更多的空间,但可能会影响系统性能或稳定性
4125 if ((flags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0) {
4126 // 返回可用空间加上可清除缓存空间再减去磁盘满空间大小,如果为负数,则取 0
4127 return Math.max(0, (usable + cacheClearable) - fullReserved);
4128 } else {
4129 // 否则,返回可用空间加上可清除缓存空间再减去低磁盘警告空间大小,如果为负数,则取 0
4130 return Math.max(0, (usable + cacheClearable) - lowReserved);
4131 }
4132 } catch (IOException e) {
4133 // 如果发生 IOException 异常,将其封装成一个 ParcelableException 对象,并抛出
4134 throw new ParcelableException(e);
4135 } finally {
4136 // 无论成功或失败,都要恢复之前的调用标识
4137 Binder.restoreCallingIdentity(token);
4138 }
4139 }
assignBytes(UUID storageUuid、ロングバイト):
// 这个方法是 StorageManagerService 类的一个公开方法,用于在指定 UUID 的外部存储设备卷上为应用程序分配指定字节数的空间
4126 @Override
4127 public void allocateBytes(String volumeUuid, long bytes, int flags, String callingPackage) {
4128 // 调整分配标志,根据调用者的 UID 和包名,判断是否需要添加 FLAG_ALLOCATE_AGGRESSIVE 标志
4129 flags = adjustAllocateFlags(flags, Binder.getCallingUid(), callingPackage);
4130
4131 // 获取卷上可分配给应用程序的非缓存空间大小
4132 final long allocatableBytes = getAllocatableBytes(volumeUuid,
4133 flags | StorageManager.FLAG_ALLOCATE_NON_CACHE_ONLY, callingPackage);
4134 // 如果要分配的字节数大于可分配的非缓存空间大小,表示空间不足
4135 if (bytes > allocatableBytes) {
4136 // 如果空间不足,检查卷上可分配给应用程序的缓存空间大小
4137 final long cacheClearable = getAllocatableBytes(volumeUuid,
4138 flags | StorageManager.FLAG_ALLOCATE_CACHE_ONLY, callingPackage);
4139 // 如果要分配的字节数大于可分配的非缓存空间大小加上可分配的缓存空间大小,表示即使清除缓存也无法满足需求,抛出 IOException 异常
4140 if (bytes > allocatableBytes + cacheClearable) {
4141 throw new ParcelableException(new IOException("Failed to allocate " + bytes
4142 + " because only " + (allocatableBytes + cacheClearable) + " allocatable"));
4143 }
4144 }
4145
4146 // 获取 StorageManager 的实例,用于操作存储相关的数据
4147 final StorageManager storage = mContext.getSystemService(StorageManager.class);
4148 // 保存当前的调用标识,并清除调用者的身份,用于跨进程调用
4149 final long token = Binder.clearCallingIdentity();
4150 try {
4151 // 为了满足分配需求和低磁盘警告空间的限制,释放足够的磁盘空间
4152 final File path = storage.findPathForUuid(volumeUuid);
4153 // 如果设置了 FLAG_ALLOCATE_AGGRESSIVE 标志,表示可以分配更多的空间,但可能会影响系统性能或稳定性,需要加上磁盘满空间大小
4154 if ((flags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0) {
4155 bytes += storage.getStorageFullBytes(path);
4156 } else {
4157 // 否则,只需要加上低磁盘警告空间大小
4158 bytes += storage.getStorageLowBytes(path);
4159 }
4160
4161 // 调用 mPmInternal 的 freeStorage 方法,传入卷的 UUID、要释放的字节数和分配标志,用于清理不必要的数据
4162 mPmInternal.freeStorage(volumeUuid, bytes, flags);
4163 } catch (IOException e) {
4164 // 如果发生 IOException 异常,将其封装成一个 ParcelableException 对象,并抛出
4165 throw new ParcelableException(e);
4166 } finally {
4167 // 无论成功或失败,都要恢复之前的调用标识
4168 Binder.restoreCallingIdentity(token);
4169 }
4170 }
ボリューム情報.java
http://aospxref.com/android-13.0.0_r3/xref/frameworks/base/core/java/android/os/storage/VolumeInfo.java
: このクラスは、ボリューム ID、タイプ、ステータス、パス、その他の属性を含む、外部ストレージ デバイスのボリューム情報を表します。これは、Intent または Bundle を介して渡すことができる Parcelable オブジェクトです。次のようないくつかの重要なプロパティとメソッドが提供されます。
isMountedReadable():
// 这判断该卷是否已挂载且可读取。
@UnsupportedAppUsage
public boolean isMountedReadable() {
// 这个方法检查存储卷的状态是否是已挂载或只读挂载
return state == STATE_MOUNTED || state == STATE_MOUNTED_READ_ONLY;
}
isMountedWritable():
//判断该卷是否已挂载且可写入。
@UnsupportedAppUsage
public boolean isMountedWritable() {
// 这个方法检查存储卷的状态是否是已挂载
return state == STATE_MOUNTED;
}
FileUtils.java
http://aospxref.com/android-13.0.0_r3/xref/frameworks/base/core/java/android/os/FileUtils.java
: このクラスは、ファイル操作、ファイル権限の設定などのためのいくつかのツール メソッドを提供します。インスタンスを作成する必要はありません。次のようないくつかの主要なメソッドが提供されます。
setPermissions(ファイルパス、intモード、int uid、int gid)
151 @UnsupportedAppUsage
152 public static int setPermissions(File path, int mode, int uid, int gid) {
153 // 调用另一个重载的方法,传入文件的绝对路径作为参数
153 return setPermissions(path.getAbsolutePath(), mode, uid, gid);
154 }
155
156 /**
157 * 设置给定路径的所有者和模式。
158 *
159 * @param mode 通过 {@code chmod} 应用的模式
160 * @param uid 通过 {@code chown} 应用的用户ID,或 -1 表示不改变
161 * @param gid 通过 {@code chown} 应用的组ID,或 -1 表示不改变
162 * @return 成功时返回 0,否则返回错误码。
163 * @hide
164 */
165 @UnsupportedAppUsage
166 public static int setPermissions(String path, int mode, int uid, int gid) {
167 try {
168 // 使用 Os 类的 chmod 方法修改文件的权限
168 Os.chmod(path, mode);
169 } catch (ErrnoException e) {
170 // 如果出现异常,打印警告信息,并返回错误码
170 Slog.w(TAG, "Failed to chmod(" + path + "): " + e);
171 return e.errno;
172 }
173
174 // 如果 uid 或 gid 不为 -1,表示需要修改文件的所有者
174 if (uid >= 0 || gid >= 0) {
175 try {
176 // 使用 Os 类的 chown 方法修改文件的所有者
176 Os.chown(path, uid, gid);
177 } catch (ErrnoException e) {
178 // 如果出现异常,打印警告信息,并返回错误码
178 Slog.w(TAG, "Failed to chown(" + path + "): " + e);
179 return e.errno;
180 }
181 }
182
183 // 如果没有异常,返回 0 表示成功
183 return 0;
184 }
185
186 /**
187 * 设置给定 {@link FileDescriptor} 的所有者和模式。
188 *
189 * @param mode 通过 {@code chmod} 应用的模式
190 * @param uid 通过 {@code chown} 应用的用户ID,或 -1 表示不改变
191 * @param gid 通过 {@code chown} 应用的组ID,或 -1 表示不改变
192 * @return 成功时返回 0,否则返回错误码。
193 * @hide
194 */
195 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
196 public static int setPermissions(FileDescriptor fd, int mode, int uid, int gid) {
197 try {
198 // 使用 Os 类的 fchmod 方法修改文件描述符的权限
198 Os.fchmod(fd, mode);
199 } catch (ErrnoException e) {
200 // 如果出现异常,打印警告信息,并返回错误码
200 Slog.w(TAG, "Failed to fchmod(): " + e);
201 return e.errno;
202 }
203
204 // 如果 uid 或 gid 不为 -1,表示需要修改文件描述符的所有者
204 if (uid >= 0 || gid >= 0) {
205 try {
206 // 使用 Os 类的 fchown 方法修改文件描述符的所有者
206 Os.fchown(fd, uid, gid);
207 } catch (ErrnoException e) {
208 // 如果出现异常,打印警告信息,并返回错误码
208 Slog.w(TAG, "Failed to fchown(): " + e);
209 return e.errno;
210 }
211 }
212
213 // 如果没有异常,返回 0 表示成功
213 return 0;
214 }
SELinux.java
http://aospxref.com/android-13.0.0_r3/xref/frameworks/base/core/java/android/os/SELinux.java
: このクラスは、ファイルに SELinux コンテキスト タグがあるかどうかの確認など、SELinux 関連のメソッドをいくつか提供します。SELinux は、ファイルとプロセスのより詳細なアクセス制御を可能にする、セキュリティが強化された Linux システムです。これは静的クラスであり、インスタンスを作成する必要はありません。次のようないくつかの主要なメソッドが提供されます。- isSELinuxEnabled(): 現在のシステムで SELinux メカニズムが有効かどうかを判断します。
- getFileContext(String path): 指定されたパスを持つファイルの SELinux コンテキスト ラベルを取得します。
- setFileContext(String path, String context): 指定されたパスを持つファイルの SELinux コンテキスト ラベルを設定します。
51 /**
52 * 判断 SELinux 是否被禁用或启用。
53 * @return 表示 SELinux 是否启用。
54 */
55 @UnsupportedAppUsage
56 // 声明一个本地方法,使用 native 关键字,不需要提供方法体
56 public static final native boolean isSELinuxEnabled();
72 /**
73 * 改变已存在的文件对象的安全上下文。
74 * @param path 表示要重新标记的文件对象的路径。
75 * @param context 新的安全上下文,以字符串形式给出。
76 * @return 表示操作是否成功。
77 */
78 // 声明一个本地方法,使用 native 关键字,不需要提供方法体
78 public static final native boolean setFileContext(String path, String context);
80 /**
81 * 获取文件对象的安全上下文。
82 * @param path 文件对象的路径名。
83 * @return 表示安全上下文。
84 */
85 @UnsupportedAppUsage
86 // 声明一个本地方法,使用 native 关键字,不需要提供方法体
86 public static final native String getFileContext(String path);
95 /**
96 * 获取文件描述符对应文件的安全上下文。
97 * @param fd 文件的文件描述符。
98 * @return 表示文件描述符的安全上下文。
99 */
100 // 声明一个本地方法,使用 native 关键字,不需要提供方法体
100 public static final native String getFileContext(FileDescriptor fd);
ファイルとディレクトリのアクセス許可
システム内の特定のディレクトリまたはファイルにアクセスできる UID またはグループを決定するには、いくつかの方法があります。
- このコマンドを使用して、
ls -l
ファイルまたはディレクトリの権限と所有者を表示します。例えば:- ファイルのアクセス許可が の場合
-rw-r--r--
、そのファイルはその所有者によって読み取りおよび書き込み可能 (最初のrw
)、ファイルが属するグループによって読み取り可能 (2 番目のr
)、および他のユーザーによって読み取り可能 ( 3番目r
)。 - ディレクトリの権限が の場合
drwxr-x---
、そのディレクトリはその所有者によって読み取り、書き込み、および実行可能 (最初のrwx
)、ディレクトリが属するグループによって読み取りおよび実行可能 (2 つ目rx
)、および他のユーザーはアクセスできないことを意味します。他のユーザー (3 番目-
)。
- ファイルのアクセス許可が の場合
stat
ファイルまたはディレクトリに関する詳細情報を表示するには、このコマンドを使用します。例えば:- ファイルの情報が である場合
Access: (0644/-rw-r--r--) Uid: ( 1000/ system) Gid: ( 0/ root)
、そのファイルのパーミッションは であることを意味し0644
、8 進数で表すことができます-rw-r--r--
。ファイルの所有者はUID を持つ0
ユーザーでありroot
、ファイルが属するグループはGID を持つ0
グループです。root
。
- ファイルの情報が である場合
- このコマンドを使用して、
find
ユーザーの所有者、グループ、UID、GID、およびその他の条件に基づいてファイルまたはディレクトリを検索します。例えば:- UID を持つユーザー
/data/data
に属するディレクトリ内のすべてのファイルを検索する場合は、次のコマンドを使用できます。1000
find /data/data -uid 1000
- UID を持つユーザー
# 查看 /etc/passwd文件的权限和所有者
rk3568_t:/etc # ls -ll passwd
-rw-r--r-- 1 root root 0 2023-07-31 06:49:44.000000000 +0000 passwd
# 查看 /etc/passwd 文件的详细信息
rk3568_t:/etc # stat passwd
File: passwd
Size: 0 Blocks: 0 IO Blocks: 512 regular empty file
Device: fd00h/64768d Inode: 1782 Links: 1 Device type: 0,0
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2023-07-31 06:49:44.000000000 +0000
Modify: 2023-07-31 06:49:44.000000000 +0000
Change: 2023-07-31 06:49:44.000000000 +0000
# 查找 /data/data 目录下属于 system 用户或者 system 组的所有目录
rk3568_t:/data/data # find . -user system -o -group system -type d
.
./com.android.dynsystem
./com.android.dynsystem/cache
./com.android.dynsystem/code_cache
Android 11、12、13 におけるファイル アクセス許可の変更と適応方法
Android 11 以降、Google はユーザーのプライバシーとデータ セキュリティを保護するために、外部ストレージ デバイスにアクセスするアプリケーションの権限にいくつかの制限と変更を加えています。これらの制限と変更は、ファイルの読み取りと書き込みが必要な一部のアプリケーションの機能と互換性に影響を与える可能性があります。これらのアクセス許可の意味と使用法を理解し、アプリケーションのニーズに応じてそれらを調整する必要があります。次の表に、外部ストレージ デバイスに関連する 3 つの権限と、その説明、レベル、および適用可能なバージョンを示します。
権限名 | 権限の説明 | 許可レベル | 対象バージョン |
---|---|---|---|
android.permission.READ_EXTERNAL_STORAGE | アプリケーションが外部ストレージ デバイス (SD カードや USB フラッシュ ドライブなど) から、写真、オーディオ、ビデオなどのメディア ファイルを含むファイルを読み取ることができるようにします。 | 実行時にユーザーの承認を必要とする危険な権限。 | Android 4.1 (API レベル 16) 以降。 |
android.permission.WRITE_EXTERNAL_STORAGE | アプリケーションが、写真、オーディオ、ビデオなどのメディア ファイルを含むファイルを外部ストレージ デバイス (SD カードや USB フラッシュ ドライブなど) に書き込むことができるようにします。この権限には、READ_EXTERNAL_STORAGE 権限の機能も含まれます。 | 実行時にユーザーの承認を必要とする危険な権限。 | Android 4.1 (API レベル 16) 以降。 |
android.permission.MANAGE_EXTERNAL_STORAGE | アプリが、システムや他のアプリのファイルを含む、外部ストレージ デバイス上のすべてのファイルにアクセスし、変更することを許可します。この権限には、WRITE_EXTERNAL_STORAGE 権限の機能も含まれています。 | 特別な権限を使用するには、システム設定でユーザーからの承認が必要です。 | Android 11 (API レベル 30) 以降。 |
以下に、これらの権限の変更と適応方法を簡単に紹介します。
- Android 10 以前では、アプリケーションが WRITE_EXTERNAL_STORAGE 権限を宣言し、ユーザーの承認を取得すると、パブリック ディレクトリ (写真、音楽など) や他のアプリケーションのプライベート ファイルを含む、外部ストレージ デバイス上のすべてのファイルにアクセスできます。 . ディレクトリ (Android/data や Android/obb など)。このモデルは「トラディショナル ストレージ」モデルと呼ばれます。
- Android 11 では、Google は「パーティションストレージ」モードと呼ばれる新しいモードを導入しました。このモードでは、アプリケーションは、独自のプライベート ディレクトリおよびパブリック ディレクトリにある独自のタイプのメディア ファイル (画像、オーディオ、ビデオなど) にのみアクセスできます。アプリケーションが他のタイプまたはアプリケーションのファイルにアクセスしたい場合は、MANAGE_EXTERNAL_STORAGE権限を宣言し、システム設定で「すべてのファイル アクセス権限」を付与するようにユーザーをガイドする必要があります。この種の許可は特別な許可であり、正当な理由があって使用する必要があります。そうでない場合、Google Play によって拒否される可能性があります。さらに、アプリケーションは MediaStore API またはシステム ファイル セレクター SAF を使用して、追加の権限を申請せずにメディア ファイルまたは非メディア ファイルにアクセスしたり共有したりすることもできます。
- Android 12 では、Google はパーティション分割ストレージ モードにいくつかの改善と最適化を加えました。たとえば、アプリケーションによるメディア ファイルのクエリ、挿入、更新、削除を容易にするために、いくつかの MediaStore API メソッドと定数が追加されました。また、ユーザーがどのアプリケーションが通知を送信できるかをより適切に制御できるようにするために、新しい実行時権限 POST_NOTIFICATIONS が追加されました。新しいメカニズムであるフォアグラウンド サービス (FGS) タスク マネージャーが追加され、ユーザーがどのアプリケーションがバックグラウンドでフォアグラウンド サービスを実行できるかをより適切に管理できるようになりました。
- Android 13 では、Google は引き続きパーティション分割ストレージ モデルの調整と改善を行っています。たとえば、アプリケーションがさまざまなオーディオ デバイスに適切に適応できるようにオーディオのルーティング方法を決定する新しい API が追加され、ユーザーがバックグラウンドでボディにアクセスできるアプリケーションをより適切に制御できるように新しい権限 BODY_SENSORS_BACKGROUND が追加されました。各アプリケーションでユーザーの優先言語を設定または取得するための新しい API が追加され、アプリケーションがさまざまな言語環境に適切に適応できるようになりました。
Android 11 以降、アプリケーションが外部ストレージ デバイスにアクセスするための権限はより厳密かつ詳細になったため、アプリケーションのニーズと機能に基づいて適応する適切な権限と API を選択する必要があります。