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)