1

I try to print a PDF file and it works fine until I try to print a malformed PDF file. I don't know why the application crashes even though I used try / catch to prevent crashes. I checked and found out that PrintManager.java:1101 throws RuntimeException:

 case MSG_ON_KILL: {
     if (DEBUG) {
         Log.i(LOG_TAG, "onKill()");
     }

     String reason = (String) message.obj;
     throw new RuntimeException(reason);
 }

so code below shouldn't lead to crash:

public static void startPdfPrintProcedure(@NonNull Context context, @NonNull String filePath, @Nullable String jobName) {
    try {
        PrintManager printManager = (PrintManager) context.getSystemService(Context.PRINT_SERVICE);
        String jobName = formatDefaultJobName(context.getResources(), jobName);
        PrintDocumentAdapter pda = new SimplePrintDocumentAdapter(new File(filePath));
        if (printManager != null) {
            try {
                printManager.print(jobName, pda, null); // <- crash here even though there is a try/catch
            } catch (RuntimeException e) {
                showUnknownError();
            }
        } else {
            showUnknownError();
        }
    } catch (RuntimeException e) {
        showUnknownError();
    }
}

Exception that I get after try to print PDF. :

 java.lang.RuntimeException: Cannot print a malformed PDF file
    at android.print.PrintManager$PrintDocumentAdapterDelegate$MyHandler.handleMessage(PrintManager.java:1101)
    at android.os.Handler.dispatchMessage(Handler.java:112)
    at android.os.Looper.loop(Looper.java:216)
    at android.app.ActivityThread.main(ActivityThread.java:7625)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:524)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:987)

Why try/catch code doesn't catch this exception? How can I secure this piece of code from crashing?

Janekxyz
  • 101
  • 1
  • 9
  • 1
    I see no reason these catchs would not cath the exception. Put a `System.out` first thing in both catches, and see if they print something. Or use a debugger and put a break point in the 2 catchs – Bentaye Mar 03 '20 at 14:16
  • 1
    @Bentaye I put logs and neither catches worked. But surprisingly, the logs were displayed before and after `printManager.print(...)` – Janekxyz Mar 03 '20 at 14:31
  • 1
    So that line is not throwing any RuntimeException. How far can you see in your stacktrace? Why do you think that `printManager.print(...)` is the line throwing the exception? – Bentaye Mar 03 '20 at 14:37
  • 2
    @Bentaye you are right. I thought that crash is on `printManager.print(...)` line but I checked and code after `try/catch` block is called. Then print dialog appear and app crashes. – Janekxyz Mar 04 '20 at 09:51

2 Answers2

1

This happens because some "Genius" Google developer has come up with the "Great" idea of throwing an exception in the main thread causing your application to close. I have tried to solve the problem using reflection but the implementation is too closed. Unfortunately, you have to use it assuming the imminent closure of your application in case of incorrect files, unless you want to implement a library to check the PDF format before calling the API. Google never fails, you always have to mess around with its implementation.

The secondary thread ends up calling this handler.

    private final class MyHandler extends Handler {

            public static final int MSG_ON_KILL = 5;
            ...

            @Override
            public void handleMessage(Message message) {
                switch (message.what) {
                    ...
                    
                    case MSG_ON_KILL: {
                        if (DEBUG) {
                            Log.i(LOG_TAG, "onKill()");
                        }

                        String reason = (String) message.obj;
                        throw new RuntimeException(reason);<---------
                    }

                    default: {
                        throw new IllegalArgumentException("Unknown message: "
                                + message.what);
                    }
                }
            }
        }
Younes
  • 41
  • 1
  • 6
0

One way is to check if PDF file is corrupted or not by using PDF viewer library : https://github.com/voghDev/PdfViewPager

import library : implementation 'es.voghdev.pdfviewpager:library:1.1.2'

Use below code to check if PDF file is corrupted

BasePDFPagerAdapter adapter;
        PDFViewPager pdfViewPager;
        pdfViewPager = findViewById(R.id.pdfViewPager);
        adapter = new PDFPagerAdapter(this, path, new PdfErrorHandler() {
            @Override
            public void onPdfError(Throwable t) {
                Log.d("pdfcorrupt",">> yes");
                isPDFCorrupted = true;
            }
        });
        pdfViewPager.setAdapter(adapter);

When PDF file is not valid, onPdfError() method will be called.

If your file is corrupted, simply do not allow to print.

Parsania Hardik
  • 4,593
  • 1
  • 33
  • 33