1

I try to implement my own printer (means print server) based on the Android PrintServer API. This print server provides a static single printer which tasks is to upload the print job to a web page via http upload.

I am using Marshmallow on a Sony Z2. And for testing I use the email application and "printing" from the submenu.

In principle I managed all the business logic already and it works, so at least I used the right approach. But when I press the "print" button (with the printer logo) in the Android print dialog to fast, the whole print server crashes. If I wait until the dialog rendered all the preview and then press the button, everything is fine.

I dont know if the problem is clear therefore I made a small movie (1,8MB), which you can find here:

https://c.geniusbytes.com/index.php/s/leJrSGRlxBSSCux

To reduce the problem I removed all the transfer code and arrived at the most minimal version which still causes the crash, see below.

Can somebody see what is the problem? On my mobile there is also the PDF-writer PrintServer (Sony!?). This does not crash with the same procedure. Instead the "print" behavior, which is the selection of the destination to store the PDF, waits until the preview is rendered and then pops up.

It is important to mention that one need a larger document to print. Otherwise the preview is rendered to fast for causing the crash by pressing the print button.

Here is the code for the PrintServer implementation:

public class UploadPrintService extends PrintService {


    private static final String LOG_TAG = "UploadPrintService";


    ////////////////////////////////////////////////
    //
    // service methods
    //
    /////////////////////////////////////////////////


    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    @Override
    protected void onConnected() {
        Log.i(LOG_TAG, "#onConnected()");
    }

    @Override
    protected void onDisconnected() {
        Log.i(LOG_TAG, "#onDisconnected()");
    }


    @Override
    protected PrinterDiscoverySession onCreatePrinterDiscoverySession() {
        Log.i(LOG_TAG, "#onCreatePrinterDiscoverySession()");
        return new UploadPrintDiscoverySession(this);
    }

    @Override
    protected void onRequestCancelPrintJob(PrintJob printJob) {

        // standard cancel
        if ((printJob.isStarted() && (!printJob.isCancelled()))) {
            printJob.cancel();
        }
    }


    @Override
    protected void onPrintJobQueued(final PrintJob printJob) {
        Log.i(LOG_TAG, "#onPrintJobQueued");
        printJob.start();
    }


}

And the PrinterDiscoverySession is implemented in a way, such that it always returns the same printer. See here:

public class UploadPrintDiscoverySession extends PrinterDiscoverySession {

    private static final String LOG_TAG = UploadPrintDiscoverySession.class.getSimpleName();


    private PrintService printService = null;

    private static final String PRINTER_ID = "my.uploadprinter.id";

    public UploadPrintDiscoverySession(PrintService printService) {
        super();
        Log.i(LOG_TAG, "new discovery session!");
        this.printService = printService;
    }

    @Override
    public void onStartPrinterDiscovery(List<PrinterId> list) {
        Log.i(LOG_TAG, "#onStartPrinterDiscovery");
        addUploadPrinter();
    }

    @Override
    public void onStopPrinterDiscovery() {
        Log.i(LOG_TAG, "#onStopPrinterDiscovery");

    }

    @Override
    public void onValidatePrinters(List<PrinterId> list) {
        Log.i(LOG_TAG, "#onValidatePrinters");
        addUploadPrinter();
    }

    @Override
    public void onStartPrinterStateTracking(PrinterId printerId) {
        Log.i(LOG_TAG, "#onStartPrinterStateTracking");
        addUploadPrinter();
    }

    @Override
    public void onStopPrinterStateTracking(PrinterId printerId) {
        Log.i(LOG_TAG, "#onStopPrinterStateTracking");
    }

    @Override
    public void onDestroy() {
        Log.i(LOG_TAG, "#onDestroy");
    }


    private void addUploadPrinter() {

        Log.i(LOG_TAG, "#addUploadPrinter");

        PrinterId uploadPrinterId = printService.generatePrinterId(PRINTER_ID);

        PrinterInfo.Builder printerInfoBuilder = new PrinterInfo.Builder(uploadPrinterId,
                "Mighty Printer", PrinterInfo.STATUS_IDLE);

        PrinterCapabilitiesInfo.Builder capBuilder =
                new PrinterCapabilitiesInfo.Builder(uploadPrinterId);
        capBuilder.addMediaSize(PrintAttributes.MediaSize.ISO_A4, true);
        capBuilder.addMediaSize(PrintAttributes.MediaSize.ISO_A3, false);
        capBuilder.addResolution(new PrintAttributes.Resolution(
                "Default", "default resolution", 600, 600), true);
        capBuilder.setColorModes(PrintAttributes.COLOR_MODE_COLOR
                        | PrintAttributes.COLOR_MODE_MONOCHROME,
                PrintAttributes.COLOR_MODE_COLOR);
        printerInfoBuilder.setCapabilities(capBuilder.build());

        Log.i(LOG_TAG, "add *the* printer to the list...");

        List<PrinterInfo> discoveredPrinterList = new ArrayList<PrinterInfo>();
        discoveredPrinterList.add(printerInfoBuilder.build());

        this.addPrinters(discoveredPrinterList);
    }

}

Thanks for the Logcat-hint, I was already wondering why I see no error but when I disabled the filter which was activated by default, then I see the following error:

09-05 20:06:05.523 27568-27568/? E/AndroidRuntime: FATAL EXCEPTION: main
                                                   Process: com.android.printspooler, PID: 27568
                                                   java.lang.IllegalArgumentException: end cannot be less than zero.
                                                       at android.print.PageRange.<init>(PageRange.java:50)
                                                       at com.android.printspooler.util.PageRangeUtils.asAbsoluteRange(PageRangeUtils.java:199)
                                                       at com.android.printspooler.ui.PrintActivity$DocumentTransformer.computePagesToShred(PrintActivity.java:2620)
                                                       at com.android.printspooler.ui.PrintActivity$DocumentTransformer.<init>(PrintActivity.java:2496)
                                                       at com.android.printspooler.ui.PrintActivity.transformDocumentAndFinish(PrintActivity.java:1733)
                                                       at com.android.printspooler.ui.PrintActivity.requestCreatePdfFileOrFinish(PrintActivity.java:967)
                                                       at com.android.printspooler.ui.PrintActivity.onUpdateCompleted(PrintActivity.java:492)
                                                       at com.android.printspooler.model.RemotePrintDocument.notifyUpdateCompleted(RemotePrintDocument.java:385)
                                                       at com.android.printspooler.model.RemotePrintDocument.access$900(RemotePrintDocument.java:55)
                                                       at com.android.printspooler.model.RemotePrintDocument$1.onDone(RemotePrintDocument.java:117)
                                                       at com.android.printspooler.model.RemotePrintDocument$WriteCommand.handleOnWriteFinished(RemotePrintDocument.java:1000)
                                                       at com.android.printspooler.model.RemotePrintDocument$WriteCommand.access$2200(RemotePrintDocument.java:867)
                                                       at com.android.printspooler.model.RemotePrintDocument$WriteCommand$WriteHandler.handleMessage(RemotePrintDocument.java:1061)
                                                       at android.os.Handler.dispatchMessage(Handler.java:102)
                                                       at android.os.Looper.loop(Looper.java:234)
                                                       at android.app.ActivityThread.main(ActivityThread.java:5526)
                                                       at java.lang.reflect.Method.invoke(Native Method)
                                                       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                                                       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
0

And some lines later there I found:

09-05 20:06:06.956 1005-3292/? I/WindowState: WIN DEATH: Window{ff8499c u0 com.android.printspooler/com.android.printspooler.ui.PrintActivity}
09-05 20:06:06.956 1005-3325/? W/UserState: Not stopping printer state tracking - session destroyed
09-05 20:06:06.956 1005-3924/? D/GraphicsStats: Buffer count: 7
09-05 20:06:06.957 1005-1005/? D/RemotePrintSpooler: Error clearing print spooler client
                                                     android.os.DeadObjectException
                                                         at android.os.BinderProxy.transactNative(Native Method)
                                                         at android.os.BinderProxy.transact(Binder.java:503)
                                                         at android.print.IPrintSpooler$Stub$Proxy.setClient(IPrintSpooler.java:358)
                                                         at com.android.server.print.RemotePrintSpooler.clearClientLocked(RemotePrintSpooler.java:424)
                                                         at com.android.server.print.RemotePrintSpooler.access$400(RemotePrintSpooler.java:53)
                                                         at com.android.server.print.RemotePrintSpooler$MyServiceConnection.onServiceDisconnected(RemotePrintSpooler.java:456)
                                                         at android.app.LoadedApk$ServiceDispatcher.doDeath(LoadedApk.java:1232)
                                                         at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1246)
                                                         at android.os.Handler.handleCallback(Handler.java:739)
                                                         at android.os.Handler.dispatchMessage(Handler.java:95)
                                                         at android.os.Looper.loop(Looper.java:234)
                                                         at com.android.server.SystemServer.run(SystemServer.java:302)
                                                         at com.android.server.SystemServer.main(SystemServer.java:174)
                                                         at java.lang.reflect.Method.invoke(Native Method)
                                                         at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                                                         at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
09-05 20:06:06.958 26358-26358/com.geniusbytes.uploadprintservice I/UploadPrintDiscoverySession: #onStopPrinterStateTracking
09-05 20:06:06.958 26358-26358/com.geniusbytes.uploadprintservice I/UploadPrintDiscoverySession: #onStopPrinterDiscovery
09-05 20:06:06.958 26358-26358/com.geniusbytes.uploadprintservice I/UploadPrintDiscoverySession: #onDestroy
09-05 20:06:06.958 26358-26358/com.geniusbytes.uploadprintservice I/UploadPrintService: #onDisconnected()
09-05 20:06:07.006 1005-3886/? I/ActivityManager: Process com.android.printspooler (pid 27568) has died
09-05 20:06:07.006 1005-3886/? W/ActivityManager: Scheduling restart of crashed service com.android.printspooler/.model.PrintSpoolerService in 1000ms
09-05 20:06:07.188 1005-1237/? W/AppOps: Finishing op nesting under-run: uid 1000 pkg android code 24 time=1462541465446 duration=2311 nesting=0
09-05 20:06:08.024 1005-1185/? I/ActivityManager: Start proc 5263:com.android.printspooler/u0a159 for service com.android.printspooler/.model.PrintSpoolerService
09-05 20:06:09.953 391-3149/? D/audio_hw_primary: out_standby: enter: stream (0xb5bf7240) usecase(0: deep-buffer-playback)
Huebi
  • 41
  • 6
  • Use LogCat to examine the Java stack trace associated with your crash: https://stackoverflow.com/questions/23353173/unfortunately-myapp-has-stopped-how-can-i-solve-this – CommonsWare Sep 05 '16 at 15:34
  • Ok, I added the logcat information. Seems like by pressing the print button, I start to print an "unready" document with page range -1 or so. So I think I have to prevent that the print is triggered while the preview is rendered. If that is correct, how!? – Huebi Sep 05 '16 at 18:18
  • I have never implemented your side of the printing system, only clients. However, your `onPrintJobQueued()` implementation seems odd: "if it's already started, start it" doesn't seem quite right. Other samples, [like this one](https://stackoverflow.com/questions/34270556/how-do-i-specify-and-add-a-custom-printer-in-an-android-app), don't do that. Either it is `isQueued()` rather than `isStarted()`, or they just call `start()`. This wouldn't feel like the source of your difficulty, but it caught my eye, so I figured I'd mention it. – CommonsWare Sep 05 '16 at 18:27
  • mhh jap right, was one of my trouble shooting trials, and is just a bug. In the original version I just had printJob.start() – Huebi Sep 06 '16 at 06:38
  • Although the if statement was wrong (I removed it now) the behavior is still the same. – Huebi Sep 06 '16 at 06:47
  • I also found out, that when I press the button in the above described way, that the "#onPrintJobQueued" comment did not appear in the log, so seems that the system is not in the state to call the method. when I wait until rendering is ready and press the button, the log entry appears and the service has the correct beahvior incl. not crashing. – Huebi Sep 06 '16 at 06:52
  • Looks similar to: http://stackoverflow.com/questions/39391630/print-spooler-has-stopped-in-printmanager – Nonos Feb 14 '17 at 17:43
  • This seems like an Android/PrintSpooler bug so I would suggest you file it as such .. On your PrintService end, it doesn't look like there is much you can do except to delay the availability of the printer (which will delay the print button from showing) but that's not a good user experience in the general case where the content was already rendered. – Nonos Feb 14 '17 at 17:49

0 Answers0