Androidでは見えない外部ストレージパス
6894 ワード
この問題はバグの分析過程を起源とし、APPのcacheパスはadbでアクセスできない.
Android 5.1コードに基づく分析
Androidアプリケーションでは、通常、ストレージパスを取得する方法として以下のいくつかが使用されます. Environment.getDownloadCacheDirectory(): Environment.getRootDirectory(): Environment.getDataDirectory(): Context.getFilesDir(): Context.getCacheDir(): Context.getPackageCodePath():プログラムのインストールパッケージパス. Context.getPackageResourcePath():現在のアプリケーションに対応するapkファイルのパス. Context.getDatabasePath(「xxx」):Context.を介してOpenOrCreateDatabaseで作成されたデータベースファイル. Context.getDir(「xxx」,Context.MODE_PRIVATE):内部ストレージに適用されるカスタムパスを取得します. Context.Environment.getExternalStorageDirectory(): Context.Environment.getExternalStoragePublicDirectory(「xxx」):外部ストレージ上の共通パスを取得します. Context.getExternalFilesDir(): Context.getExternalCacheDir():
上記のインタフェースの多くは、今回議論するインタフェースとは無関係であり、主に外部ストレージパスに注目しています.外部ストレージパスは、Androidバージョンによって異なる場合があります.上記はAndroid 5.1に表示された結果です.外部ストレージの意味は、バージョンによって異なります.Android 4.4以前のバージョンでは、外部ストレージとは拡張されたSDカード、すなわちスロットで接続されたSDカードを指す.Android 4.4から、システムは本体ストレージを内部ストレージと外部ストレージに分け、SDカードを拡張していない場合、外部ストレージは本体ストレージの一部であり、
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上のストレージパス自体が混乱している状態であり、これも私たちが関心を持っているポイントではありません.我々が関心を持っているのは
アプリケーションのデータが
Mountネーミングスペースは、ユーザーまたはコンテナが独立したファイルシステムツリーを提供します.プロセスごとに表示されるマウントポイントのリストを分離します.つまり、Mountネーミングスペースごとに独自のマウントポイントのリストがあります.これは、異なるネーミングスペース内のプロセスが異なるディレクトリ階層(ディレクトリツリー)を表示および制御できることを意味します.簡単に言えば、各プロセスには独自のMountノードがあり、他のプロセスは見えません.Androidでどのように使われているかを見てみましょう.アプリケーションの起動時に、次のプロセスでシミュレーションストレージがマウントされます.
MountEmulatedStorage()は、特定のアナログストレージマウントを完了します.
実装プロセスも簡単です.アプリケーションの起動中に独立したMountネーミングスペースを作成し、そのネーミングスペースでMS_を使用します.BINDは、
シミュレーションストレージのマウントには、
参照ドキュメント:
Androidファイルストレージ---内部ストレージ、外部ストレージ、およびさまざまなストレージパスを徹底的に理解する
Android 5.1コードに基づく分析
Androidアプリケーションでは、通常、ストレージパスを取得する方法として以下のいくつかが使用されます.
/cache
、cacheディレクトリパス./system
、systemディレクトリパス./data
、内部に格納されているルートパス./data/user/0//files
、内部ストレージのfilesパス./data/user/0//cache
、内部ストレージ内のcacheパス./storage/emulated/0
、外部ストレージパスを取得します./storage/emulated/0/Android/data//files
、外部ストレージのfilesパス./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ファイルストレージ---内部ストレージ、外部ストレージ、およびさまざまなストレージパスを徹底的に理解する