Androidシステムのファイルアクセス許可に関する注意事項

ファイル アクセス許可は、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に属するディレクトリ内のすべてのファイルを検索する場合は、次のコマンドを使用できます。1000find /data/data -uid 1000
# 查看 /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 を選択する必要があります。


おすすめ

転載: blog.csdn.net/SHH_1064994894/article/details/132869552