-
Notifications
You must be signed in to change notification settings - Fork 114
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Clean package manager caches on boot
Some devices seem to have an issue where (presumably) resources from an old version are being used with new code. This causes BCR to crash with an error about the app theme not being derived from Theme.AppCompat. This commit works around the issue in a brute force way by deleting BCR's dalvik cache and package manager cache entry on every boot. Fixes: #275, #303, #307 Signed-off-by: Andrew Gunnerson <[email protected]>
- Loading branch information
1 parent
2c75213
commit 6357df4
Showing
6 changed files
with
174 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# source "${0%/*}/boot_common.sh" <log file> | ||
|
||
exec >"${1}" 2>&1 | ||
|
||
mod_dir=${0%/*} | ||
|
||
header() { | ||
echo "----- ${*} -----" | ||
} | ||
|
||
module_prop() { | ||
grep "^${1}=" "${mod_dir}/module.prop" | cut -d= -f2 | ||
} | ||
|
||
run_cli_apk() { | ||
CLASSPATH="${cli_apk}" app_process / "${@}" & | ||
pid=${!} | ||
wait "${pid}" | ||
echo "Exit status: ${?}" | ||
echo "Logcat:" | ||
logcat -d --pid "${pid}" | ||
} | ||
|
||
app_id=$(module_prop id) | ||
app_version=$(module_prop version) | ||
cli_apk=$(echo "${mod_dir}"/system/priv-app/"${app_id}"/app-*.apk) | ||
|
||
header Environment | ||
echo "Timestamp: $(date)" | ||
echo "Script: ${0}" | ||
echo "App ID: ${app_id}" | ||
echo "App version: ${app_version}" | ||
echo "CLI APK: ${cli_apk}" | ||
echo "UID/GID/Context: $(id)" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# On some devices, upgrading BCR seems to cause some old state to unexpectedly | ||
# linger around, causing BCR to crash with an obscure error about the theme not | ||
# being derived from Theme.AppCompat. | ||
|
||
source "${0%/*}/boot_common.sh" /data/local/tmp/bcr_clear_package_manager_caches.log | ||
|
||
header Clear package manager caches | ||
run_cli_apk com.chiller3.bcr.standalone.ClearPackageManagerCachesKt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
127 changes: 127 additions & 0 deletions
127
app/src/main/java/com/chiller3/bcr/standalone/ClearPackageManagerCaches.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
@file:Suppress("SameParameterValue") | ||
|
||
package com.chiller3.bcr.standalone | ||
|
||
import com.chiller3.bcr.BuildConfig | ||
import java.nio.file.Path | ||
import java.nio.file.Paths | ||
import kotlin.io.path.ExperimentalPathApi | ||
import kotlin.io.path.deleteIfExists | ||
import kotlin.io.path.isRegularFile | ||
import kotlin.io.path.name | ||
import kotlin.io.path.readBytes | ||
import kotlin.io.path.walk | ||
import kotlin.system.exitProcess | ||
|
||
private val DALVIK_CACHE_DIR = Paths.get("/data/dalvik-cache") | ||
private val PACKAGE_CACHE_DIR = Paths.get("/data/system/package_cache") | ||
|
||
private var dryRun = false | ||
|
||
private fun delete(path: Path) { | ||
if (dryRun) { | ||
println("Would have deleted: $path") | ||
} else { | ||
println("Deleting: $path") | ||
path.deleteIfExists() | ||
} | ||
} | ||
|
||
private fun ByteArray.indexOfSubarray(needle: ByteArray, start: Int = 0): Int { | ||
require(start >= 0) { "start must be non-negative" } | ||
|
||
if (needle.isEmpty()) { | ||
return 0 | ||
} | ||
|
||
outer@ for (i in 0 until size - needle.size + 1) { | ||
for (j in needle.indices) { | ||
if (this[i + j] != needle[j]) { | ||
continue@outer | ||
} | ||
} | ||
return i | ||
} | ||
|
||
return -1 | ||
} | ||
|
||
@OptIn(ExperimentalPathApi::class) | ||
private fun clearDalvikCache(appId: String): Boolean { | ||
var ret = true | ||
|
||
for (path in DALVIK_CACHE_DIR.walk()) { | ||
if (!path.isRegularFile()) { | ||
continue | ||
} | ||
|
||
val pieces = path.name.split('@') | ||
val appIdIndex = pieces.indexOf(appId) | ||
if (appIdIndex < 0 || appIdIndex == pieces.size - 1) { | ||
continue | ||
} | ||
|
||
val nextPiece = pieces[appIdIndex + 1] | ||
if (nextPiece.startsWith("app-") && nextPiece.endsWith(".apk")) { | ||
try { | ||
delete(path) | ||
} catch (e: Exception) { | ||
e.printStackTrace() | ||
ret = false | ||
} | ||
} | ||
} | ||
|
||
return ret | ||
} | ||
|
||
@OptIn(ExperimentalPathApi::class) | ||
private fun clearPackageManagerCache(appId: String): Boolean { | ||
// The current implementation of the package cache uses PackageImpl.writeToParcel(), which | ||
// serializes the cache entry to the file as a Parcel. The current Parcel implementation stores | ||
// string values as null-terminated little-endian UTF-16. One of the string values stored is | ||
// manifestPackageName, which we can match on. | ||
// | ||
// This is a unique enough search that there should never be a false positive, but even if there | ||
// is, the package manager will just repopulate the cache. | ||
val needle = "\u0000$appId\u0000".toByteArray(Charsets.UTF_16LE) | ||
var ret = true | ||
|
||
for (path in PACKAGE_CACHE_DIR.walk()) { | ||
if (!path.isRegularFile()) { | ||
continue | ||
} | ||
|
||
try { | ||
// Not the most efficient, but these are tiny files that Android is later going to read | ||
// entirely into memory anyway | ||
if (path.readBytes().indexOfSubarray(needle) >= 0) { | ||
delete(path) | ||
} | ||
} catch (e: Exception) { | ||
e.printStackTrace() | ||
ret = false | ||
} | ||
} | ||
|
||
return ret | ||
} | ||
|
||
private fun mainInternal() { | ||
clearDalvikCache(BuildConfig.APPLICATION_ID) | ||
clearPackageManagerCache(BuildConfig.APPLICATION_ID) | ||
} | ||
|
||
fun main(args: Array<String>) { | ||
if ("--dry-run" in args) { | ||
dryRun = true | ||
} | ||
|
||
try { | ||
mainInternal() | ||
} catch (e: Exception) { | ||
System.err.println("Failed to clear caches") | ||
e.printStackTrace() | ||
exitProcess(1) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters