Androidでは見えない外部ストレージパス

6894 ワード

この問題はバグの分析過程を起源とし、APPのcacheパスはadbでアクセスできない.
Android 5.1コードに基づく分析
Androidアプリケーションでは、通常、ストレージパスを取得する方法として以下のいくつかが使用されます.
  • Environment.getDownloadCacheDirectory():/cache 、cacheディレクトリパス.
  • Environment.getRootDirectory():/system、systemディレクトリパス.
  • Environment.getDataDirectory():/data、内部に格納されているルートパス.
  • Context.getFilesDir():/data/user/0//files、内部ストレージのfilesパス.
  • Context.getCacheDir():/data/user/0//cache、内部ストレージ内のcacheパス.
  • Context.getPackageCodePath():プログラムのインストールパッケージパス.
  • Context.getPackageResourcePath():現在のアプリケーションに対応するapkファイルのパス.
  • Context.getDatabasePath(「xxx」):Context.を介してOpenOrCreateDatabaseで作成されたデータベースファイル.
  • Context.getDir(「xxx」,Context.MODE_PRIVATE):内部ストレージに適用されるカスタムパスを取得します.
  • Context.Environment.getExternalStorageDirectory():/storage/emulated/0、外部ストレージパスを取得します.
  • Context.Environment.getExternalStoragePublicDirectory(「xxx」):外部ストレージ上の共通パスを取得します.
  • Context.getExternalFilesDir():/storage/emulated/0/Android/data//files、外部ストレージのfilesパス.
  • Context.getExternalCacheDir():/storage/emulated/0/Android/data//cache、外部ストレージのcacheパス.

  • 上記のインタフェースの多くは、今回議論するインタフェースとは無関係であり、主に外部ストレージパスに注目しています.外部ストレージパスは、Androidバージョンによって異なる場合があります.上記はAndroid 5.1に表示された結果です.外部ストレージの意味は、バージョンによって異なります.Android 4.4以前のバージョンでは、外部ストレージとは拡張されたSDカード、すなわちスロットで接続されたSDカードを指す.Android 4.4から、システムは本体ストレージを内部ストレージと外部ストレージに分け、SDカードを拡張していない場合、外部ストレージは本体ストレージの一部であり、/storage/emulated/0を指す.拡張SDカードが挿入されると、2つの外部ストレージパスが得られます.
    getExternalStorageDirectory()の異なるAndroidバージョンでのパスは、次のとおりです.
    システムバージョン
    外部ストレージパス
    SDカードパスの拡張
    4.0
    /mnt/sdcard
    /mnt/sdcard
    4.1
    /storage/sdcard0
    /storage/sdcard0
    4.2
    /storage/sdcard0
    /storage/sdcard1
    4.4
    /storage/emulated/0
    /storage/sdcard1
    6.0
    /storage/emulated/0
    /storage/
    上記の表のパスは実際に検証したことがありません.また、デバイスメーカーは自分のSDカードのパスを変更します.実際のパスとは異なるかもしれません.Android 4上のストレージパス自体が混乱している状態であり、これも私たちが関心を持っているポイントではありません.我々が関心を持っているのは/storage/emulated/0であり,この経路は機体内部記憶シミュレーションを用いた外部記憶である.アプリケーションでは、getExternalStorageDirectory()を介して戻ってくるのがこのパスなので、アプリケーション自体のデータがこのパスの下に書き込まれます.しかし、adb接続を使用してメモリデバッグを行うと、このパスは存在せず、次の結果しか表示されません.
    # ls -l /storage/emulated/                                   
    lrwxrwxrwx root     root              1970-01-01 08:00 legacy -> /mnt/shell/emulated/0
    # ls -l /mnt/shell/emulated
    drwxrwx--x root     sdcard_r          2020-04-15 11:07 0
    drwxrwx--x root     sdcard_r          1970-01-01 08:03 legacy
    drwxrwx--x root     sdcard_r          1970-01-01 08:00 obb

    アプリケーションのデータが/mnt/shell/emulated/0に書き込まれることはよく知られていますが、このパスはどのように/storage/emulated/0に関連付けられていますか?LinuxのMountネーミングスペースについてお話しします.
    Mountネーミングスペースは、ユーザーまたはコンテナが独立したファイルシステムツリーを提供します.プロセスごとに表示されるマウントポイントのリストを分離します.つまり、Mountネーミングスペースごとに独自のマウントポイントのリストがあります.これは、異なるネーミングスペース内のプロセスが異なるディレクトリ階層(ディレクトリツリー)を表示および制御できることを意味します.簡単に言えば、各プロセスには独自のMountノードがあり、他のプロセスは見えません.Androidでどのように使われているかを見てみましょう.アプリケーションの起動時に、次のプロセスでシミュレーションストレージがマウントされます.
    Zygote.forkAndSpecialize() ->
        Dalvik_dalvik_system_Zygote_forkAndSpecialize() ->
            forkAndSpecializeCommon() ->
                mountEmulatedStorage()

    MountEmulatedStorage()は、特定のアナログストレージマウントを完了します.
    frameworks/base/core/jni/com_android_internal_os_Zygote.cpp
    
    static bool MountEmulatedStorage(uid_t uid, jint mount_mode, bool force_mount_namespace) {
      if (mount_mode == MOUNT_EXTERNAL_NONE && !force_mount_namespace) {
        return true;
      }
    
      //                
      if (unshare(CLONE_NEWNS) == -1) {
        ......
      }
      ......
    
      //  uid     id,       10000 uid
      userid_t user_id = multiuser_get_user_id(uid);
    
      // Bind    
      if (mount_mode == MOUNT_EXTERNAL_MULTIUSER || mount_mode == MOUNT_EXTERNAL_MULTIUSER_ALL) {
        // These paths must already be created by init.rc
        const char* source = getenv("EMULATED_STORAGE_SOURCE");
        const char* target = getenv("EMULATED_STORAGE_TARGET");
        const char* legacy = getenv("EXTERNAL_STORAGE");
        ......
        // /mnt/shell/emulated/0
        const String8 source_user(String8::format("%s/%d", source, user_id));
        // /storage/emulated/0
        const String8 target_user(String8::format("%s/%d", target, user_id));
    
        if (fs_prepare_dir(source_user.string(), 0000, 0, 0) == -1
            || fs_prepare_dir(target_user.string(), 0000, 0, 0) == -1) {
          return false;
        }
    
        if (mount_mode == MOUNT_EXTERNAL_MULTIUSER_ALL) {
          ......
        } else {
          //   /mnt/shell/emulated/0     /storage/emulated/0  
          if (TEMP_FAILURE_RETRY(
                  mount(source_user.string(), target_user.string(), NULL, MS_BIND, NULL)) == -1) {
            ......
          }
        }
    
        if (fs_prepare_dir(legacy, 0000, 0, 0) == -1) {
            return false;
        }
    
        //   /storage/emulated/0     /storage/emulated/legacy  
        if (TEMP_FAILURE_RETRY(
                mount(target_user.string(), legacy, NULL, MS_BIND | MS_REC, NULL)) == -1) {
          ......
        }
      } else {
        ......
      }
    
      return true;       
    }

    実装プロセスも簡単です.アプリケーションの起動中に独立したMountネーミングスペースを作成し、そのネーミングスペースでMS_を使用します.BINDは、/mnt/shell/emulated/0/storage/emulated/0にバインドし、/storage/emulated/0/storage/emulated/legacyにバインドし、/storage/emulated/0へのアクセスは/mnt/shell/emulated/0へのアクセスと同等である.adbはzygoteで起動したわけではないので、/storage/emulated/0ディレクトリは見えません.
    シミュレーションストレージのマウントには、init.rcで設定された環境変数を使用する必要があります.次に、init.rcの記憶パスに関するコードを示す.
        export EXTERNAL_STORAGE /storage/emulated/legacy #       
        export EMULATED_STORAGE_SOURCE /mnt/shell/emulated #         
        export EMULATED_STORAGE_TARGET /storage/emulated #          
        export SECONDARY_STORAGE /storage/sdcard1 #   SD   
    
        # Support legacy paths
        #         /sdcard,/mnt/sdcard,/storage/sdcard0
        symlink /storage/emulated/legacy /sdcard 
        symlink /storage/emulated/legacy /mnt/sdcard
        symlink /storage/emulated/legacy /storage/sdcard0
        #     /mnt/shell/emulated/0       ,          
        symlink /mnt/shell/emulated/0 /storage/emulated/legacy
        #        
        mkdir /storage/external_storage 0555 root root
        symlink /storage/sdcard1 /storage/external_storage/sdcard1
        symlink /storage/udisk0 /storage/external_storage/udisk0
        symlink /storage/sr0 /storage/external_storage/sr0

    参照ドキュメント:
    Androidファイルストレージ---内部ストレージ、外部ストレージ、およびさまざまなストレージパスを徹底的に理解する