Disable automatic backups when a removable USB flash drive is used
as storage location. The backup backoff time is not reliable for this as the system still attempts to backup the magic @pm@ package without checking for the backoff value.
This commit is contained in:
parent
2c4d44c5b9
commit
26f23e95fe
8 changed files with 59 additions and 19 deletions
|
@ -19,6 +19,11 @@
|
|||
android:name="android.permission.MANAGE_USB"
|
||||
tools:ignore="ProtectedPermissions" />
|
||||
|
||||
<!-- This is needed to change system backup settings -->
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_SECURE_SETTINGS"
|
||||
tools:ignore="ProtectedPermissions" />
|
||||
|
||||
<application
|
||||
android:name=".Backup"
|
||||
android:allowBackup="false"
|
||||
|
|
|
@ -50,7 +50,7 @@ class RestoreProgressFragment : Fragment() {
|
|||
// success
|
||||
currentPackageView.text = getString(R.string.restore_finished_success)
|
||||
val settingsManager = (requireContext().applicationContext as Backup).settingsManager
|
||||
warningView.text = if (settingsManager.getStorage()?.ejectable == true) {
|
||||
warningView.text = if (settingsManager.getStorage()?.isUsb == true) {
|
||||
getString(R.string.restore_finished_warning_only_installed, getString(R.string.restore_finished_warning_ejectable))
|
||||
} else {
|
||||
getString(R.string.restore_finished_warning_only_installed, null)
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package com.stevesoltys.backup.settings
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.provider.Settings
|
||||
|
||||
private val SETTING = Settings.Secure.BACKUP_MANAGER_CONSTANTS
|
||||
private const val DELIMITER = ','
|
||||
|
||||
private const val KEY_VALUE_BACKUP_INTERVAL_MILLISECONDS = "key_value_backup_interval_milliseconds"
|
||||
private const val FULL_BACKUP_INTERVAL_MILLISECONDS = "full_backup_interval_milliseconds"
|
||||
|
||||
object BackupManagerSettings {
|
||||
|
||||
fun enableAutomaticBackups(resolver: ContentResolver) {
|
||||
// setting this to null will cause the BackupManagerConstants to use default values
|
||||
setSettingValue(resolver, null)
|
||||
}
|
||||
|
||||
fun disableAutomaticBackups(resolver: ContentResolver) {
|
||||
val value = Long.MAX_VALUE
|
||||
val kv = "$KEY_VALUE_BACKUP_INTERVAL_MILLISECONDS=$value"
|
||||
val full = "$FULL_BACKUP_INTERVAL_MILLISECONDS=$value"
|
||||
setSettingValue(resolver, "$kv$DELIMITER$full")
|
||||
}
|
||||
|
||||
private fun setSettingValue(resolver: ContentResolver, value: String?) {
|
||||
Settings.Secure.putString(resolver, SETTING, value)
|
||||
}
|
||||
|
||||
}
|
|
@ -9,7 +9,7 @@ import java.util.*
|
|||
|
||||
private const val PREF_KEY_STORAGE_URI = "storageUri"
|
||||
private const val PREF_KEY_STORAGE_NAME = "storageName"
|
||||
private const val PREF_KEY_STORAGE_EJECTABLE = "storageEjectable"
|
||||
private const val PREF_KEY_STORAGE_IS_USB = "storageIsUsb"
|
||||
|
||||
private const val PREF_KEY_FLASH_DRIVE_NAME = "flashDriveName"
|
||||
private const val PREF_KEY_FLASH_DRIVE_SERIAL_NUMBER = "flashSerialNumber"
|
||||
|
@ -28,7 +28,7 @@ class SettingsManager(context: Context) {
|
|||
prefs.edit()
|
||||
.putString(PREF_KEY_STORAGE_URI, storage.uri.toString())
|
||||
.putString(PREF_KEY_STORAGE_NAME, storage.name)
|
||||
.putBoolean(PREF_KEY_STORAGE_EJECTABLE, storage.ejectable)
|
||||
.putBoolean(PREF_KEY_STORAGE_IS_USB, storage.isUsb)
|
||||
.apply()
|
||||
}
|
||||
|
||||
|
@ -36,8 +36,8 @@ class SettingsManager(context: Context) {
|
|||
val uriStr = prefs.getString(PREF_KEY_STORAGE_URI, null) ?: return null
|
||||
val uri = Uri.parse(uriStr)
|
||||
val name = prefs.getString(PREF_KEY_STORAGE_NAME, null) ?: throw IllegalStateException()
|
||||
val ejectable = prefs.getBoolean(PREF_KEY_STORAGE_EJECTABLE, false)
|
||||
return Storage(uri, name, ejectable)
|
||||
val isUsb = prefs.getBoolean(PREF_KEY_STORAGE_IS_USB, false)
|
||||
return Storage(uri, name, isUsb)
|
||||
}
|
||||
|
||||
fun setFlashDrive(usb: FlashDrive?) {
|
||||
|
@ -86,9 +86,9 @@ class SettingsManager(context: Context) {
|
|||
/**
|
||||
* Sets the last backup time to "now".
|
||||
*/
|
||||
fun saveNewBackupTime() {
|
||||
fun saveNewBackupTime(millis: Long = Date().time) {
|
||||
prefs.edit()
|
||||
.putLong(PREF_KEY_BACKUP_TIME, Date().time)
|
||||
.putLong(PREF_KEY_BACKUP_TIME, millis)
|
||||
.apply()
|
||||
}
|
||||
|
||||
|
@ -118,7 +118,7 @@ class SettingsManager(context: Context) {
|
|||
data class Storage(
|
||||
val uri: Uri,
|
||||
val name: String,
|
||||
val ejectable: Boolean) {
|
||||
val isUsb: Boolean) {
|
||||
fun getDocumentFile(context: Context) = DocumentFile.fromTreeUri(context, uri)
|
||||
?: throw AssertionError("Should only happen on API < 21.")
|
||||
}
|
||||
|
|
|
@ -199,7 +199,7 @@ class BackupCoordinator(
|
|||
// back off if there's no storage set
|
||||
val storage = settingsManager.getStorage() ?: return defaultBackoff
|
||||
// don't back off if storage is not ejectable or available right now
|
||||
return if (!storage.ejectable || storage.getDocumentFile(context).isDirectory) noBackoff
|
||||
return if (!storage.isUsb || storage.getDocumentFile(context).isDirectory) noBackoff
|
||||
// otherwise back off
|
||||
else defaultBackoff
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ data class StorageRoot(
|
|||
internal val title: String,
|
||||
internal val summary: String?,
|
||||
internal val availableBytes: Long?,
|
||||
internal val supportsEject: Boolean,
|
||||
internal val isUsb: Boolean,
|
||||
internal val enabled: Boolean = true) {
|
||||
|
||||
internal val uri: Uri by lazy {
|
||||
|
@ -41,7 +41,7 @@ data class StorageRoot(
|
|||
}
|
||||
|
||||
fun isInternal(): Boolean {
|
||||
return authority == AUTHORITY_STORAGE && !supportsEject
|
||||
return authority == AUTHORITY_STORAGE && !isUsb
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,7 +122,6 @@ internal class StorageRootFetcher(private val context: Context) {
|
|||
if (!supportsCreate || !supportsIsChild) return null
|
||||
val rootId = cursor.getString(COLUMN_ROOT_ID)!!
|
||||
if (authority == AUTHORITY_STORAGE && rootId == ROOT_ID_HOME) return null
|
||||
val supportsEject = flags and FLAG_SUPPORTS_EJECT != 0
|
||||
return StorageRoot(
|
||||
authority = authority,
|
||||
rootId = rootId,
|
||||
|
@ -131,13 +130,13 @@ internal class StorageRootFetcher(private val context: Context) {
|
|||
title = cursor.getString(COLUMN_TITLE)!!,
|
||||
summary = cursor.getString(COLUMN_SUMMARY),
|
||||
availableBytes = cursor.getLong(COLUMN_AVAILABLE_BYTES),
|
||||
supportsEject = supportsEject
|
||||
isUsb = flags and FLAG_REMOVABLE_USB != 0
|
||||
)
|
||||
}
|
||||
|
||||
private fun checkOrAddUsbRoot(roots: ArrayList<StorageRoot>) {
|
||||
for (root in roots) {
|
||||
if (root.authority == AUTHORITY_STORAGE && root.supportsEject) return
|
||||
if (root.authority == AUTHORITY_STORAGE && root.isUsb) return
|
||||
}
|
||||
val root = StorageRoot(
|
||||
authority = AUTHORITY_STORAGE,
|
||||
|
@ -147,7 +146,7 @@ internal class StorageRootFetcher(private val context: Context) {
|
|||
title = context.getString(R.string.storage_fake_drive_title),
|
||||
summary = context.getString(R.string.storage_fake_drive_summary),
|
||||
availableBytes = null,
|
||||
supportsEject = true,
|
||||
isUsb = true,
|
||||
enabled = false
|
||||
)
|
||||
roots.add(root)
|
||||
|
|
|
@ -15,6 +15,7 @@ import androidx.lifecycle.MutableLiveData
|
|||
import com.stevesoltys.backup.Backup
|
||||
import com.stevesoltys.backup.R
|
||||
import com.stevesoltys.backup.isMassStorage
|
||||
import com.stevesoltys.backup.settings.BackupManagerSettings
|
||||
import com.stevesoltys.backup.settings.FlashDrive
|
||||
import com.stevesoltys.backup.settings.Storage
|
||||
import com.stevesoltys.backup.transport.ConfigurableBackupTransportService
|
||||
|
@ -45,7 +46,7 @@ internal abstract class StorageViewModel(private val app: Application) : Android
|
|||
internal fun validLocationIsSet(context: Context): Boolean {
|
||||
val settingsManager = (context.applicationContext as Backup).settingsManager
|
||||
val storage = settingsManager.getStorage() ?: return false
|
||||
if (storage.ejectable) return true
|
||||
if (storage.isUsb) return true
|
||||
return storage.getDocumentFile(context).isDirectory
|
||||
}
|
||||
}
|
||||
|
@ -88,16 +89,20 @@ internal abstract class StorageViewModel(private val app: Application) : Android
|
|||
} else {
|
||||
root.title
|
||||
}
|
||||
val storage = Storage(uri, name, root.supportsEject)
|
||||
val storage = Storage(uri, name, root.isUsb)
|
||||
settingsManager.setStorage(storage)
|
||||
|
||||
// reset time of last backup to "Never"
|
||||
settingsManager.resetBackupTime()
|
||||
|
||||
if (storage.ejectable) {
|
||||
if (storage.isUsb) {
|
||||
val wasSaved = saveUsbDevice()
|
||||
// reset stored flash drive, if we did not update it
|
||||
if (!wasSaved) settingsManager.setFlashDrive(null)
|
||||
BackupManagerSettings.disableAutomaticBackups(app.contentResolver)
|
||||
} else {
|
||||
settingsManager.setFlashDrive(null)
|
||||
BackupManagerSettings.enableAutomaticBackups(app.contentResolver)
|
||||
}
|
||||
|
||||
// stop backup service to be sure the old location will get updated
|
||||
|
@ -114,7 +119,7 @@ internal abstract class StorageViewModel(private val app: Application) : Android
|
|||
return true
|
||||
}
|
||||
}
|
||||
Log.w(TAG, "No USB device found for ejectable storage.")
|
||||
Log.e(TAG, "No USB device found even though we were expecting one.")
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -3,5 +3,6 @@
|
|||
<privapp-permissions package="com.stevesoltys.backup">
|
||||
<permission name="android.permission.BACKUP"/>
|
||||
<permission name="android.permission.MANAGE_USB"/>
|
||||
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
|
||||
</privapp-permissions>
|
||||
</permissions>
|
Loading…
Reference in a new issue