1 background
The previous chapter [Android Framework Series] Chapter 16 Storage Access Framework (SAF) mainly analyzed the storage access framework (SAF) introduced in Android 4.4 存储访问框架(SAF)
. In this chapter we Android10(Q)
analyze the storage related and understand its restricted storage methods.
Google
To give users more control over their files and limit file clutter, the way you access your device's storage Android Q
has changed .App
Before 1.1 Android Q
As long as the program obtains READ_EXTERNAL_STORAGE
permission, it can freely read the external storage public directory;
as long as the program obtains WRITE_EXTERNAL_STORAGE
permission, it can freely create new files or folders on the public directory written to the external storage.
1.2 Android Q and later
So Google proposed it in Android Q 分区存储
, intending to limit the use of public directories in external storage by programs .
Partitioned storage has no impact on both internal storage private directories and external storage private directories.
2 Sandbox mode
2.1 Android Q stipulates that the App has two storage space mode views: Legacy View and Filtered View.
2.1.1 Legacy View (compatibility mode)
When the project targetSdkVersion <= 28, the compatibility mode is used by default on the AndroidQ device, the storage method is the same as Android Q
before, App
and the access Sdcard
is complete, with full access permissions.
2.1.2 Filtered View (sandbox mode)
When the project targetSdkVersion > 28, the AndroidQ device App
can only directly access App-specific
directory files and does not have permission to access App-specific
external files. Access to other directories can only be provided through MediaStore
, SAF
, or other Apps ContentProvider
.
2.2 Scoped Storage divides the storage space into two parts
2.2.1 SD card public directory: Downloads, Documents, Pictures, DCIM, Movies, Music, Ringtones
- Files in the public directory
App
will not be deleted after uninstallation - Can be accessed through
SAF
theMediaStore
interface
2.2…2 data/data private directory (App-specific directory)
- For
Filtered View App
,App-specific
the directory can only be accessed directly by yourself App
Uninstall and the data will be cleared.
2.3 Compatibility impact
Scoped Storage
It has a great impact on App访问存储方式
, App数据存放
and App间数据共享
.
2.4 Adaptation
For specific adaptation, please refer to the official documentation.
2.4.1 App running view
The system determines the App running mode through the following:
App TargetSDK > 28
,defaultFiltered View(沙箱模式)
App TargetSDK <= 28
, declares READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE permission, defaultLegacy View(兼容模式)
- The application can set requestLegacyExternalStorage through AndroidManifest.xml and select the corresponding method:
declaredREAD_EXTERNAL_STORAGE
,WRITE_EXTERNAL_STORAGE
permission (ignored if not declared):
requestLegacyExternalStorage=true
meansLegacy View(兼容模式)
requestLegacyExternalStorage=false
Filtered View(沙箱模式)
(注意:该方式Android11后失效了)
- System applications can apply for
android.permission.WRITE_MEDIA_STORAGE
system permissions and also have full storage space permissions and can access all files. However, in the CTS test, only apps that have no user interaction and are visible can apply. Specific reference《Android Bootcamp 2019 - Privacy Overview.pdf》
.
- App is owned when the following conditions are met
: ① StatementINSTALL_PACKAGES
, or动态申请INSTALL_PACKAGES
permission
② OwnershipWRITE_EXTERNAL_STORAGE权限
③ App ownership外置存储空间Read、Write权限
.
However,Environment.isExternalStorageLegacy
judging by the interface, the return is not necessarily a Legacy View.
2.4.2 Determine the current App running mode
To determine what mode the current App is running, you can use this API to determine:
Environment.isExternalStorageLegacy();
2.5 Reading and writing public directories
After the App is started Filtered View
, it can only access itself directly App-specific目录
, so Android Q
two methods of accessing the public directory are provided:
2.5.1 Uri defined by MediaStore
MediaStore
The following types of access are provided Uri
, and the purpose of access is achieved by searching for the corresponding Uri data.
Each of the following types is divided into three types Uri
, Internal
, External
, 可移动存储
:
Audio
- Internal: MediaStore.Audio.Media.INTERNAL_CONTENT_URI
content://media/internal/audio/media。- External: MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
content://media/external/audio/media。- Removable storage: MediaStore.Audio.Media.getContentUri
content://media//audio/media.
Video
- Internal: MediaStore.Video.Media.INTERNAL_CONTENT_URI
content://media/internal/video/media。- External: MediaStore.Video.Media.EXTERNAL_CONTENT_URI
content://media/external/video/media。- Removable storage: MediaStore.Video.Media.getContentUri
content://media//video/media.
Image
- Internal: MediaStore.Images.Media.INTERNAL_CONTENT_URI
content://media/internal/images/media。- External: MediaStore.Images.Media.EXTERNAL_CONTENT_URI
content://media/external/images/media。- Removable storage: MediaStore.Images.Media.getContentUri
content://media//images/media.
File
- MediaStore. Files.Media.getContentUri
content://media//file。
Downloads
- Internal: MediaStore.Downloads.INTERNAL_CONTENT_URI
content://media/internal/downloads。- External: MediaStore.Downloads.EXTERNAL_CONTENT_URI
content://media/external/downloads。- Removable storage: MediaStore.Downloads.getContentUri
content://media//downloads.
2.5.1.1 Get all Volumes
For the Uri described earlier, getContentUri
how to get all of them can be done in the following way:
for(String volume:MediaStore.getExternalVolumeNames(this)){
MediaStore.Audio.Media.getContentUri(volume);
}
2.5.1.2 Relationship between Uri and public directory
MediaProvider
For the App to be stored in a public directory file, it is determined by the Uri in the method. The relative path is ContentResolver insert
shown in the following table , and the complete file is: content://media//<Uri path>.<Uri路径>
2.5.1.3 Permissions
MediaStore
Through different methods Uri
, users can add, delete (if files cannot be deleted through File Uri, you need to use the SAF interface), and modify.
The corresponding permissions of the App are as follows:
2.5.1.4 Query files
By ContentResolver
querying different content based on different Uri:
try (Cursor c = getContentResolver()
.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, PROJECTION, null, null, null)) {
while (c.moveToNext()) {
Uri contentUri =
ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, c.getLong(0));
}
}
PS: During MediaStore.Files
the process Query
, only pictures, videos and audio files will be displayed.
2.5.1.5 Reading files
Through ContentResolver query
the interface, how to read the file after finding it can be done in the following way:
- Through
ContentResolver openFileDescriptor
the interface, select the corresponding opening method.
For example, "r" means reading, "w" means writing, and the returnParcelFileDescriptor
typeFD
. - Accessed
Thumbnail
viaContentResolver loadThumbnail
the interface
By passing the size,MediaProvider
the specified size is returnedThumbnail
. - Native code accesses files.
If Native code needs to access files, you can refer to the following methods:
① ByopenFileDescriptor
returningParcelFileDescriptor
② ByParcelFileDescriptor.detachFd()
readingFD
③FD
Passing toNative
the layer code
④App
Need to be responsible for closing throughclose
the interfaceFD
ParcelFileDescriptor parcelFd = resolver.openFileDescriptor(uri, file0penMode);
if (parcelFd != null) {
int fd = parcelFd.detachFd();
// Pass the integer value "fd" into your native code. Remember to call
// close(2) on the file descriptor when you're done using it.
}
2.5.1.6 Create new file
If you need to store a new file in a public directory, you need to ContentResolver insert
use a different interface Uri
and choose to store it in a different directory.
2.5.1.7 Modify files
If you need to modify a multimedia file, you need to ContentResolver query
find the Uri of the corresponding file through the interface.
If you did not create a new file yourself, you need to pay attention to the description in 2.5.1.3 Permissions. You need to apply WRITE_EXTERNAL_STORAGE权限
or catch RecoverableSecurityException
a pop-up box will give the user a choice.
Through the following interface, obtain the file that needs to be modified FD
or OutputStream
:
- getContentResolver().openOutputStream(contentUri)
gets the OutputStream of the corresponding file. - getContentResolver().openFile or getContentResolver().openFileDescriptor
opens the file through openFile or openFileDescriptor. You need to select Mode as "w", which indicates write permission. These interfaces return a ParcelFileDescriptor.
getContentResolver().openFileDescriptor(contentUri,“w”);
getContentResolver().openFile(contentUri,“w”,null);
2.5.1.8 Delete files
Delete files through the ContentResolver interface, and the Uri is the Uri from the query:
getContentResolver().delete(contentUri,null,null);
2.5.2 Via SAF interface
2.5.2.1 Introduction to SAF
SAF
, that is , providing users with the ability to open and browse files Storage Access Framework
by selecting different ones . DocumentsProvider
We can look back at the previous chapter
Android provides the following by default DocumentsProvider
:
MediaDocumentsProvider
, ExternalStorageProvider
, DownloadStorageProvider
.
The difference between them is:
In this picture, there are three areas, namely:
- MediaDocumentsProvide
- DownloadStorageProvider
- ExternalStorageProvider
- Third-party DocumentsProvider
2.5.2.2 How to use
Please refer to the official documentation for details
The general method is as follows:
- Select a single file
- Select the directory,
file management program, and cleanup program. You can use this method to obtain all management permissions for the corresponding directory and subdirectories. - create a new file
- delete
DocumentsContract.deleteDocument(getContentResolver(),uri);
- Revise
①Get OutputStream
getContentResolver().openOutputStream(uri);
②Get the writable ParcelFileDescriptor
getContentResolver().openFileDescriptor(contentUri,"w");
getContentResolver().openFile (contentUri,"w",null);
2.6 Access App-specific directories
There are two situations of access App-specific
, the first is access App自身App-specific目录
and the second is access 其他App目录文件
.
2.6.1 App’s own App-specific directory
Android Q, if the App is started Filtered View
, it can only directly access files in its own directory:
- The interfaces Environment.getExternalStorageDirectory and getExternalStoragePublicDirectory
are obsolete on Android Q. The App is a Filtered View and cannot directly access this directory. - Accessing
the App through File ("/sdcard/") is a Filtered View and cannot directly access this directory. - Get the App-specific directory
Get the Media interface: getExternalMediaDirs
Get the Cache interface: getExternalCacheDirs
Get the Obb interface: getObbDirs
Get the Data interface: getExternalFilesDirs
2.6.2 Multimedia files in the App-specific directory
Multimedia files inside the App-specific directory:
- App's own access
App-specific
is the same as 2.6.1 App's own directory - Access from other Apps
① By default , the multimedia files insideMedia Scanner
will not be scanned . If scanning is required, it needs to be added to the database. The access method is the same as 2.5 reading and writing public directories. ②App is shared through sharingApp-specific
MediaScannerConnection.scanFile
MediaProvider
ContentProvider
2.6.3 Other App directory files
App is a Filtered View. Other apps cannot directly access the current App private directory. You need to use the following method:
2.6.3.1 Through SAF file
- Share App customization
DocumentsProvider
App customization DocumentsProvider
requires the following steps:
a) SpecifyDocumentsProvider
b) DocumentsProvider
Implement the basic interface:
- Access the App
ACTION_OPEN_DOCUMENT
and start browsing
2.6.3.2 Sharing App to implement FileProvider
FileProvider specific usage reference
Here is a summary of the general steps:
-
Specified App FileProvider
-
Specify the file path, the configuration file must be placed in res/xml
-
Get share Uri
-
Set permissions and send Uri
-
Receive App and set the accepted inter-filter
-
Receive and process Uri
2.6.3.3 App custom private provider
Apps can be customized ContentProvider
, especially 内部文件共享
, but UI interaction is not expected.
2.7 MediaStore
2.7.1 MediaStore_data field
MediaStore
In, DATA即(_data)字段
, Android Q
began to be abandoned in. Reading and writing files requires passing openFileDescriptor
.
2.7.2 MediaStore file Pending status
On Android Q, MediaStore
a was added IS_PENDING Flag
to mark Pending
the status of the current file.
Other apps MediaStore
query files. If no setIncludePending
interface is set, files set to Pending status cannot be queried. This gives the App exclusive access to this file. Used in some situations, such as when downloading: During downloading, the file is in Pending status and the download is completed, and the Pending status of the file is set to 0.
2.7.3 MediaColumns.RELATIVE_PATH sets the storage path
On Android Q, by MediaStore
storing files in the public directory, in addition to the first-level directory of each storage space specified in the relationship between Uri and public directory in Section 2.5.1.2, the secondary directory for storage can be specified through MediaColumns.RELATIVE_PATH. This directory Multiple levels can be made, the specific code is as follows:
- ContentResolver insert method
specifies the storage directory through values.put(Media.RELATIVE_PATH,"Pictures/album/family "). Among them, Pictures is a first-level directory and album/family is a subdirectory. - ContentResolver update method
specifies the storage directory through values.put(Media.RELATIVE_PATH,"Pictures/album/family "). Through the update method, the storage location can be moved.
2.7.4 Access image Exif Metadata
On Android Q, if the App needs to access the Exif Metadata on the image, it needs to do the following:
- Apply
ACCESS_MEDIA_LOCATION权限
- The Demo Code is as
MediaStore.setRequireOriginal返回新Uri
follows:
2.7.5 App Filtered View, summary of access permissions
App’s permissions to access different directories are summarized as follows:
2.7.6 Application uninstallation
If the App AndroidManifest.xml
declares in: android:hasFragileUserData="true"
When uninstalling the app, you will be prompted whether to retain the App data:
2.7.7 App data migration
On Android Q, App TargetSDK>=Q
the default is Filtered View
. If the App is a Filtered View, it will involve data migration, otherwise the old data will become unusable. You can start data migration from the following aspects:
- App needs to
Legacy View
be downloaded to have full permission to operate storage - App files stored in non-public areas can be accessed by
SAF访问
selecting directory files through SAF, and users can choose to access App files.
- The App can put the files that need to be saved:
Images
,Video
,Audio
into the corresponding public directory. Other files can be placedDownloads
below without deleting the files after uninstalling.
2.7.8 MediaStore Queries
When using MediaStore
to perform query
actions, Projection
when using, Column Name
it must MediaStore
be defined in.
2.8 WRITE_MEDIA_STORAGE permission
2.8.1 Background
WRITE_MEDIA_STORAGE
It is a very powerful permission that allows the App to gain access to all storage devices. Permission to access all storage devices, this should only be given to the Media Stack.
2.8.2 Compatibility impact
In the Android system, it is stipulated that the user group WRITE_MEDIA_STORAGE
can be obtained media_rw
:
- For all removable storage devices, such as T cards and U disks, when they are mounted to Android, ordinary apps only have read permissions and no write permissions. Only media_rw user group apps can write to removable storage devices.
- For Scoped Storage on Android Q, you can use this permission to set the App to run in compatibility mode.
- Android CTS will conduct testing. User-launchable Apps cannot apply for this permission.
For details, please refer to "Android Bootcamp 2019 - Privacy Overview.pdf".
2.8.3 Adaptation
If the App needs to access Media
or 外置存储设备
, you can use MediaStore
or Storage Access Framework(SAF)接口
.
3 Summary
Okay, here’s a summary:
App TargetSDK > 28 即 Android10(Q)及以上
Project, Google has restricted the storage sandbox mode. In theAndroid10(Q)以上的设备
recommended use私有目录data/data
, it can no longer be accessed directly外部SD卡存储目录
. If you need to use it , you need to access it外部SD卡存储目录
through the interface, and it can only be accessed , such as , , , , , , etc. These external SD card storage directories (public directories) can be accessed by all apps, so they are not very secure.SAF
MediaStore
特定的外部SD卡存储目录
Downloads
Documents
Pictures
DCIM
Movies
Music
Ringtones
App TargetSDK <= 28 即 Android10(Q)以下
The project is unrestricted, as long as it hasWRITE_MEDIA_STORAGE
permissionsREAD_MEDIA_STORAGE
. So if you really don’t want to change the external SD storage, then change the project’s targetSdk to <=28.- As for
SAF
theMediaStore
interface access外部SD卡存储特定目录
method, please refer to the above description or refer to the official documentation for details. Here is a demo ofMediaStore
how to use it for your reference外部SD卡存储特定目录
. Don’t forget to click Star.