diff --git a/watchface/README.md b/watchface/README.md index 3cb14ac3..cf9bbc14 100644 --- a/watchface/README.md +++ b/watchface/README.md @@ -21,10 +21,4 @@ example, using `pngquant` on all images, to help keep the watch face size to a m To package the watch face, the [Pack](https://github.com/google/pack) is used. This is a native library, so the pre-builts are provided in `jniLibs`. A script is also included for building these -fresh, but that should not be necessary. - -## Signing the APK - -For the purposes of this project, a key is generated at runtime and used to sign the APK. This is -not the approach to take in production, but the watch face APK must be signed, and it doesn't so -much matter what key is used to do it. \ No newline at end of file +fresh, but that should not be necessary. \ No newline at end of file diff --git a/watchface/build.gradle.kts b/watchface/build.gradle.kts index ff1dd2dd..ad85d46e 100644 --- a/watchface/build.gradle.kts +++ b/watchface/build.gradle.kts @@ -30,8 +30,11 @@ android { minSdk = libs.versions.minSdk.get().toInt() targetSdk = 36 testInstrumentationRunner = "com.android.developers.testing.AndroidifyTestRunner" + consumerProguardFiles("proguard-rules.pro") + } + buildFeatures { + buildConfig = true } - compileOptions { sourceCompatibility = JavaVersion.toVersion(libs.versions.javaVersion.get()) targetCompatibility = JavaVersion.toVersion(libs.versions.javaVersion.get()) diff --git a/watchface/pack-java/Cargo.lock b/watchface/pack-java/Cargo.lock index f8f0a212..6d7a794c 100644 --- a/watchface/pack-java/Cargo.lock +++ b/watchface/pack-java/Cargo.lock @@ -609,6 +609,7 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "pack-aab" version = "0.1.0" +source = "git+https://github.com/google/pack.git#dc7132121bd674a1b424d72fc487b8498d00352d" dependencies = [ "deku", "pack-asset-compiler", @@ -622,6 +623,7 @@ dependencies = [ [[package]] name = "pack-api" version = "0.1.0" +source = "git+https://github.com/google/pack.git#dc7132121bd674a1b424d72fc487b8498d00352d" dependencies = [ "deku", "pack-aab", @@ -634,6 +636,7 @@ dependencies = [ [[package]] name = "pack-asset-compiler" version = "0.1.0" +source = "git+https://github.com/google/pack.git#dc7132121bd674a1b424d72fc487b8498d00352d" dependencies = [ "deku", "pack-common", @@ -644,6 +647,7 @@ dependencies = [ [[package]] name = "pack-common" version = "0.1.0" +source = "git+https://github.com/google/pack.git#dc7132121bd674a1b424d72fc487b8498d00352d" dependencies = [ "deku", "pem", @@ -667,6 +671,7 @@ dependencies = [ [[package]] name = "pack-sign" version = "0.1.0" +source = "git+https://github.com/google/pack.git#dc7132121bd674a1b424d72fc487b8498d00352d" dependencies = [ "base64", "byteorder", @@ -684,6 +689,7 @@ dependencies = [ [[package]] name = "pack-zip" version = "0.1.0" +source = "git+https://github.com/google/pack.git#dc7132121bd674a1b424d72fc487b8498d00352d" dependencies = [ "pack-common", "zip", @@ -1538,9 +1544,9 @@ checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[package]] name = "zip" -version = "4.5.0" +version = "5.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835eb39822904d39cb19465de1159e05d371973f0c6df3a365ad50565ddc8b9" +checksum = "2f852905151ac8d4d06fdca66520a661c09730a74c6d4e2b0f27b436b382e532" dependencies = [ "arbitrary", "crc32fast", diff --git a/watchface/proguard-rules.pro b/watchface/proguard-rules.pro index 3328ea7a..10581e3c 100644 --- a/watchface/proguard-rules.pro +++ b/watchface/proguard-rules.pro @@ -6,4 +6,29 @@ -dontwarn org.eclipse.wst.xml.xpath2.processor.** # Ignore missing Java SE annotation processing classes, often from libraries like AutoValue/JavaPoet --dontwarn javax.lang.model.** \ No newline at end of file +-dontwarn javax.lang.model.** + +-keep class com.android.developers.androidify.watchface.creator.PackPackage { + native ; +} + +-keep class com.android.developers.androidify.watchface.creator.PackPackage$Resource { *; } + +# Keep all classes in the BouncyCastle provider, as they are loaded via reflection +-keep class org.bouncycastle.** { *; } +-keep interface org.bouncycastle.** { *; } + +# Keep the APK Signer library +-keep class com.android.apksig.** { *; } +-keep interface com.android.apksig.** { *; } + +# Keep Apache Xerces XML parser +-keep class org.apache.xerces.** { *; } + +## Keep standard Java XML (JAXP), DOM, and SAX interfaces and classes +-keep interface org.w3c.dom.** { *; } +-keep class org.w3c.dom.** { *; } +-keep interface org.xml.sax.** { *; } +-keep class org.xml.sax.** { *; } +-keep class javax.xml.** { *; } +-keep interface javax.xml.** { *; } \ No newline at end of file diff --git a/watchface/src/main/java/com/android/developers/androidify/watchface/creator/PackPackage.kt b/watchface/src/main/java/com/android/developers/androidify/watchface/creator/PackPackage.kt index 93c5a941..b17b7abe 100644 --- a/watchface/src/main/java/com/android/developers/androidify/watchface/creator/PackPackage.kt +++ b/watchface/src/main/java/com/android/developers/androidify/watchface/creator/PackPackage.kt @@ -24,9 +24,9 @@ data class PackPackage( var resources: MutableList = mutableListOf() data class Resource( - val subdirectory: String, - val name: String, - val contentsBase64: String, + @JvmField val subdirectory: String, + @JvmField val name: String, + @JvmField val contentsBase64: String, ) { companion object { fun fromBase64Contents( diff --git a/watchface/src/main/java/com/android/developers/androidify/watchface/creator/Signer.kt b/watchface/src/main/java/com/android/developers/androidify/watchface/creator/Signer.kt index c1e3b348..ed4a2168 100644 --- a/watchface/src/main/java/com/android/developers/androidify/watchface/creator/Signer.kt +++ b/watchface/src/main/java/com/android/developers/androidify/watchface/creator/Signer.kt @@ -23,6 +23,7 @@ import com.android.apksig.util.DataSink import com.android.apksig.util.DataSource import com.android.apksig.util.DataSources import com.android.apksig.util.ReadableDataSink +import com.android.developers.androidify.watchface.BuildConfig import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder @@ -38,8 +39,10 @@ import java.security.cert.X509Certificate import java.util.Calendar import java.util.Date -private const val KEY_ALIAS = "com.android.developers.androidify.ApkSigningKey" -private const val CERT_ALIAS = "com.android.developers.androidify.Cert" +private val keyAlias : String + get() = "com.android.developers.androidify.ApkSigningKey-" + BuildConfig.BUILD_TYPE +private val certAlias: String + get() = "com.android.developers.androidify.Cert-" + BuildConfig.BUILD_TYPE private const val ANDROID_KEYSTORE = "AndroidKeyStore" /** @@ -71,13 +74,13 @@ fun signApk(unsignedApk: ByteArray): ByteArray { private fun getOrCreateSigningKeyPair(): KeyPair { val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE).apply { load(null) } - if (keyStore.containsAlias(KEY_ALIAS)) { - val entry = keyStore.getEntry(KEY_ALIAS, null) as KeyStore.PrivateKeyEntry + if (keyStore.containsAlias(keyAlias)) { + val entry = keyStore.getEntry(keyAlias, null) as KeyStore.PrivateKeyEntry return KeyPair(entry.certificate.publicKey, entry.privateKey) } val parameterSpec = KeyGenParameterSpec.Builder( - KEY_ALIAS, + keyAlias, KeyProperties.PURPOSE_SIGN, ).run { setDigests(KeyProperties.DIGEST_SHA256) @@ -151,7 +154,7 @@ private fun storeCertificate(certificate: X509Certificate): Boolean { val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE).apply { load(null) } - keyStore.setCertificateEntry(CERT_ALIAS, certificate) + keyStore.setCertificateEntry(certAlias, certificate) true } catch (e: Exception) { // Log the exception for debugging @@ -169,7 +172,7 @@ private fun getOrCreateCertificate(keyPair: KeyPair): X509Certificate { val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE).apply { load(null) } - return (keyStore.getCertificate(CERT_ALIAS) ?: createCertificate(keyPair)) as X509Certificate + return (keyStore.getCertificate(certAlias) ?: createCertificate(keyPair)) as X509Certificate } /**