Clean up system USB storage feature a bit
This commit is contained in:
parent
81d5281a94
commit
d598aac81e
9 changed files with 46 additions and 27 deletions
|
@ -144,6 +144,7 @@ fun <T> permitDiskReads(func: () -> T): T {
|
|||
}
|
||||
|
||||
fun Context.getSystemContext(isUsbStorage: () -> Boolean): Context {
|
||||
return if (checkSelfPermission(INTERACT_ACROSS_USERS_FULL) == PERMISSION_GRANTED
|
||||
&& isUsbStorage()) createContextAsUser(UserHandle.SYSTEM, 0) else this
|
||||
return if (checkSelfPermission(INTERACT_ACROSS_USERS_FULL) == PERMISSION_GRANTED &&
|
||||
isUsbStorage()
|
||||
) createContextAsUser(UserHandle.SYSTEM, 0) else this
|
||||
}
|
||||
|
|
|
@ -21,9 +21,13 @@ internal class DocumentsProviderStoragePlugin(
|
|||
private val storage: DocumentsStorage,
|
||||
) : StoragePlugin {
|
||||
|
||||
private val context: Context get() = appContext.getSystemContext {
|
||||
storage.storage?.isUsb == true
|
||||
}
|
||||
/**
|
||||
* Attention: This context might be from a different user. Use with care.
|
||||
*/
|
||||
private val context: Context
|
||||
get() = appContext.getSystemContext {
|
||||
storage.storage?.isUsb == true
|
||||
}
|
||||
|
||||
private val packageManager: PackageManager = appContext.packageManager
|
||||
|
||||
|
|
|
@ -45,19 +45,21 @@ internal class DocumentsStorage(
|
|||
private val appContext: Context,
|
||||
private val settingsManager: SettingsManager,
|
||||
) {
|
||||
|
||||
private val context: Context get() = appContext.getSystemContext {
|
||||
storage?.isUsb ?: false
|
||||
}
|
||||
|
||||
private val contentResolver: ContentResolver get() = context.contentResolver
|
||||
|
||||
internal var storage: Storage? = null
|
||||
get() {
|
||||
if (field == null) field = settingsManager.getStorage()
|
||||
return field
|
||||
}
|
||||
|
||||
/**
|
||||
* Attention: This context might be from a different user. Use with care.
|
||||
*/
|
||||
private val context: Context
|
||||
get() = appContext.getSystemContext {
|
||||
storage?.isUsb == true
|
||||
}
|
||||
private val contentResolver: ContentResolver get() = context.contentResolver
|
||||
|
||||
internal var rootBackupDir: DocumentFile? = null
|
||||
get() = runBlocking {
|
||||
if (field == null) {
|
||||
|
|
|
@ -5,11 +5,11 @@ import android.hardware.usb.UsbDevice
|
|||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.Uri
|
||||
import android.os.UserHandle
|
||||
import androidx.annotation.UiThread
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.stevesoltys.seedvault.getSystemContext
|
||||
import com.stevesoltys.seedvault.permitDiskReads
|
||||
import com.stevesoltys.seedvault.transport.backup.BackupCoordinator
|
||||
import java.util.concurrent.ConcurrentSkipListSet
|
||||
|
@ -122,8 +122,8 @@ class SettingsManager(private val context: Context) {
|
|||
@WorkerThread
|
||||
fun canDoBackupNow(): Boolean {
|
||||
val storage = getStorage() ?: return false
|
||||
return !storage.isUnavailableUsb(context.createContextAsUser(UserHandle.SYSTEM, 0))
|
||||
&& !storage.isUnavailableNetwork(context)
|
||||
val systemContext = context.getSystemContext { storage.isUsb }
|
||||
return !storage.isUnavailableUsb(systemContext) && !storage.isUnavailableNetwork(context)
|
||||
}
|
||||
|
||||
fun backupApks(): Boolean {
|
||||
|
|
|
@ -13,6 +13,9 @@ internal class SeedvaultStoragePlugin(
|
|||
private val storage: DocumentsStorage,
|
||||
private val keyManager: KeyManager,
|
||||
) : SafStoragePlugin(appContext) {
|
||||
/**
|
||||
* Attention: This context might be from a different user. Use with care.
|
||||
*/
|
||||
override val context: Context
|
||||
get() = appContext.getSystemContext {
|
||||
storage.storage?.isUsb == true
|
||||
|
|
|
@ -18,14 +18,13 @@ import android.provider.DocumentsContract.Root.FLAG_SUPPORTS_CREATE
|
|||
import android.provider.DocumentsContract.Root.FLAG_SUPPORTS_IS_CHILD
|
||||
import android.util.Log
|
||||
import com.stevesoltys.seedvault.R
|
||||
import com.stevesoltys.seedvault.getSystemContext
|
||||
import com.stevesoltys.seedvault.ui.storage.StorageOption.SafOption
|
||||
|
||||
internal object StorageRootResolver {
|
||||
|
||||
private val TAG = StorageRootResolver::class.java.simpleName
|
||||
|
||||
private const val usbAuthority = "com.android.externalstorage.documents"
|
||||
|
||||
fun getStorageRoots(context: Context, authority: String): List<SafOption> {
|
||||
val roots = ArrayList<SafOption>()
|
||||
val rootsUri = DocumentsContract.buildRootsUri(authority)
|
||||
|
@ -37,12 +36,17 @@ internal object StorageRootResolver {
|
|||
if (root != null) roots.add(root)
|
||||
}
|
||||
}
|
||||
if (usbAuthority == authority && UserHandle.myUserId() != UserHandle.USER_SYSTEM) {
|
||||
val c: Context = context.createContextAsUser(UserHandle.SYSTEM, 0)
|
||||
// add special system user roots for USB devices
|
||||
val c = context.getSystemContext {
|
||||
authority == AUTHORITY_STORAGE && UserHandle.myUserId() != UserHandle.USER_SYSTEM
|
||||
}
|
||||
// only proceed if we really got a different [Context], e.g. had permission for it
|
||||
if (context !== c) {
|
||||
c.contentResolver.query(rootsUri, null, null, null, null)?.use { cursor ->
|
||||
while (cursor.moveToNext()) {
|
||||
// Pass in context since it is used to query package manager for app icons
|
||||
// Pass in [context] since it is used to query package manager for app icons
|
||||
val root = getStorageRoot(context, authority, cursor)
|
||||
// only add USB storage from system user, no others
|
||||
if (root != null && root.isUsb) roots.add(root)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ internal class StoragePluginTest : BackupTest() {
|
|||
// get current set dir and for that the current token
|
||||
every { storage getProperty "currentToken" } returns token
|
||||
every { settingsManager.getToken() } returns token
|
||||
every { storage getProperty "storage" } returns null // just to check if isUsb
|
||||
coEvery { storage.getSetDir(token) } returns setDir
|
||||
// delete contents of current set dir
|
||||
coEvery { setDir.listFilesBlocking(context) } returns listOf(backupFile)
|
||||
|
|
|
@ -11,7 +11,7 @@ import javax.crypto.SecretKey
|
|||
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
class TestSafStoragePlugin(
|
||||
private val appContext: Context,
|
||||
appContext: Context,
|
||||
private val getLocationUri: () -> Uri?,
|
||||
) : SafStoragePlugin(appContext) {
|
||||
|
||||
|
|
|
@ -35,12 +35,15 @@ private const val TAG = "SafStoragePlugin"
|
|||
public abstract class SafStoragePlugin(
|
||||
private val appContext: Context,
|
||||
) : StoragePlugin {
|
||||
|
||||
private val cache = SafCache()
|
||||
// In the case of USB storage, if INTERACT_ACROSS_USERS_FULL is granted, this context will match
|
||||
// the system user's application context. Otherwise, matches appContext.
|
||||
/**
|
||||
* Attention: This context could be unexpected. E.g. the system user's application context,
|
||||
* in the case of USB storage, if INTERACT_ACROSS_USERS_FULL permission is granted.
|
||||
* Use [appContext], if you need the context of the current app and user
|
||||
* and [context] for all file access.
|
||||
*/
|
||||
protected abstract val context: Context
|
||||
protected abstract val root: DocumentFile?
|
||||
private val cache = SafCache()
|
||||
|
||||
private val folder: DocumentFile?
|
||||
get() {
|
||||
|
@ -48,8 +51,9 @@ public abstract class SafStoragePlugin(
|
|||
if (cache.currentFolder != null) return cache.currentFolder
|
||||
|
||||
@SuppressLint("HardwareIds")
|
||||
// this is unique to each combination of app-signing key, user, and device
|
||||
// so we don't leak anything by not hashing this and can use it as is
|
||||
// This is unique to each combination of app-signing key, user, and device
|
||||
// so we don't leak anything by not hashing this and can use it as is.
|
||||
// Note: Use [appContext] here to not get the wrong ID for a different user.
|
||||
val androidId = Settings.Secure.getString(appContext.contentResolver, ANDROID_ID)
|
||||
// the folder name is our user ID
|
||||
val folderName = "$androidId.sv"
|
||||
|
|
Loading…
Reference in a new issue