Files
plexamp-lite/tools/patch_bc_dalvik.py
T
xockoandClaude Opus 5.5 58d21b6d68 Minamp 1.3
- Fix NoSuchMethodError on Android 2.x: Dalvik before 4.0 can't link Object
  methods (toString/equals/...) called through an interface. Patch the bundled
  Bouncy Castle (tools/patch_bc_dalvik.py) and our own CertVerifier, and make
  build.sh fail if tools/scan_dalvik.py finds any such call
- Treat a manually entered private address (10.x, 172.16-31.x, 192.168.x,
  .local) as home, so LAN servers that allow access without sign-in work
- Send Plex client headers on media requests (files and transcodes)
- Explain a 503 on a file as the server being unable to open it (drive or
  folder not available)
- Connection log: home/away, token presence, Plex's file check and a direct
  file request in the streaming test

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 20:56:27 +02:00

43 lines
3.0 KiB
Python

"""Rewrites Bouncy Castle calls that Android 2.x's Dalvik can't link.
Calling an Object method (toString/equals/hashCode/getClass) through an *interface* type compiles
to invokeinterface, which Dalvik before 4.0 can't resolve: NoSuchMethodError at runtime. Casting
the receiver to Object makes javac emit invokevirtual instead. tools/scan_dalvik.py checks nothing
was missed.
"""
import sys
FIXES = [
("asn1/ASN1InputStream.java", "asn1Obj.getClass()", "((Object) asn1Obj).getClass()"),
("asn1/ConstructedBitStream.java", "asn1Obj.getClass()", "((Object) asn1Obj).getClass()"),
("asn1/ConstructedOctetStream.java", "asn1Obj.getClass()", "((Object) asn1Obj).getClass()"),
("asn1/x509/AlgorithmIdentifier.java", "return pa.equals(pb);", "return ((Object) pa).equals(pb);"),
("asn1/x509/GeneralName.java", "buf.append(obj.toString());", "buf.append(((Object) obj).toString());"),
("crypto/CryptoServicesPermission.java", "return actions.toString();", "return ((Object) actions).toString();"),
("crypto/digests/SHA256Digest.java", "digest.getClass().getName()", "((Object) digest).getClass().getName()"),
("crypto/engines/AESEngine.java", "params.getClass().getName()", "((Object) params).getClass().getName()"),
("crypto/engines/ARIAEngine.java", "params.getClass().getName()", "((Object) params).getClass().getName()"),
("crypto/engines/DESedeEngine.java", "params.getClass().getName()", "((Object) params).getClass().getName()"),
("crypto/engines/SM4Engine.java", "params.getClass().getName()", "((Object) params).getClass().getName()"),
("crypto/modes/CCMBlockCipher.java", "params.getClass().getName()", "((Object) params).getClass().getName()"),
("math/ec/ECCurve.java", "getField().equals(other.getField())", "((Object) getField()).equals(other.getField())"),
("math/ec/ECCurve.java", "return getField().hashCode()", "return ((Object) getField()).hashCode()"),
("math/field/GenericPolynomialExtensionField.java", "return subfield.equals(other.subfield) && minimalPolynomial.equals(other.minimalPolynomial);",
"return ((Object) subfield).equals(other.subfield) && ((Object) minimalPolynomial).equals(other.minimalPolynomial);"),
("math/field/GenericPolynomialExtensionField.java", "return subfield.hashCode()", "return ((Object) subfield).hashCode()"),
("math/field/GenericPolynomialExtensionField.java", "rotateLeft(minimalPolynomial.hashCode(), 16)", "rotateLeft(((Object) minimalPolynomial).hashCode(), 16)"),
("tls/crypto/impl/AbstractTlsCrypto.java", "secret.getClass().getName()", "((Object) secret).getClass().getName()"),
("tls/crypto/impl/bc/BcTlsSecret.java", "secret.getClass().getName()", "((Object) secret).getClass().getName()"),
]
root = sys.argv[1].rstrip("/") + "/com/minamp/bc/"
for rel, old, new in FIXES:
p = root + rel
s = open(p).read()
if new in s:
continue
if old not in s:
raise SystemExit("pattern not found in %s: %s" % (rel, old))
open(p, "w").write(s.replace(old, new))
print("patched %d call sites" % len(FIXES))