ligi/PassAndroid

Invalid Passbook when trying to import a PDF

chipotledonut opened this issue · 14 comments

I get this error

problem with temp file:java.ioIOExcepction: Stream Closed

ligi commented

Can you provide more details like what version of the app you are using? Also can u try another pdf to see if it a problem with a certain PDF (if so it would be good to have this one)

I see the same error loading a .pkpass file.
App version 3.7.0 from fdroid, Android 11

jejb commented

I see the same thing on 3.7.0 fdroid Android-10

It seems to be coming from the renovation commit 4182154 ("Modernize"). Everything up to this commit builds and runs just fine on Android-10. After this everything fails. I think the problem is the version bumps; they seem to have taken koin to a level that requires at least sdk 31, which is Android-12, so all this could be an artifact of running on
an android version which is now too old.

ligi commented

I needed to increase the targetSDK to solve: #382
but kept the minSDK at 14 - if koin now requires a higher minSDK it should acutally fail the build

jejb commented
ligi commented

Great - appreciate your support there!
Yea - the new one is still considered a beta-version. Bit sad that f-droid does not really have this concept AFAIK.

jejb commented

Well, what I had to do to get 3.7.0 to compile for android-10 really isn't pretty. Unsurprisingly I had to undo most of the dependabot androidx updates.

diff --git a/android/build.gradle b/android/build.gradle
index c177ca28..b62a976c 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -13,15 +13,14 @@ repositories {
 
 android {
 
-    compileSdkVersion 33
+    compileSdkVersion 29
 
     defaultConfig {
         versionCode 370
         versionName "3.7.0"
         minSdkVersion 14
-        targetSdkVersion 33
+        targetSdkVersion 29
         applicationId "org.ligi.passandroid"
-        namespace "org.ligi.passandroid"
         testInstrumentationRunner "org.ligi.passandroid.AppReplacingRunner"
         archivesBaseName = "PassAndroid-$versionName"
         vectorDrawables.useSupportLibrary = true
@@ -120,11 +119,11 @@ android {
 dependencies {
     implementation 'com.github.permissions-dispatcher:permissionsdispatcher-ktx:4.8.0'
 
-    implementation "io.insert-koin:koin-android:3.3.3"
+    implementation "io.insert-koin:koin-android:2.2.3"
 
     implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
     implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
-    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
+    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
 
     androidTestImplementation 'com.github.ligi:trulesk:4.3'
     androidTestImplementation 'com.jraska:falcon:2.2.0'
@@ -150,15 +149,15 @@ dependencies {
     implementation 'com.jakewharton.threetenabp:threetenabp:1.4.6'
     implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
 
-    implementation 'androidx.appcompat:appcompat:1.6.1'
-    implementation 'androidx.preference:preference-ktx:1.2.0'
+    implementation 'androidx.appcompat:appcompat:1.3.1'
+    implementation 'androidx.preference:preference-ktx:1.1.1'
     implementation 'androidx.annotation:annotation:1.2.0'
-    implementation 'androidx.recyclerview:recyclerview:1.3.0'
+    implementation 'androidx.recyclerview:recyclerview:1.1.0'
     implementation 'androidx.cardview:cardview:1.0.0'
-    implementation 'androidx.core:core-ktx:1.10.0'
+    implementation 'androidx.core:core-ktx:1.6.0'
     implementation 'androidx.fragment:fragment-ktx:1.3.2'
-    implementation 'androidx.webkit:webkit:1.6.0'
-    implementation 'com.google.android.material:material:1.8.0'
+    implementation 'androidx.webkit:webkit:1.4.0'
+    implementation 'com.google.android.material:material:1.4.0'
     implementation 'net.i2p.android.ext:floatingactionbutton:1.10.1'
 
     implementation 'com.github.ligi:KAXT:1.0'
@@ -168,9 +167,9 @@ dependencies {
     implementation 'com.github.ligi:tracedroid:4.1'
     implementation 'com.jakewharton.timber:timber:5.0.1'
 
-    forPlayImplementation 'com.github.ligi.snackengage:snackengage-playrate:0.30'
-    forFDroidImplementation 'com.github.ligi.snackengage:snackengage-playrate:0.30'
-    forAmazonImplementation 'com.github.ligi.snackengage:snackengage-amazonrate:0.30'
+    forPlayImplementation 'com.github.ligi.snackengage:snackengage-playrate:0.29'
+    forFDroidImplementation 'com.github.ligi.snackengage:snackengage-playrate:0.29'
+    forAmazonImplementation 'com.github.ligi.snackengage:snackengage-amazonrate:0.29'
 
     // https://medium.com/square-corner-blog/okhttp-3-13-requires-android-5-818bb78d07ce
     // Don't update to >=3.13 before minSDK 21 + Java 8
@@ -182,8 +181,8 @@ dependencies {
 
     implementation 'com.squareup.okio:okio:2.9.0'
 
-    implementation 'com.squareup.moshi:moshi:1.14.0'
-    kapt "com.squareup.moshi:moshi-kotlin-codegen:1.14.0"
+    implementation 'com.squareup.moshi:moshi:1.12.0'
+    kapt "com.squareup.moshi:moshi-kotlin-codegen:1.12.0"
 
     implementation "com.chibatching.kotpref:kotpref:$kotpref_version"
     implementation "com.chibatching.kotpref:initializer:$kotpref_version"
diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml
index a7dc8fa5..e918ee95 100644
--- a/android/src/main/AndroidManifest.xml
+++ b/android/src/main/AndroidManifest.xml
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="utf-8" standalone="no"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           xmlns:tools="http://schemas.android.com/tools"
+          package="org.ligi.passandroid"
           android:installLocation="auto">
 
     <uses-sdk tools:overrideLibrary="com.jakewharton.threetenabp"/>
diff --git a/android/src/main/java/org/ligi/passandroid/App.kt b/android/src/main/java/org/ligi/passandroid/App.kt
index 3c22fe22..0c323b32 100644
--- a/android/src/main/java/org/ligi/passandroid/App.kt
+++ b/android/src/main/java/org/ligi/passandroid/App.kt
@@ -8,6 +8,7 @@ import org.koin.android.ext.koin.androidContext
 import org.koin.android.ext.koin.androidLogger
 import org.koin.core.context.startKoin
 import org.koin.core.module.Module
+import org.koin.core.logger.Level
 import org.koin.dsl.module
 import org.ligi.passandroid.json_adapter.ColorAdapter
 import org.ligi.passandroid.json_adapter.ZonedTimeAdapter
@@ -41,7 +42,7 @@ open class App : Application() {
         super.onCreate()
 
         startKoin {
-            if (BuildConfig.DEBUG) androidLogger()
+            androidLogger(if (BuildConfig.DEBUG) Level.ERROR else Level.NONE)
             androidContext(this@App)
             modules(createKoin())
         }
diff --git a/android/src/main/java/org/ligi/passandroid/scan/SearchPassesIntentService.kt b/android/src/main/java/org/ligi/passandroid/scan/SearchPassesIntentService.kt
index 9e2c6415..d63e7aa8 100644
--- a/android/src/main/java/org/ligi/passandroid/scan/SearchPassesIntentService.kt
+++ b/android/src/main/java/org/ligi/passandroid/scan/SearchPassesIntentService.kt
@@ -64,7 +64,7 @@ class SearchPassesIntentService : LifecycleService() {
                 notifyManager.createNotificationChannel(channel)
             }
 
-            val intentFlags =  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) PendingIntent.FLAG_IMMUTABLE else 0
+            val intentFlags =  0
             val pendingIntent = PendingIntent.getActivity(applicationContext, 1, Intent(baseContext, PassListActivity::class.java), intentFlags)
             progressNotificationBuilder = NotificationCompat.Builder(this@SearchPassesIntentService, NOTIFICATION_CHANNEL_ID).setContentTitle(getString(R.string.scanning_for_passes))
                     .setSmallIcon(R.drawable.ic_refresh)
diff --git a/android/src/main/java/org/ligi/passandroid/scan/SearchSuccessCallback.kt b/android/src/main/java/org/ligi/passandroid/scan/SearchSuccessCallback.kt
index 68b3c0d0..2caa70e7 100644
--- a/android/src/main/java/org/ligi/passandroid/scan/SearchSuccessCallback.kt
+++ b/android/src/main/java/org/ligi/passandroid/scan/SearchSuccessCallback.kt
@@ -47,7 +47,7 @@ internal class SearchSuccessCallback(private val context: Context, private val p
             val intent = Intent(context, PassViewActivity::class.java)
             intent.putExtra(PassViewActivityBase.EXTRA_KEY_UUID, uuid)
 
-            val intentFlags =  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) PendingIntent.FLAG_IMMUTABLE else 0
+            val intentFlags =  0
 
             findNotificationBuilder.setContentIntent(PendingIntent.getActivity(context, SearchPassesIntentService.REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT or intentFlags))
             notifyManager.notify(SearchPassesIntentService.FOUND_NOTIFICATION_ID, findNotificationBuilder.build())
diff --git a/build.gradle b/build.gradle
index e7e87f84..c42b0c57 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,6 @@
 buildscript {
     ext {
-        kotlin_version = '1.8.20'
+        kotlin_version = '1.5.32'
 	// wait for upstream https://github.com/mockito/mockito/issues/2007
         mockito_version = '3.4.6'
         threetenbp_version = '1.6.8'
@@ -16,7 +16,7 @@ buildscript {
 
     dependencies {
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
-        classpath 'com.android.tools.build:gradle:7.4.2'
+        classpath 'com.android.tools.build:gradle:4.1.3'
         classpath 'com.trevjonez.composer:plugin:1.0.0-rc08'
         classpath 'com.github.ben-manes:gradle-versions-plugin:0.46.0'
         classpath 'com.github.bjoernq:unmockplugin:0.7.9'

Which I arrived at by crawling up the dependabot updates one commit at a time. I also found a bug in the PKPass rendering change in e3554ce ("Remove android extensions") which causes a crash; I'll submit a PR for this separately

jejb commented

Now that I have this built, the problem is that the use in the fromContent of InputStreamProvider.kt is getting called prematurely (debug says at the time the fromURI is created). The fix seems to be to shift the use to where it is actually used for a content based URI, like this

diff --git a/android/src/main/java/org/ligi/passandroid/functions/InputStreamProvider.kt b/android/src/main/java/org/ligi/passandroid/functions/InputStreamProvider.kt
index f2e46263..7ff692b8 100644
--- a/android/src/main/java/org/ligi/passandroid/functions/InputStreamProvider.kt
+++ b/android/src/main/java/org/ligi/passandroid/functions/InputStreamProvider.kt
@@ -65,8 +65,6 @@ private fun fromOKHttp(uri: Uri, tracker: Tracker): InputStreamWithSource? {
     return null
 }
 
-private fun fromContent(ctx: Context, uri: Uri) = ctx.contentResolver.openInputStream(uri)?.use {
-    InputStreamWithSource("$uri", it)
-}
+private fun fromContent(ctx: Context, uri: Uri) = InputStreamWithSource("$uri", ctx.contentResolver.openInputStream(uri)!!)
 
 private fun getDefaultInputStreamForUri(uri: Uri) = InputStreamWithSource("$uri", BufferedInputStream(URL("$uri").openStream(), 4096))
diff --git a/android/src/main/java/org/ligi/passandroid/ui/UnzipPassController.kt b/android/src/main/java/org/ligi/passandroid/ui/UnzipPassController.kt
index a0b87040..7afc5afd 100644
--- a/android/src/main/java/org/ligi/passandroid/ui/UnzipPassController.kt
+++ b/android/src/main/java/org/ligi/passandroid/ui/UnzipPassController.kt
@@ -40,10 +40,12 @@ object UnzipPassController : KoinComponent {
 
     fun processInputStream(spec: InputStreamUnzipControllerSpec) {
         try {
-            val tempFile = File.createTempFile("ins", "pass")
-            spec.inputStreamWithSource.inputStream.copyTo(FileOutputStream(tempFile))
-            processFile(FileUnzipControllerSpec(tempFile.absolutePath, spec))
-            tempFile.delete()
+            spec.inputStreamWithSource.inputStream.use {
+                val tempFile = File.createTempFile("ins", "pass")
+                it.copyTo(FileOutputStream(tempFile))
+                processFile(FileUnzipControllerSpec(tempFile.absolutePath, spec))
+                tempFile.delete()
+            }
         } catch (e: Exception) {
             tracker.trackException("problem processing InputStream", e, false)
             spec.failCallback?.fail("problem with temp file: $e")

However, I have no idea why this is suddenly a problem when it wasn't previously (or why it only seems to affect androids 10 and 11)

Late but 3.7.0 from Fdroid Android 11.

Sorry about that.

Bit sad that f-droid does not really have this concept AFAIK.

They do, the app suggests the last stable release if the last one is marked as alpha AFAIK.

Edit: by the way, it would be awesome some kind of backup for all the passbooks. I know I can downgrade to previous version and make it work but I will lose all my history. They are not needed but it's sentimental/historical reasons.

My fault I didn't make a backu of the data of the app with adb BEFORE upgrading. Now if I downgrade to previous version and try to restore my backup it doesn't work. It works with 3.7.0 version

ligi commented

They do, the app suggests the last stable release if the last one is marked as alpha AFAIK.

how to mark as alpha for f-droid?

ligi commented

They do, the app suggests the last stable release if the last one is marked as alpha AFAIK.

how to mark as alpha for f-droid?

spaetz commented

Afaik, it is enough to tag the release as X.X-alpha or x.x-beta to mark sth as unstable. Sorry, slightly offtopic in this issue :-)

FWIW, I am seeing this problem when trying to import a .pkpass file, too - I get "Invalid Passbook" problem with temp file: java.io.IOException: Stream Closed
This is passandroid version 3.7.0 from f-droid on an Android 12 device.

If I force install the older version 3.5.7, then the file imports just fine.