[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: ad7d1aef49 -s ours

am skip reason: subject contains skip directive

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/services/BuiltInPrintService/+/22980528

Change-Id: Ic14a4c40e4aa74763f9502724ade42c05669624f
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/OWNERS b/OWNERS
index e2a1aec..dc1919c 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1 +1,2 @@
-svetoslavganov@google.com
+# Bug component: 47273
+include platform/frameworks/base:/OWNERS
diff --git a/jni/include/ifc_print_job.h b/jni/include/ifc_print_job.h
index 0c0fc2a..c64a807 100644
--- a/jni/include/ifc_print_job.h
+++ b/jni/include/ifc_print_job.h
@@ -34,14 +34,14 @@
     /*
      * Validates job and connection params, updating parameters as necessary.
      */
-    status_t (*validate_job)(const struct ifc_print_job_st *this_p,
-            wprint_job_params_t *job_params);
+    status_t (*validate_job)(const struct ifc_print_job_st *this_p, wprint_job_params_t *job_params,
+            const printer_capabilities_t *printer_caps);
 
     /*
      * Start a print job with given params
      */
     status_t (*start_job)(const struct ifc_print_job_st *this_p,
-            const wprint_job_params_t *job_params);
+            const wprint_job_params_t *job_params, const printer_capabilities_t *printer_caps);
 
     /*
      * Sends data to the ip address set on initialization, returning the amount of data
diff --git a/jni/include/lib_wprint.h b/jni/include/lib_wprint.h
index b60e539..6c1d26e 100755
--- a/jni/include/lib_wprint.h
+++ b/jni/include/lib_wprint.h
@@ -134,14 +134,16 @@
     color_space_t color_space;
     media_tray_t media_tray;
     unsigned int num_copies;
+    unsigned int job_pages_per_set;
     bool borderless;
     unsigned int render_flags;
     float job_top_margin;
     float job_left_margin;
     float job_right_margin;
     float job_bottom_margin;
+    bool preserve_scaling;
 
-    bool renderInReverseOrder;
+    bool face_down_tray;
 
     // these values are pixels
     unsigned int print_top_margin;
@@ -190,6 +192,7 @@
     const char *useragent;
     char docCategory[10];
     const char *media_default;
+    char print_scaling[MAX_PRINT_SCALING_LENGTH];
 
     // Expected certificate if any
     uint8 *certificate;
@@ -221,7 +224,8 @@
     /* Timeout per retry in milliseconds */
     long timeout;
     /* Return non-0 if the received certificate is not acceptable. */
-    int (*validate_certificate)(struct wprint_connect_info_st *connect_info, uint8 *data, int data_len);
+    int (*validate_certificate)(struct wprint_connect_info_st *connect_info,
+                                uint8 *data, int data_len);
     /* User-supplied data. */
     void *user;
 };
@@ -249,7 +253,6 @@
    int                 current_page;
    int                 total_pages;
    int                 page_total_update;
-
 } wprint_page_info_t;
 
 typedef struct {
@@ -290,8 +293,8 @@
     status_t (*print_page)(wprint_job_params_t *job_params, const char *mime_type,
             const char *pathname);
 
-    status_t (*print_blank_page)(wJob_t job_handle,
-            wprint_job_params_t *job_params);
+    status_t (*print_blank_page)(wJob_t job_handle, wprint_job_params_t *job_params,
+            const char *mime_type, const char *pathname);
 
     status_t (*end_job)(wprint_job_params_t *job_params);
 } wprint_plugin_t;
@@ -314,6 +317,12 @@
         printer_capabilities_t *printer_cap);
 
 /*
+ * Returns a preferred print format supported by the printer
+ */
+char *_get_print_format(const char *mime_type, const wprint_job_params_t *job_params,
+                        const printer_capabilities_t *cap);
+
+/*
  * Fills in the job params structure with default values.
  */
 status_t wprintGetDefaultJobParams(wprint_job_params_t *job_params);
@@ -367,6 +376,18 @@
  */
 void wprintSetSourceInfo(const char *appName, const char *appVersion, const char *osName);
 
+/*
+ * Returns true, if a blank page to be printed in duplex print for PCLm
+ */
+bool wprintBlankPageForPclm(const wprint_job_params_t *job_params,
+        const printer_capabilities_t *printer_cap);
+
+/*
+ * Returns true, if a blank page to be printed in duplex print for PWG
+ */
+bool wprintBlankPageForPwg(const wprint_job_params_t *job_params,
+        const printer_capabilities_t *printer_cap);
+
 /* Global variables to hold API, application, and OS details */
 extern int g_API_version;
 extern char g_osName[MAX_ID_STRING_LENGTH + 1];
diff --git a/jni/include/printer_capabilities_types.h b/jni/include/printer_capabilities_types.h
index 90cb581..6313a31 100644
--- a/jni/include/printer_capabilities_types.h
+++ b/jni/include/printer_capabilities_types.h
@@ -26,6 +26,8 @@
 #define MAX_URI_LENGTH 1024
 #define MAX_STRING 256
 #define MAX_UUID 46
+#define MAX_PRINT_SCALING_LENGTH    32
+#define MAX_PRINT_SCALING_COUNT     10
 
 #include "wprint_df_types.h"
 
@@ -44,6 +46,7 @@
  */
 typedef struct {
     unsigned char duplex;
+    unsigned char sidesSupported;
     unsigned char borderless;
     unsigned char canPrintPDF;
     unsigned char canPrintPCLm;
@@ -91,6 +94,10 @@
     unsigned char docSourceAppVersion;
     unsigned char docSourceOsName;
     unsigned char docSourceOsVersion;
+    char print_scalings_supported[MAX_PRINT_SCALING_COUNT][MAX_PRINT_SCALING_LENGTH];
+    int print_scalings_supported_count;
+    char print_scaling_default[MAX_PRINT_SCALING_LENGTH]; /* Printer default value */
+    unsigned char jobPagesPerSetSupported;
 } printer_capabilities_t;
 
 #endif // __PRINTER_CAPABILITIES_TYPES_H__
\ No newline at end of file
diff --git a/jni/include/wprint_status_types.h b/jni/include/wprint_status_types.h
index 83758b7..b1449bc 100644
--- a/jni/include/wprint_status_types.h
+++ b/jni/include/wprint_status_types.h
@@ -41,7 +41,6 @@
 #define BLOCKED_REASON_CANCELLED         (1 << PRINT_STATUS_CANCELLED)
 #define BLOCKED_REASON_PRINT_STATUS_VERY_LOW_ON_INK (1 << PRINT_STATUS_VERY_LOW_ON_INK)
 #define BLOCKED_REASON_PARTIAL_CANCEL    (1 << PRINT_STATUS_PARTIAL_CANCEL)
-#define BLOCKED_REASON_BAD_CERTIFICATE   (1 << PRINT_STATUS_BAD_CERTIFICATE)
 #define BLOCKED_REASON_PAUSED            (LONG_ONE << PRINT_STATUS_PAUSED)
 #define BLOCKED_REASON_STOPPED		     (LONG_ONE << PRINT_STATUS_STOPPED)
 #define BLOCKED_REASON_INPUT_CANNOT_FEED_SIZE_SELECTED  \
@@ -153,7 +152,6 @@
 
     PRINT_STATUS_VERY_LOW_ON_INK,
     PRINT_STATUS_PARTIAL_CANCEL,
-    PRINT_STATUS_BAD_CERTIFICATE,
 
     PRINT_STATUS_INPUT_CANNOT_FEED_SIZE_SELECTED,
     PRINT_STATUS_INTERLOCK_ERROR,
diff --git a/jni/include/wtypes.h b/jni/include/wtypes.h
index 764d6b0..29ed6b0 100644
--- a/jni/include/wtypes.h
+++ b/jni/include/wtypes.h
@@ -36,6 +36,9 @@
 
     /* Request failed because corrupt data was detected */
     CORRUPT = -3,
+
+    /* Request failed because unexpected ssl certificate received */
+    BAD_CERTIFICATE = -4
 } status_t;
 
 #define ARRAY_SIZE(X) (sizeof(X)/sizeof(X[0]))
diff --git a/jni/ipphelper/ipp_print.c b/jni/ipphelper/ipp_print.c
index 785fd9a..a95b2db 100644
--- a/jni/ipphelper/ipp_print.c
+++ b/jni/ipphelper/ipp_print.c
@@ -28,9 +28,11 @@
 static status_t _init(const ifc_print_job_t *this_p, const char *printer_address, int port,
         const char *printer_uri, bool use_secure_uri);
 
-static status_t _validate_job(const ifc_print_job_t *this_p, wprint_job_params_t *job_params);
+static status_t _validate_job(const ifc_print_job_t *this_p, wprint_job_params_t *job_params,
+        const printer_capabilities_t *printer_caps);
 
-static status_t _start_job(const ifc_print_job_t *this_p, const wprint_job_params_t *job_params);
+static status_t _start_job(const ifc_print_job_t *this_p, const wprint_job_params_t *job_params,
+        const printer_capabilities_t *printer_caps);
 
 static int _send_data(const ifc_print_job_t *this_p, const char *buffer, size_t length);
 
@@ -170,7 +172,8 @@
 /*
  * Fills and returns an ipp request object with the given job parameters
  */
-static ipp_t *_fill_job(int ipp_op, char *printer_uri, const wprint_job_params_t *job_params) {
+static ipp_t *_fill_job(int ipp_op, char *printer_uri, const wprint_job_params_t *job_params,
+        const printer_capabilities_t *printer_caps) {
     LOGD("_fill_job: Enter");
     ipp_t *request = NULL; // IPP request object
     ipp_attribute_t *attrptr; // Attribute pointer
@@ -291,11 +294,29 @@
         }
     }
 
+    // Add print-scaling attribute to the request
+    if (strlen(job_params->print_scaling) != 0) {
+        LOGD("_fill_job: setting print-scaling to %s", job_params->print_scaling);
+        ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "print-scaling",
+                     NULL, job_params->print_scaling);
+    }
+
     // Add copies support if required and allowed
     if (job_params->copies_supported && (strcmp(job_params->print_format, PRINT_FORMAT_PDF) == 0)) {
         ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies", job_params->num_copies);
     }
 
+    if (printer_caps->jobPagesPerSetSupported && job_params->job_pages_per_set > 0) {
+        unsigned int job_pages_per_set = job_params->job_pages_per_set;
+        if (strcmp(job_params->print_format, PRINT_FORMAT_PCLM) == 0
+            && wprintBlankPageForPclm(job_params, printer_caps)) {
+            job_pages_per_set++;
+            LOGD("_fill_job: incremented job_pages_per_set: %d", job_pages_per_set);
+        }
+        ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-pages-per-set",
+                job_pages_per_set);
+    }
+
     // Add print quality if requested
     if (job_params->print_quality) {
         ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
@@ -304,15 +325,18 @@
 
     ippAddResolution(request, IPP_TAG_JOB, "printer-resolution", IPP_RES_PER_INCH,
             job_params->pixel_units, job_params->pixel_units);
-    if (job_params->duplex == DUPLEX_MODE_BOOK) {
-        ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, IPP_SIDES_TAG, NULL,
-                IPP_SIDES_TWO_SIDED_LONG_EDGE);
-    } else if (job_params->duplex == DUPLEX_MODE_TABLET) {
-        ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, IPP_SIDES_TAG, NULL,
-                IPP_SIDES_TWO_SIDED_SHORT_EDGE);
-    } else {
-        ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, IPP_SIDES_TAG, NULL,
-                IPP_SIDES_ONE_SIDED);
+
+    if (printer_caps->sidesSupported) {
+        if (job_params->duplex == DUPLEX_MODE_BOOK) {
+            ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, IPP_SIDES_TAG, NULL,
+                    IPP_SIDES_TWO_SIDED_LONG_EDGE);
+        } else if (job_params->duplex == DUPLEX_MODE_TABLET) {
+            ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, IPP_SIDES_TAG, NULL,
+                    IPP_SIDES_TWO_SIDED_SHORT_EDGE);
+        } else {
+            ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, IPP_SIDES_TAG, NULL,
+                    IPP_SIDES_ONE_SIDED);
+        }
     }
 
     if (job_params->color_space == COLOR_SPACE_MONO) {
@@ -409,7 +433,8 @@
     return request;
 }
 
-static status_t  _validate_job(const ifc_print_job_t *this_p, wprint_job_params_t *job_params) {
+static status_t _validate_job(const ifc_print_job_t *this_p, wprint_job_params_t *job_params,
+        const printer_capabilities_t *printer_caps) {
     LOGD("_validate_job: Enter");
     status_t result = ERROR;
     ipp_print_job_t *ipp_job;
@@ -437,7 +462,7 @@
             ipp_job->useragent = job_params->useragent;
         }
 
-        request = _fill_job(IPP_VALIDATE_JOB, ipp_job->printer_uri, job_params);
+        request = _fill_job(IPP_VALIDATE_JOB, ipp_job->printer_uri, job_params, printer_caps);
 
         if (ipp_job->useragent != NULL) {
             httpSetDefaultField(ipp_job->http, HTTP_FIELD_USER_AGENT, ipp_job->useragent);
@@ -477,7 +502,8 @@
     return result;
 }
 
-static status_t _start_job(const ifc_print_job_t *this_p, const wprint_job_params_t *job_params) {
+static status_t _start_job(const ifc_print_job_t *this_p, const wprint_job_params_t *job_params,
+        const printer_capabilities_t *printer_caps) {
     LOGD("_start_job: Enter");
     status_t result;
     ipp_print_job_t *ipp_job;
@@ -499,7 +525,7 @@
         if ((job_params->useragent != NULL) && (strlen(job_params->useragent) > 0)) {
             ipp_job->useragent = job_params->useragent;
         }
-        request = _fill_job(IPP_PRINT_JOB, ipp_job->printer_uri, job_params);
+        request = _fill_job(IPP_PRINT_JOB, ipp_job->printer_uri, job_params, printer_caps);
 
         if (request == NULL) {
             continue;
diff --git a/jni/ipphelper/ipphelper.c b/jni/ipphelper/ipphelper.c
index 702fcd6..2da3628 100644
--- a/jni/ipphelper/ipphelper.c
+++ b/jni/ipphelper/ipphelper.c
@@ -572,7 +572,6 @@
               job_state_dyn_t *job_state_dyn,
               ipp_jstate_t *job_state,
               const char *requesting_user) {
-
     LOGD("get_JobStatus(): Enter");
     static const char *const jattrs[] =
             {            /* Job attributes we want */
@@ -989,14 +988,16 @@
 }
 
 static void addRollSupportedSizes(
-        unsigned int width,
+        unsigned int minWidth,
+        unsigned int maxWidth,
         unsigned int minHeight,
         unsigned int maxHeight,
         media_supported_t *media_supported,
         int *sizesIdx) {
     // If a supported media size fits on the roll size, add it to the list
     for (int i = 0; i < SUPPORTED_MEDIA_SIZE_COUNT; i++) {
-        if(SupportedMediaSizes[i].WidthInMicrometers / 10 <= width
+        if (SupportedMediaSizes[i].WidthInMicrometers / 10 >= minWidth
+            && SupportedMediaSizes[i].WidthInMicrometers / 10 <= maxWidth
             && SupportedMediaSizes[i].HeightInMicrometers / 10 >= minHeight
             && SupportedMediaSizes[i].HeightInMicrometers / 10 <= maxHeight) {
             addMediaIfNotDuplicate(i, sizesIdx, media_supported, SupportedMediaSizes[i].media_size);
@@ -1010,6 +1011,7 @@
         printer_capabilities_t *capabilities) {
     int i;
     int sizes_idx = 0;
+    bool roll_supported = false;
     LOGD(" Entered getMediaSupported");
 
     media_size_t media_sizeTemp;
@@ -1017,7 +1019,7 @@
 
     // Check for media-col-ready first
     ipp_attribute_t *attrptr;
-    if((attrptr =
+    if ((attrptr =
         ippFindAttribute(response, "media-col-ready", IPP_TAG_BEGIN_COLLECTION)) != NULL) {
         LOGD("media-col-ready found");
         for (i = 0; i < ippGetCount(attrptr); i++) {
@@ -1025,6 +1027,7 @@
             ipp_attribute_t *attrptr2;
             media_ready_set_t mediaReadySet = {};
             int minHeight = 0, maxHeight = 0;
+            int minWidth = 0, maxWidth = 0;
             for (attrptr2 = ippFirstAttribute(collection);
                  (attrptr2 != NULL);
                  attrptr2 = ippNextAttribute(collection)) {
@@ -1035,7 +1038,13 @@
                          (attrptr3 != NULL);
                          attrptr3 = ippNextAttribute(collection_sec)) {
                         if (strcmp("x-dimension", ippGetName(attrptr3)) == 0) {
-                            mediaReadySet.x_dimension = ippGetInteger(attrptr3, 0);
+                            if (ippGetValueTag(attrptr3) == IPP_TAG_RANGE) {
+                                minWidth = ippGetRange(attrptr3, 0, &maxWidth);
+                                mediaReadySet.x_dimension = minWidth;
+                            } else if (ippGetValueTag(attrptr3) == IPP_TAG_INTEGER) {
+                                maxWidth = ippGetInteger(attrptr3, 0);
+                                mediaReadySet.x_dimension = maxWidth;
+                            }
                         } else if (strcmp("y-dimension", ippGetName(attrptr3)) == 0) {
                             if (ippGetValueTag(attrptr3) == IPP_TAG_RANGE) {
                                 minHeight = ippGetRange(attrptr3, 0, &maxHeight);
@@ -1053,8 +1062,9 @@
             }
             if (minHeight > 0 && maxHeight > 0
                 && strstr(mediaReadySet.media_tray_tag, "roll") != NULL) {
+                roll_supported = true;
                 // If the source is a roll, add supported sizes that would fit on the roll
-                addRollSupportedSizes(mediaReadySet.x_dimension, minHeight, maxHeight,
+                addRollSupportedSizes(minWidth, maxWidth, minHeight, maxHeight,
                                       media_supported, &sizes_idx);
             } else {
                 // Get the media size name from x and y dimensions
@@ -1100,6 +1110,31 @@
             }
         }
     }
+    // Get common media which is supported by media-col-ready(roll) and media-supported
+    if (roll_supported) {
+        int supported_by_roll_and_media = 0;
+        for (int j = 0; j <= PAGE_STATUS_MAX - 1 &&
+                        media_supported->media_size[j] != 0; j++) {
+            for (int k = j + 1; k <= PAGE_STATUS_MAX - 1 &&
+                                media_supported->media_size[k] != 0; k++) {
+                if (media_supported->media_size[j] != 0 &&
+                    media_supported->media_size[j] == media_supported->media_size[k]) {
+                    media_supported->media_size[supported_by_roll_and_media] =
+                            media_supported->media_size[j];
+                    media_supported->idxKeywordTranTable[supported_by_roll_and_media] =
+                            media_supported->idxKeywordTranTable[j];
+                    supported_by_roll_and_media += 1;
+                    break;
+                }
+            }
+        }
+
+        for (int j = supported_by_roll_and_media; j <= PAGE_STATUS_MAX - 1 &&
+                                                  media_supported->media_size[j] != 0; j++) {
+            media_supported->media_size[j] = 0;
+            media_supported->idxKeywordTranTable[j] = -1;
+        }
+    }
     if (sizes_idx == 0) {
         LOGD("No supported media found");
     }
@@ -1158,6 +1193,7 @@
     media_supported_t media_supported;
     for (i = 0; i <= PAGE_STATUS_MAX - 1; i++) {
         media_supported.media_size[i] = 0;
+        media_supported.idxKeywordTranTable[i] = -1;
     }
     parse_getMediaSupported(response, &media_supported, capabilities);
 
@@ -1167,7 +1203,7 @@
     int idx = 0;
     capabilities->numSupportedMediaTypes = 0;
     for (i = 0; i <= PAGE_STATUS_MAX - 1; i++) {
-        if (media_supported.media_size[i] != 0) {
+        if (media_supported.media_size[i] != 0 && media_supported.idxKeywordTranTable[i] >= 0) {
             capabilities->supportedMediaSizes[capabilities->numSupportedMediaSizes++] =
                     media_supported.media_size[i];
             idx = media_supported.idxKeywordTranTable[i];
@@ -1266,6 +1302,7 @@
     }
 
     if ((attrptr = ippFindAttribute(response, "sides-supported", IPP_TAG_KEYWORD)) != NULL) {
+        capabilities->sidesSupported = 1;
         for (i = 0; i < ippGetCount(attrptr); i++) {
             if (strcmp(IPP_SIDES_TWO_SIDED_SHORT_EDGE, ippGetString(attrptr, i, NULL)) == 0) {
                 capabilities->duplex = 1;
@@ -1529,6 +1566,32 @@
             }
         }
     }
+
+    // Determine types of print-scaling supported
+    capabilities->print_scalings_supported_count = 0;
+    if ((attrptr = ippFindAttribute(response, "print-scaling-supported", IPP_TAG_KEYWORD)) != NULL) {
+        for (i = 0; i < ippGetCount(attrptr) && i < MAX_PRINT_SCALING_COUNT; i++) {
+            capabilities->print_scalings_supported_count++;
+            strlcpy(capabilities->print_scalings_supported[i], ippGetString(attrptr, i, NULL),
+                    sizeof(capabilities->print_scalings_supported[i]));
+        }
+    } else {
+        LOGD("print-scaling-supported not found");
+    }
+
+    memset(capabilities->print_scaling_default, '\0', sizeof(capabilities->print_scaling_default));
+    if ((attrptr = ippFindAttribute(response, "print-scaling-default", IPP_TAG_KEYWORD)) != NULL) {
+        strlcpy(capabilities->print_scaling_default, ippGetString(attrptr, 0, NULL),
+                sizeof(capabilities->print_scaling_default));
+    } else {
+        LOGD("print-scaling-default not found");
+    }
+
+    if ((attrptr = ippFindAttribute(response, "job-pages-per-set-supported",
+            IPP_TAG_BOOLEAN)) != NULL && ippGetBoolean(attrptr, 0)) {
+        capabilities->jobPagesPerSetSupported = 1;
+    }
+
     debuglist_printerCapabilities(capabilities);
 }
 
@@ -1587,6 +1650,11 @@
     LOGD("ippVersionMinor: %d", capabilities->ippVersionMinor);
     LOGD("strip height: %d", capabilities->stripHeight);
     LOGD("faceDownTray: %d", capabilities->faceDownTray);
+    for (int i = 0; i < capabilities->print_scalings_supported_count; i++) {
+        LOGD("print-scaling-supported (%d): %s", i, capabilities->print_scalings_supported[i]);
+    }
+    LOGD("print_scaling_default: %s",capabilities->print_scaling_default);
+    LOGD("jobPagesPerSetSupported: %d", capabilities->jobPagesPerSetSupported);
 }
 
 void debuglist_printerStatus(printer_state_dyn_t *printer_state_dyn) {
diff --git a/jni/ipphelper/ippstatus_capabilities.c b/jni/ipphelper/ippstatus_capabilities.c
index 6bd821f..48fa143 100644
--- a/jni/ipphelper/ippstatus_capabilities.c
+++ b/jni/ipphelper/ippstatus_capabilities.c
@@ -68,7 +68,10 @@
         "pwg-raster-document-sheet-back",
         "document-format-details-supported",
         "media-ready",
-        "media-col-ready"
+        "media-col-ready",
+        "print-scaling-supported",
+        "print-scaling-default",
+        "job-pages-per-set-supported"
 };
 
 static void _init(const ifc_printer_capabilities_t *this_p,
diff --git a/jni/lib/lib_wprint.c b/jni/lib/lib_wprint.c
index ebdec13..ac39a81 100755
--- a/jni/lib/lib_wprint.c
+++ b/jni/lib/lib_wprint.c
@@ -103,7 +103,7 @@
     JOB_STATE_COMPLETED, // print job completed successfully, waiting to be freed
     JOB_STATE_ERROR, // job could not be run due to error
     JOB_STATE_CORRUPTED, // job could not be run due to error
-
+    JOB_STATE_BAD_CERTIFICATE, // server gave an unexpected ssl certificate
     NUM_JOB_STATES
 } _job_state_t;
 
@@ -197,6 +197,10 @@
 
 static _io_plugin_t _io_plugins[2];
 
+static volatile bool stop_run = false;
+
+static printer_capabilities_t g_printer_caps = {0};
+
 char g_osName[MAX_ID_STRING_LENGTH + 1] = {0};
 char g_appName[MAX_ID_STRING_LENGTH + 1] = {0};
 char g_appVersion[MAX_ID_STRING_LENGTH + 1] = {0};
@@ -883,6 +887,7 @@
     int i;
     status_t job_result;
     int corrupted = 0;
+    printer_capabilities_t printer_caps;
 
     while (OK == msgQReceive(_msgQ, (char *) &msg, sizeof(msg), WAIT_FOREVER)) {
         if (msg.id == MSG_RUN_JOB) {
@@ -926,8 +931,9 @@
             if ((jq->status_ifc != NULL) && (jq->status_ifc->get_status != NULL)) {
                 int retry = 0;
                 bool idle = false;
+                bool bad_certificate = false;
                 printer_state_dyn_t printer_state;
-                while (!idle) {
+                while (!idle && !stop_run) {
                     print_status_t status;
                     jq->status_ifc->get_status(jq->status_ifc, &printer_state);
                     status = printer_state.printer_status & ~PRINTER_IDLE_BIT;
@@ -953,7 +959,7 @@
                             jq->blocked_reasons = BLOCKED_REASON_UNABLE_TO_CONNECT;
                         } else {
                             LOGD("%s: Bad certificate", __func__);
-                            jq->blocked_reasons = BLOCKED_REASON_BAD_CERTIFICATE;
+                            bad_certificate = true;
                         }
                     } else if (printer_state.printer_status & PRINTER_IDLE_BIT) {
                         LOGD("%s: printer blocked but appears to be in an idle state. "
@@ -996,9 +1002,16 @@
 
                 if (jq->job_params.cancelled) {
                     job_result = CANCELLED;
+                } else if (bad_certificate) {
+                    job_result = BAD_CERTIFICATE;
                 } else {
-                    job_result = (((printer_state.printer_status & ~PRINTER_IDLE_BIT) ==
-                            PRINT_STATUS_IDLE) ? OK : ERROR);
+                    if (stop_run) {
+                        jq->job_state = JOB_STATE_ERROR;
+                        job_result = ERROR;
+                    } else {
+                        job_result = (((printer_state.printer_status & ~PRINTER_IDLE_BIT) ==
+                                       PRINT_STATUS_IDLE) ? OK : ERROR);
+                    }
                 }
             }
 
@@ -1027,14 +1040,20 @@
                 jq->cb_fn(job_handle, (void *) &cb_param);
             }
 
+            memcpy(&printer_caps, &g_printer_caps, sizeof(printer_capabilities_t));
+
             jq->job_params.page_num = -1;
             if (job_result == OK) {
                 if (jq->print_ifc != NULL) {
                     LOGD("_job_thread: Calling validate_job");
                     if (jq->print_ifc->validate_job != NULL) {
-                        job_result = jq->print_ifc->validate_job(jq->print_ifc, &jq->job_params);
+                        job_result = jq->print_ifc->validate_job(jq->print_ifc, &jq->job_params,
+                                &printer_caps);
                     }
-
+                    if (!_is_certificate_allowed(jq)) {
+                        LOGD("_job_thread: bad certificate found at validate job");
+                        job_result = BAD_CERTIFICATE;
+                    }
                     /* PDF format plugin's start_job and end_job are to be called for each copy,
                      * inside the for-loop for num_copies.
                      */
@@ -1042,7 +1061,7 @@
                     // Do not call start_job unless validate_job returned OK
                     if ((job_result == OK) && (jq->print_ifc->start_job != NULL) &&
                             (strcmp(jq->job_params.print_format, PRINT_FORMAT_PDF) != 0)) {
-                        jq->print_ifc->start_job(jq->print_ifc, &jq->job_params);
+                        jq->print_ifc->start_job(jq->print_ifc, &jq->job_params, &printer_caps);
                     }
                 }
 
@@ -1072,7 +1091,7 @@
                     bool pdf_printed = false;
                     if (jq->print_ifc->start_job != NULL &&
                             (strcmp(jq->job_params.print_format, PRINT_FORMAT_PDF) == 0)) {
-                        jq->print_ifc->start_job(jq->print_ifc, &jq->job_params);
+                        jq->print_ifc->start_job(jq->print_ifc, &jq->job_params, &printer_caps);
                     }
 
                     per_copy_page_num = 0;
@@ -1107,6 +1126,13 @@
                             jq->job_params.last_page = false;
                         }
 
+                        bool printBlankPage = (strcmp(jq->job_params.print_format,
+                                PRINT_FORMAT_PCLM) == 0) ? wprintBlankPageForPclm(
+                                &jq->job_params, &printer_caps) : wprintBlankPageForPwg(
+                                &jq->job_params, &printer_caps);
+
+                        printBlankPage &= (jq->plugin->print_blank_page != NULL);
+
                         if (strlen(page.filename) > 0) {
                             per_copy_page_num++;
                             {
@@ -1126,7 +1152,7 @@
 
                             jq->job_params.copy_num = (i + 1);
                             jq->job_params.copy_page_num = page.page_num;
-                            jq->job_params.page_backside = !(per_copy_page_num & 0x1);
+                            jq->job_params.page_backside = !(page.page_num & 0x1);
                             jq->job_params.page_corrupted = (page.corrupted ? 1 : 0);
                             jq->job_params.page_printing = true;
                             _unlock();
@@ -1134,6 +1160,14 @@
                             if (!page.corrupted) {
                                 LOGD("_job_thread(): page not corrupt, calling plugin's print_page"
                                         " function for page #%d", page.page_num);
+
+                                // make sure we always print an even number of pages in duplex jobs
+                                if ((page.page_num == jq->job_params.job_pages_per_set) &&
+                                        !(jq->job_params.face_down_tray) && printBlankPage) {
+                                    jq->plugin->print_blank_page(job_handle, &(jq->job_params),
+                                            jq->mime_type, page.filename);
+                                }
+
                                 if (strcmp(jq->job_params.print_format, PRINT_FORMAT_PDF) != 0) {
                                     job_result = jq->plugin->print_page(&(jq->job_params),
                                             jq->mime_type,
@@ -1152,7 +1186,8 @@
                                 job_result = CORRUPT;
                                 if ((jq->job_params.duplex != DUPLEX_MODE_NONE) &&
                                         (jq->plugin->print_blank_page != NULL)) {
-                                    jq->plugin->print_blank_page(job_handle, &(jq->job_params));
+                                    jq->plugin->print_blank_page(job_handle, &(jq->job_params),
+                                            jq->mime_type, page.filename);
                                 }
                             }
                             _lock();
@@ -1171,11 +1206,11 @@
                         }
 
                         // make sure we always print an even number of pages in duplex jobs
-                        if (page.last_page && (jq->job_params.duplex != DUPLEX_MODE_NONE)
-                                && !(jq->job_params.page_backside)
-                                && (jq->plugin->print_blank_page != NULL)) {
+                        if (page.last_page && (jq->job_params.face_down_tray) &&
+                                !(jq->job_params.page_backside) && printBlankPage) {
                             _unlock();
-                            jq->plugin->print_blank_page(job_handle, &(jq->job_params));
+                            jq->plugin->print_blank_page(job_handle, &(jq->job_params),
+                                    jq->mime_type, page.filename);
                             _lock();
                         }
 
@@ -1252,7 +1287,7 @@
                     if ((jq->job_params.duplex != DUPLEX_MODE_NONE)
                             && (jq->plugin->print_blank_page != NULL)) {
                         jq->plugin->print_blank_page(job_handle,
-                                &(jq->job_params));
+                                &(jq->job_params), jq->mime_type, page.filename);
                     }
 
                     _lock();
@@ -1265,9 +1300,9 @@
             // if we started the job end it
             if (jq->job_params.page_num >= 0) {
                 // if the job was cancelled without sending anything through, print a blank sheet
-                if ((jq->job_params.page_num == 0)
-                        && (jq->plugin->print_blank_page != NULL)) {
-                    jq->plugin->print_blank_page(job_handle, &(jq->job_params));
+                if ((jq->job_params.page_num == 0) && (jq->plugin->print_blank_page != NULL)) {
+                    jq->plugin->print_blank_page(job_handle, &(jq->job_params), jq->mime_type,
+                            page.filename);
                 }
                 if (jq->plugin->end_job != NULL) {
                     jq->plugin->end_job(&(jq->job_params));
@@ -1369,6 +1404,11 @@
                         jq->job_state = JOB_STATE_CORRUPTED;
                         jq->blocked_reasons = 0;
                         break;
+                    case BAD_CERTIFICATE:
+                        LOGD("_job_thread(): BAD_CERTIFICATE");
+                        jq->job_state = JOB_STATE_BAD_CERTIFICATE;
+                        jq->blocked_reasons = 0;
+                        break;
                     case ERROR:
                     default:
                         LOGE("_job_thread(): ERROR plugin->start_job(%ld): %s => %s", job_handle,
@@ -1419,6 +1459,7 @@
     _job_tid = pthread_self();
 
     result = OK;
+    stop_run = false;
     sigfillset(&allsig);
 #if CHECK_PTHREAD_SIGMASK_STATUS
     result = pthread_sigmask(SIG_SETMASK, &allsig, &oldsig);
@@ -1453,6 +1494,7 @@
  * Waits for the job thread to reach a stopped state
  */
 static int _stop_thread(void) {
+    stop_run = true;
     if (!pthread_equal(_job_tid, pthread_self())) {
         pthread_join(_job_tid, 0);
         _job_tid = pthread_self();
@@ -1676,6 +1718,8 @@
             printer_cap->canPrintPWG);
 
     if (result == OK) {
+        memcpy(&g_printer_caps, printer_cap, sizeof(printer_capabilities_t));
+
         LOGD("\tmake: %s", printer_cap->make);
         LOGD("\thas color: %d", printer_cap->color);
         LOGD("\tcan duplex: %d", printer_cap->duplex);
@@ -1703,7 +1747,7 @@
 /*
  * Returns a preferred print format supported by the printer
  */
-static char *_get_print_format(const char *mime_type, const wprint_job_params_t *job_params,
+char *_get_print_format(const char *mime_type, const wprint_job_params_t *job_params,
         const printer_capabilities_t *cap) {
     char *print_format = NULL;
 
@@ -1747,10 +1791,10 @@
             .duplex = DUPLEX_MODE_NONE, .dry_time = DUPLEX_DRY_TIME_NORMAL,
             .color_space = COLOR_SPACE_COLOR, .media_tray = TRAY_SRC_AUTO_SELECT,
             .pixel_units = DEFAULT_RESOLUTION, .render_flags = 0, .num_copies =1,
-            .borderless = false, .cancelled = false, .renderInReverseOrder = false,
+            .borderless = false, .cancelled = false, .face_down_tray = false,
             .ipp_1_0_supported = false, .ipp_2_0_supported = false, .epcl_ipp_supported = false,
             .strip_height = STRIPE_HEIGHT, .docCategory = {0},
-            .copies_supported = false};
+            .copies_supported = false, .preserve_scaling = false};
 
     if (job_params == NULL) return result;
 
@@ -1883,11 +1927,7 @@
         LOGD("wprintGetFinalJobParams: Duplex is on and device needs back page rotated.");
     }
 
-    if ((job_params->duplex == DUPLEX_MODE_NONE) && !printer_cap->faceDownTray) {
-        job_params->renderInReverseOrder = true;
-    } else {
-        job_params->renderInReverseOrder = false;
-    }
+    job_params->face_down_tray = printer_cap->faceDownTray;
 
     if (job_params->render_flags & RENDER_FLAG_AUTO_SCALE) {
         job_params->render_flags |= AUTO_SCALE_RENDER_FLAGS;
@@ -2196,7 +2236,7 @@
 
     if (jq) {
         LOGI("received cancel request");
-        // send a dummy page in case we're waiting on the msgQ page receive
+        // send an empty page in case we're waiting on the msgQ page receive
         if ((jq->job_state == JOB_STATE_RUNNING) || (jq->job_state == JOB_STATE_BLOCKED)) {
             bool enableTimeout = true;
             jq->cancel_ok = true;
@@ -2291,6 +2331,7 @@
 
         sem_destroy(&_job_end_wait_sem);
         sem_destroy(&_job_start_wait_sem);
+        pthread_mutex_destroy(&_q_lock);
     }
 
     return OK;
@@ -2311,3 +2352,17 @@
 
     LOGI("App Name: '%s', Version: '%s', OS: '%s'", g_appName, g_appVersion, g_osName);
 }
+
+bool wprintBlankPageForPclm(const wprint_job_params_t *job_params,
+        const printer_capabilities_t *printer_cap) {
+    return ((job_params->job_pages_per_set % 2) &&
+            ((job_params->num_copies > 1 && printer_cap->sidesSupported) ||
+                    (job_params->num_copies == 1)) && (job_params->duplex != DUPLEX_MODE_NONE));
+}
+
+bool wprintBlankPageForPwg(const wprint_job_params_t *job_params,
+        const printer_capabilities_t *printer_cap) {
+    return ((job_params->job_pages_per_set % 2) && (job_params->duplex != DUPLEX_MODE_NONE) &&
+            !(printer_cap->jobPagesPerSetSupported &&
+                    strcmp(job_params->print_format, PRINT_FORMAT_PWG) == 0));
+}
diff --git a/jni/lib/printable_area.c b/jni/lib/printable_area.c
index 81534b7..701f34a 100755
--- a/jni/lib/printable_area.c
+++ b/jni/lib/printable_area.c
@@ -47,16 +47,30 @@
     if (fabsf(job_params->source_width - job_params->page_width) < PAGE_SIZE_EPSILON &&
         fabsf(job_params->source_height - job_params->page_height) < PAGE_SIZE_EPSILON) {
         top_margin = left_margin = right_margin = bottom_margin = 0.0f;
+        job_params->preserve_scaling = true;
     }
 
     // don't adjust for margins if job is PCLm.  dimensions of image will not
     // match (will be bigger than) the dimensions of the page size and a corrupt image will render
     // in genPCLm
     if (job_params->pcl_type == PCLm) {
-        job_params->printable_area_width = (unsigned int) _MI_TO_PIXELS(
-                job_params->page_width * 1000, job_params->pixel_units);
-        job_params->printable_area_height = (unsigned int) _MI_TO_PIXELS(
-                job_params->page_height * 1000, job_params->pixel_units);
+        if (job_params->borderless) {
+            job_params->printable_area_width = (unsigned int) _MI_TO_PIXELS(
+                    job_params->page_width * 1000, job_params->pixel_units);
+            job_params->printable_area_height = (unsigned int) _MI_TO_PIXELS(
+                    job_params->page_height * 1000, job_params->pixel_units);
+        } else {
+            job_params->printable_area_width =
+                    (unsigned int) _MI_TO_PIXELS(job_params->page_width * 1000,
+                                                 job_params->pixel_units)
+                    - floorf(left_margin * (float) job_params->pixel_units)
+                    - floorf(right_margin * (float) job_params->pixel_units);
+            job_params->printable_area_height =
+                    (unsigned int) _MI_TO_PIXELS(job_params->page_height * 1000,
+                                                 job_params->pixel_units)
+                    - floorf(top_margin * (float) job_params->pixel_units)
+                    - floorf(bottom_margin * (float) job_params->pixel_units);
+        }
     } else {
         job_params->printable_area_width = (unsigned int) floorf(((job_params->page_width -
                 (left_margin + right_margin)) * (float)job_params->pixel_units));
diff --git a/jni/lib/printer.c b/jni/lib/printer.c
index 99aadf4..625d549 100644
--- a/jni/lib/printer.c
+++ b/jni/lib/printer.c
@@ -77,7 +77,8 @@
     }
 }
 
-static int _start_job(const ifc_print_job_t *this_p, const wprint_job_params_t *job_params) {
+static int _start_job(const ifc_print_job_t *this_p, const wprint_job_params_t *job_params,
+        const printer_capabilities_t *printer_caps) {
     _print_job_t *print_job = IMPL(_print_job_t, ifc, this_p);
 
     if (print_job) {
diff --git a/jni/lib/wprintJNI.c b/jni/lib/wprintJNI.c
index 1a3e757..f0d2d0d 100755
--- a/jni/lib/wprintJNI.c
+++ b/jni/lib/wprintJNI.c
@@ -20,6 +20,8 @@
 #include "lib_wprint.h"
 #include "wprint_debug.h"
 #include <errno.h>
+#include <bits/strcasecmp.h>
+#include <string.h>
 #include "../plugins/wprint_mupdf.h"
 
 #define TAG "wprintJNI"
@@ -63,6 +65,8 @@
 static jfieldID _LocalJobParamsField__pdf_render_resolution;
 static jfieldID _LocalJobParamsField__source_width;
 static jfieldID _LocalJobParamsField__source_height;
+static jfieldID _LocalJobParamsField__shared_photo;
+static jfieldID _LocalJobParamsField__preserve_scaling;
 
 static jclass _LocalPrinterCapabilitiesClass;
 static jfieldID _LocalPrinterCapabilitiesField__name;
@@ -417,8 +421,8 @@
 
     // print forward direction if printer prints pages face down; otherwise print backward
     // NOTE: last page is sent from calling function
-    if (printer_cap->faceDownTray || duplex) {
-        LOGD("_print_pdf_pages(), pages print face down or duplex, printing in normal order");
+    if (printer_cap->faceDownTray) {
+        LOGD("_print_pdf_pages(), pages print face down, printing in normal order");
         page_index = 0;
         while (page_index < num_pages) {
             LOGD("_print_pdf_pages(), PRINTING PDF: %d", *(pages_ary + page_index));
@@ -497,6 +501,10 @@
             "Z");
     _LocalJobParamsField__fill_page = (*env)->GetFieldID(env, _LocalJobParamsClass, "fill_page",
             "Z");
+    _LocalJobParamsField__shared_photo = (*env)->GetFieldID(env, _LocalJobParamsClass,
+            "shared_photo", "Z");
+    _LocalJobParamsField__preserve_scaling = (*env)->GetFieldID(env, _LocalJobParamsClass,
+            "preserve_scaling", "Z");
     _LocalJobParamsField__auto_rotate = (*env)->GetFieldID(env, _LocalJobParamsClass, "auto_rotate",
             "Z");
     _LocalJobParamsField__portrait_mode = (*env)->GetFieldID(env, _LocalJobParamsClass,
@@ -750,7 +758,6 @@
     _PrintServiceStringsField__BLOCKED_REASON__PRINTER_NMS_RESET = (*env)->GetStaticFieldID(env,
             _PrintServiceStringsClass, "BLOCKED_REASON__PRINTER_NMS_RESET", "Ljava/lang/String;");
 
-
     _PrintServiceStringsField__ALIGNMENT__CENTER = (*env)->GetStaticFieldID(
             env, _PrintServiceStringsClass, "ALIGN_CENTER", "I");
     _PrintServiceStringsField__ALIGNMENT__CENTER_HORIZONTAL = (*env)->GetStaticFieldID(
@@ -966,6 +973,8 @@
             env, javaJobParams, _LocalJobParamsField__source_height);
     wprintJobParams->source_width = (float) (*env)->GetFloatField(
             env, javaJobParams, _LocalJobParamsField__source_width);
+    wprintJobParams->preserve_scaling = (bool) (*env)->GetBooleanField(env, javaJobParams,
+            _LocalJobParamsField__preserve_scaling);
 
     if ((*env)->GetBooleanField(env, javaJobParams, _LocalJobParamsField__portrait_mode)) {
         wprintJobParams->render_flags |= RENDER_FLAG_PORTRAIT_MODE;
@@ -1119,6 +1128,8 @@
             (wprintJobParams->render_flags & RENDER_FLAG_PORTRAIT_MODE) != 0));
     (*env)->SetBooleanField(env, javaJobParams, _LocalJobParamsField__landscape_mode, (jboolean) (
             (wprintJobParams->render_flags & RENDER_FLAG_LANDSCAPE_MODE) != 0));
+    (*env)->SetBooleanField(env, javaJobParams, _LocalJobParamsField__preserve_scaling,
+            (jboolean) (wprintJobParams->preserve_scaling));
 
     // update the printable area & DPI information
     (*env)->SetIntField(env, javaJobParams, _LocalJobParamsField__print_resolution,
@@ -1570,6 +1581,11 @@
                             env, _PrintServiceStringsClass,
                             _PrintServiceStringsField__JOB_DONE_CORRUPT);
                     break;
+                case BAD_CERTIFICATE:
+                    jStr = (jstring) (*env)->GetStaticObjectField(
+                            env, _PrintServiceStringsClass,
+                            _PrintServiceStringsField__JOB_DONE_BAD_CERTIFICATE);
+                    break;
                 default:
                     jStr = (jstring) (*env)->GetStaticObjectField(
                             env, _PrintServiceStringsClass,
@@ -1819,6 +1835,7 @@
 
     int pdf_pages_ary[len];
     int pages_ary[len][MAX_NUM_PAGES];
+    const char *print_format = _get_print_format(mimeTypeStr, &params, &caps);
 
     if (hasFiles) {
         result = OK;
@@ -1860,6 +1877,61 @@
         }
 
         (*env)->ReleaseStringUTFChars(env, page, pageStr);
+
+        bool shared_photo = (*env)->GetBooleanField(env, jobParams,
+                                                    _LocalJobParamsField__shared_photo);
+        bool preserve_scaling = (*env)->GetBooleanField(env, jobParams,
+                                                        _LocalJobParamsField__preserve_scaling);
+        LOGD("setting print-scaling job param");
+        LOGD("shared_photo = %d", shared_photo);
+        LOGD("preserve_scaling = %d", preserve_scaling);
+
+        char print_scaling[MAX_PRINT_SCALING_LENGTH] = "";
+        if (strcmp(print_format, PRINT_FORMAT_PDF) == 0) {
+            LOGD("PDF pass-through");
+            bool is_photo = strcasecmp(params.docCategory, "Photo") == 0;
+            if ((is_photo && shared_photo) || preserve_scaling) {
+                for (int i = 0; i < caps.print_scalings_supported_count; i++) {
+                    if (strcmp(caps.print_scalings_supported[i], "none") == 0) {
+                        strlcpy(print_scaling, "none", sizeof(print_scaling));
+                        break;
+                    }
+                }
+            } else {
+                bool auto_supported = false;
+                for (int i = 0; i < caps.print_scalings_supported_count; i++) {
+                    if (strcmp(caps.print_scalings_supported[i], "auto") == 0) {
+                        strlcpy(print_scaling, "auto", sizeof(print_scaling));
+                        auto_supported = true;
+                        break;
+                    }
+                }
+                if (!auto_supported) {
+                    if (strcmp(caps.print_scaling_default, "") != 0) {
+                        strlcpy(print_scaling, caps.print_scaling_default,
+                                sizeof(caps.print_scaling_default));
+                    } else {
+                        strlcpy(print_scaling, "fit", sizeof(print_scaling));
+                    }
+                }
+            }
+        } else {
+            LOGD("PCLm / PWG-Raster");
+            for (int i = 0; i < caps.print_scalings_supported_count; i++) {
+                if (strcmp(caps.print_scalings_supported[i], "none") == 0) {
+                    strlcpy(print_scaling, "none", sizeof(print_scaling));
+                    break;
+                }
+            }
+        }
+        LOGD("setting print-scaling value = %s", print_scaling);
+        strlcpy(params.print_scaling, print_scaling, sizeof(params.print_scaling));
+
+        params.job_pages_per_set = 0;
+        for (int i = 0; i < len; i++) {
+            params.job_pages_per_set += pdf_pages_ary[i];
+        }
+
         const char *jobDebugDirStr = NULL;
         if (jobDebugDir != NULL) {
             jobDebugDirStr = (*env)->GetStringUTFChars(env, jobDebugDir, NULL);
@@ -1879,7 +1951,7 @@
         job_handle = (wJob_t) result;
 
         // register job handle with service
-        if (caps.faceDownTray || params.duplex) {
+        if (caps.faceDownTray) {
             index = 0;
             incrementor = 1;
         } else {
diff --git a/jni/plugins/lib_pwg.c b/jni/plugins/lib_pwg.c
index 8e3e525..54d9340 100644
--- a/jni/plugins/lib_pwg.c
+++ b/jni/plugins/lib_pwg.c
@@ -319,12 +319,14 @@
  * Allocate and fill a blank page of PackBits data. Writes size into buffer_size. The buffer
  * must be free'd by the caller.
  */
-unsigned char *_generate_blank_data(int pixel_width, int pixel_height, uint8 monochrome, size_t *buffer_size) {
+unsigned char *_generate_blank_data(int pixel_width, int pixel_height,
+                                    uint8 monochrome, size_t *buffer_size) {
     if (pixel_width == 0 || pixel_height == 0) return NULL;
 
     /* PWG Raster's PackBits-like algorithm allows for a maximum of:
      * 256 repeating rows and is encoded using a single octet containing (count - 1)
-     * 128 repeating color value and is run length encoded using a single octet containing (count - 1)
+     * 128 repeating color value and is run length encoded using a single octet
+     * containing (count - 1)
      */
     int rows_full = pixel_height / 256;
     int columns_full = pixel_width / 128;
@@ -371,8 +373,9 @@
 
         size_t buffer_size;
         unsigned char *buffer;
-        _start_page(job_info, header_pwg.cupsWidth, header_pwg.cupsHeight);
-        buffer = _generate_blank_data(header_pwg.cupsWidth, header_pwg.cupsHeight, job_info->monochrome, &buffer_size);
+        _start_page(job_info, job_info->pixel_width, job_info->pixel_height);
+        buffer = _generate_blank_data(job_info->pixel_width, job_info->pixel_height,
+                                      job_info->monochrome, &buffer_size);
         if (buffer == NULL) {
             return ERROR;
         } else {
diff --git a/jni/plugins/plugin_pcl.c b/jni/plugins/plugin_pcl.c
index 4170173..6433cd7 100755
--- a/jni/plugins/plugin_pcl.c
+++ b/jni/plugins/plugin_pcl.c
@@ -32,6 +32,7 @@
 
 #include <pthread.h>
 #include <semaphore.h>
+#include <string.h>
 
 #define MAX_SEND_BUFFS (BUFFERED_ROWS / STRIPE_HEIGHT)
 
@@ -273,10 +274,94 @@
     return ERROR;
 }
 
+static status_t _setup_image_info(wprint_job_params_t *job_params, wprint_image_info_t *image_info,
+        const char *mime_type, const char *pathname) {
+    FILE *imgfile;
+    status_t result;
+    plugin_data_t *priv;
+
+    priv = (plugin_data_t *) job_params->plugin_data;
+    if (priv == NULL) return ERROR;
+
+    if (pathname == NULL) {
+        LOGE("_setup_image_info(): cannot print file with NULL name");
+        return ERROR;
+    }
+
+    if (!strlen(pathname)) {
+        LOGE("_setup_image_info(): filename was empty");
+        return ERROR;
+    }
+
+    imgfile = fopen(pathname, "r");
+    if (imgfile == NULL) {
+        LOGE("_setup_image_info(): could not open %s", pathname);
+        return CORRUPT;
+    }
+
+    LOGD("_setup_image_info(): fopen succeeded on %s", pathname);
+    wprint_image_setup(image_info, mime_type, priv->job_info.wprint_ifc,
+            job_params->pixel_units, job_params->pdf_render_resolution);
+    wprint_image_init(image_info, pathname, job_params->page_num);
+
+    // get the image_info of the input file of specified MIME type
+    if ((result = wprint_image_get_info(imgfile, image_info)) == OK) {
+        wprint_rotation_t rotation = ROT_0;
+
+        if ((job_params->render_flags & RENDER_FLAG_PORTRAIT_MODE) != 0) {
+            LOGI("_setup_image_info(): portrait mode");
+            rotation = ROT_0;
+        } else if ((job_params->render_flags & RENDER_FLAG_LANDSCAPE_MODE) != 0) {
+            LOGI("_setup_image_info(): landscape mode");
+            rotation = ROT_90;
+        } else if (wprint_image_is_landscape(image_info) &&
+                ((job_params->render_flags & RENDER_FLAG_AUTO_ROTATE) != 0)) {
+            LOGI("_setup_image_info(): auto mode");
+            rotation = ROT_90;
+        }
+
+        if ((job_params->render_flags & RENDER_FLAG_CENTER_ON_ORIENTATION) != 0) {
+            job_params->render_flags &= ~(RENDER_FLAG_CENTER_HORIZONTAL |
+                    RENDER_FLAG_CENTER_VERTICAL);
+            job_params->render_flags |= ((rotation == ROT_0) ? RENDER_FLAG_CENTER_HORIZONTAL
+                    : RENDER_FLAG_CENTER_VERTICAL);
+        }
+
+        if ((job_params->duplex == DUPLEX_MODE_BOOK) &&
+                (job_params->page_backside) &&
+                ((job_params->render_flags & RENDER_FLAG_ROTATE_BACK_PAGE) != 0) &&
+                ((job_params->render_flags & RENDER_FLAG_BACK_PAGE_PREROTATED) == 0)) {
+            rotation = ((rotation == ROT_0) ? ROT_180 : ROT_270);
+        }
+        LOGI("_setup_image_info(): rotation = %d", rotation);
+
+        int image_padding = PAD_PRINT;
+        switch (job_params->pcl_type) {
+            case PCLm:
+            case PCLPWG:
+                image_padding = PAD_ALL;
+                break;
+            default:
+                break;
+        }
+
+        wprint_image_set_output_properties(image_info, rotation,
+                job_params->printable_area_width, job_params->printable_area_height,
+                job_params->print_top_margin, job_params->print_left_margin,
+                job_params->print_right_margin, job_params->print_bottom_margin,
+                job_params->render_flags, job_params->strip_height, MAX_SEND_BUFFS,
+                image_padding);
+    } else {
+        LOGE("_setup_image_info(): file does not appear to be valid");
+        result = CORRUPT;
+    }
+    fclose(imgfile);
+    return result;
+}
+
 static status_t _print_page(wprint_job_params_t *job_params, const char *mime_type,
         const char *pathname) {
     wprint_image_info_t *image_info;
-    FILE *imgfile;
     status_t result;
     int num_rows, height, image_row;
     int blank_data;
@@ -287,7 +372,6 @@
     int nbytes;
     plugin_data_t *priv;
     msgQ_msg_t msg;
-    int image_padding = PAD_PRINT;
 
     if (job_params == NULL) return ERROR;
 
@@ -295,200 +379,124 @@
 
     if (priv == NULL) return ERROR;
 
-    switch (job_params->pcl_type) {
-        case PCLm:
-        case PCLPWG:
-            image_padding = PAD_ALL;
-            break;
-        default:
-            break;
-    }
+    image_info = malloc(sizeof(wprint_image_info_t));
 
-    if (pathname == NULL) {
-        LOGE("_print_page(): cannot print file with NULL name");
-        msg.param.end_page.page = -1;
-        msg.param.end_page.count = 0;
-        result = ERROR;
-    } else if (strlen(pathname)) {
-        image_info = malloc(sizeof(wprint_image_info_t));
-        if (image_info == NULL) return ERROR;
+    if (image_info == NULL) return ERROR;
 
-        imgfile = fopen(pathname, "r");
-        if (imgfile) {
-            LOGD("_print_page(): fopen succeeded on %s", pathname);
-            wprint_image_setup(image_info, mime_type, priv->job_info.wprint_ifc,
-                    job_params->pixel_units, job_params->pdf_render_resolution);
-            wprint_image_init(image_info, pathname, job_params->page_num);
+    if ((result = _setup_image_info(job_params, image_info, mime_type, pathname)) == OK) {
+        // allocate memory for a stripe of data
+        for (i = 0; i < MAX_SEND_BUFFS; i++) {
+            buff_pool[i] = NULL;
+        }
 
-            // get the image_info of the input file of specified MIME type
-            if ((result = wprint_image_get_info(imgfile, image_info)) == OK) {
-                wprint_rotation_t rotation = ROT_0;
+        blank_data = MAX_SEND_BUFFS;
+        buff_size = wprint_image_get_output_buff_size(image_info);
+        for (i = 0; i < MAX_SEND_BUFFS; i++) {
+            buff_pool[i] = malloc(buff_size);
+            if (buff_pool[i] == NULL) {
+                break;
+            }
+            memset(buff_pool[i], 0xff, buff_size);
+        }
 
-                if ((job_params->render_flags & RENDER_FLAG_PORTRAIT_MODE) != 0) {
-                    LOGI("_print_page(): portrait mode");
-                    rotation = ROT_0;
-                } else if ((job_params->render_flags & RENDER_FLAG_LANDSCAPE_MODE) != 0) {
-                    LOGI("_print_page(): landscape mode");
-                    rotation = ROT_90;
-                } else if (wprint_image_is_landscape(image_info) &&
-                        ((job_params->render_flags & RENDER_FLAG_AUTO_ROTATE) != 0)) {
-                    LOGI("_print_page(): auto mode");
-                    rotation = ROT_90;
+        if (i == MAX_SEND_BUFFS) {
+            msg.id = MSG_START_PAGE;
+            msg.param.start_page.extra_margin = ((job_params->duplex != DUPLEX_MODE_NONE) &&
+                    ((job_params->page_num & 0x1) == 0)) ? job_params->page_bottom_margin : 0.0f;
+            msg.param.start_page.width = wprint_image_get_width(image_info);
+            msg.param.start_page.height = wprint_image_get_height(image_info);
+            priv->job_info.num_components = image_info->num_components;
+            priv->job_info.wprint_ifc->msgQSend(priv->msgQ, (char *) &msg, sizeof(msgQ_msg_t),
+                    NO_WAIT, MSG_Q_FIFO);
+
+            msg.id = MSG_SEND;
+            msg.param.send.bytes_per_row = BYTES_PER_PIXEL(wprint_image_get_width(image_info));
+
+            // send blank rows for any offset
+            buff_index = 0;
+            num_rows = wprint_image_get_height(image_info);
+            image_row = 0;
+
+            // decode and render each stripe into PCL3 raster format
+            while ((result != ERROR) && (num_rows > 0)) {
+                if (priv->pcl_ifc->canCancelMidPage() && job_params->cancelled) {
+                    break;
                 }
+                sem_wait(&priv->buffs_sem);
 
-                if ((job_params->render_flags & RENDER_FLAG_CENTER_ON_ORIENTATION) != 0) {
-                    job_params->render_flags &= ~(RENDER_FLAG_CENTER_HORIZONTAL |
-                            RENDER_FLAG_CENTER_VERTICAL);
-                    job_params->render_flags |= ((rotation == ROT_0) ? RENDER_FLAG_CENTER_HORIZONTAL
-                            : RENDER_FLAG_CENTER_VERTICAL);
-                }
+                buff = buff_pool[buff_index];
+                buff_index = ((buff_index + 1) % MAX_SEND_BUFFS);
 
-                if ((job_params->duplex == DUPLEX_MODE_BOOK) &&
-                        (job_params->page_backside) &&
-                        ((job_params->render_flags & RENDER_FLAG_ROTATE_BACK_PAGE) != 0) &&
-                        ((job_params->render_flags & RENDER_FLAG_BACK_PAGE_PREROTATED) == 0)) {
-                    rotation = ((rotation == ROT_0) ? ROT_180 : ROT_270);
-                }
-                LOGI("_print_page(): rotation = %d", rotation);
+                height = MIN(num_rows, job_params->strip_height);
+                if (!job_params->cancelled) {
+                    nbytes = wprint_image_decode_stripe(image_info, image_row, &height,
+                            (unsigned char *) buff);
 
-                wprint_image_set_output_properties(image_info, rotation,
-                        job_params->printable_area_width, job_params->printable_area_height,
-                        job_params->print_top_margin, job_params->print_left_margin,
-                        job_params->print_right_margin, job_params->print_bottom_margin,
-                        job_params->render_flags, job_params->strip_height, MAX_SEND_BUFFS,
-                        image_padding);
-
-                // allocate memory for a stripe of data
-                for (i = 0; i < MAX_SEND_BUFFS; i++) {
-                    buff_pool[i] = NULL;
-                }
-
-                blank_data = MAX_SEND_BUFFS;
-                buff_size = wprint_image_get_output_buff_size(image_info);
-                for (i = 0; i < MAX_SEND_BUFFS; i++) {
-                    buff_pool[i] = malloc(buff_size);
-                    if (buff_pool[i] == NULL) {
-                        break;
+                    if (blank_data > 0) {
+                        blank_data--;
                     }
-                    memset(buff_pool[i], 0xff, buff_size);
+                } else if (blank_data < MAX_SEND_BUFFS) {
+                    nbytes = buff_size;
+                    memset(buff, 0xff, buff_size);
+                    blank_data++;
                 }
 
-                if (i == MAX_SEND_BUFFS) {
-                    msg.id = MSG_START_PAGE;
-                    msg.param.start_page.extra_margin = ((job_params->duplex !=
-                            DUPLEX_MODE_NONE) &&
-                            ((job_params->page_num & 0x1) == 0))
-                            ? job_params->page_bottom_margin : 0.0f;
-                    msg.param.start_page.width = wprint_image_get_width(image_info);
-                    msg.param.start_page.height = wprint_image_get_height(image_info);
-                    priv->job_info.num_components = image_info->num_components;
-                    priv->job_info.wprint_ifc->msgQSend(priv->msgQ, (char *) &msg,
+                if (nbytes > 0) {
+                    msg.param.send.buffer = buff;
+                    msg.param.send.start_row = image_row;
+                    msg.param.send.num_rows = height;
+
+                    result = priv->job_info.wprint_ifc->msgQSend(priv->msgQ, (char *) &msg,
                             sizeof(msgQ_msg_t), NO_WAIT, MSG_Q_FIFO);
-
-                    msg.id = MSG_SEND;
-                    msg.param.send.bytes_per_row = BYTES_PER_PIXEL(wprint_image_get_width(
-                            image_info));
-
-                    // send blank rows for any offset
-                    buff_index = 0;
-                    num_rows = wprint_image_get_height(image_info);
-                    image_row = 0;
-
-                    // decode and render each stripe into PCL3 raster format
-                    while ((result != ERROR) && (num_rows > 0)) {
-                        if (priv->pcl_ifc->canCancelMidPage() && job_params->cancelled) {
-                            break;
-                        }
-                        sem_wait(&priv->buffs_sem);
-
-                        buff = buff_pool[buff_index];
-                        buff_index = ((buff_index + 1) % MAX_SEND_BUFFS);
-
-                        height = MIN(num_rows, job_params->strip_height);
-                        if (!job_params->cancelled) {
-                            nbytes = wprint_image_decode_stripe(image_info, image_row, &height,
-                                    (unsigned char *) buff);
-
-                            if (blank_data > 0) {
-                                blank_data--;
-                            }
-                        } else if (blank_data < MAX_SEND_BUFFS) {
-                            nbytes = buff_size;
-                            memset(buff, 0xff, buff_size);
-                            blank_data++;
-                        }
-
-                        if (nbytes > 0) {
-                            msg.param.send.buffer = buff;
-                            msg.param.send.start_row = image_row;
-                            msg.param.send.num_rows = height;
-
-                            result = priv->job_info.wprint_ifc->msgQSend(priv->msgQ, (char *) &msg,
-                                    sizeof(msgQ_msg_t), NO_WAIT, MSG_Q_FIFO);
-                            if (result < 0) {
-                                sem_post(&priv->buffs_sem);
-                            }
-
-                            image_row += height;
-                            num_rows -= height;
-                        } else {
-                            sem_post(&priv->buffs_sem);
-                            if (nbytes < 0) {
-                                LOGE("_print_page(): ERROR: file appears to be corrupted");
-                                result = CORRUPT;
-                            }
-                            break;
-                        }
+                    if (result < 0) {
+                        sem_post(&priv->buffs_sem);
                     }
 
-                    if ((result == OK) && job_params->cancelled) {
-                        result = CANCELLED;
-                    }
-
-                    LOGI("_print_page(): sends done, result: %d", result);
-
-                    // free the buffer and eject the page
-                    msg.param.end_page.page = job_params->page_num;
-                    LOGI("_print_page(): processed %d out of"
-                            " %d rows of page # %d from %s to printer %s %s {%s}",
-                            image_row, wprint_image_get_height(image_info),
-                            job_params->page_num, pathname,
-                            (job_params->last_page) ? "- last page" : "- ",
-                            (job_params->cancelled) ? "- job cancelled"
-                                    : ".",
-                            (result == OK) ? "OK" : "ERROR");
+                    image_row += height;
+                    num_rows -= height;
                 } else {
-                    msg.param.end_page.page = -1;
-                    result = ERROR;
-                    LOGE("_print_page(): plugin_pcl cannot allocate memory for image stripe");
+                    sem_post(&priv->buffs_sem);
+                    if (nbytes < 0) {
+                        LOGE("_print_page(): ERROR: file appears to be corrupted");
+                        result = CORRUPT;
+                    }
+                    break;
                 }
-                for (i = 0; i < MAX_SEND_BUFFS; i++) {
-                    msg.param.end_page.buffers[i] = buff_pool[i];
-                }
-                msg.param.end_page.count = MAX_SEND_BUFFS;
-            } else {
-                msg.param.end_page.page = -1;
-                msg.param.end_page.count = 0;
-                result = CORRUPT;
-                LOGE("_print_page(): file does not appear to be valid");
             }
 
-            // send the end page message
-            wprint_image_cleanup(image_info);
-            fclose(imgfile);
+            if ((result == OK) && job_params->cancelled) {
+                result = CANCELLED;
+            }
+
+            LOGI("_print_page(): sends done, result: %d", result);
+
+            // free the buffer and eject the page
+            msg.param.end_page.page = job_params->page_num;
+            LOGI("_print_page(): processed %d out of"
+                 " %d rows of page # %d from %s to printer %s %s {%s}",
+                    image_row, wprint_image_get_height(image_info), job_params->page_num, pathname,
+                    (job_params->last_page) ? "- last page" : "- ",
+                    (job_params->cancelled) ? "- job cancelled" : ".",
+                    (result == OK) ? "OK" : "ERROR");
         } else {
             msg.param.end_page.page = -1;
-            msg.param.end_page.count = 0;
-            LOGE("_print_page(): could not open %s", pathname);
-            result = CORRUPT;
+            result = ERROR;
+            LOGE("_print_page(): plugin_pcl cannot allocate memory for image stripe");
         }
-        free(image_info);
+        for (i = 0; i < MAX_SEND_BUFFS; i++) {
+            msg.param.end_page.buffers[i] = buff_pool[i];
+        }
+        msg.param.end_page.count = MAX_SEND_BUFFS;
+
+        // send the end page message
+        wprint_image_cleanup(image_info);
     } else {
-        LOGE("_print_page(): ERROR: filename was empty");
+        LOGE("_print_page(): _setup_image_info() is failed");
         msg.param.end_page.page = -1;
         msg.param.end_page.count = 0;
         result = ERROR;
     }
+    free(image_info);
 
     msg.id = MSG_END_PAGE;
     priv->job_info.wprint_ifc->msgQSend(priv->msgQ, (char *) &msg, sizeof(msgQ_msg_t), NO_WAIT,
@@ -499,7 +507,8 @@
 /*
  * Prints a blank page
  */
-static int _print_blank_page(wJob_t job_handle, wprint_job_params_t *job_params) {
+static int _print_blank_page(wJob_t job_handle, wprint_job_params_t *job_params,
+        const char *mime_type, const char *pathname) {
     msgQ_msg_t msg;
     plugin_data_t *priv;
 
@@ -508,6 +517,22 @@
     priv = (plugin_data_t *) job_params->plugin_data;
     if (priv == NULL) return ERROR;
 
+    if ((!job_params->face_down_tray && job_params->duplex != DUPLEX_MODE_NONE) ||
+            priv->job_info.pixel_width <= 0 || priv->job_info.pixel_height <= 0) {
+        // in this case, the page size for blank page has not been decided yet
+        // so we need to calculate it
+        wprint_image_info_t *image_info = malloc(sizeof(wprint_image_info_t));
+        if (image_info == NULL) return ERROR;
+
+        if (_setup_image_info(job_params, image_info, mime_type, pathname) == OK) {
+            priv->job_info.pixel_width = wprint_image_get_width(image_info);
+            priv->job_info.pixel_height = wprint_image_get_height(image_info);
+            priv->job_info.num_components = image_info->num_components;
+            wprint_image_cleanup(image_info);
+        }
+        free(image_info);
+    }
+
     msg.id = MSG_END_PAGE;
     msg.param.end_page.page = -1;
     msg.param.end_page.count = 0;
diff --git a/src/com/android/bips/ImagePrintActivity.java b/src/com/android/bips/ImagePrintActivity.java
index 765303a..75e4d2f 100644
--- a/src/com/android/bips/ImagePrintActivity.java
+++ b/src/com/android/bips/ImagePrintActivity.java
@@ -36,6 +36,7 @@
 import android.print.PrintDocumentAdapter;
 import android.print.PrintDocumentInfo;
 import android.print.PrintJob;
+import android.print.PrintJobId;
 import android.print.PrintManager;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -66,6 +67,8 @@
         "IQ", "SY", "YE", "VN", "MA"
     };
 
+    public static PrintJobId sPrintJobId;
+
     private CancellationSignal mCancellationSignal = new CancellationSignal();
     private String mJobName;
     private Bitmap mBitmap;
@@ -153,6 +156,7 @@
                     .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
                     .build();
             mPrintJob = printManager.print(mJobName, new ImageAdapter(), printAttributes);
+            sPrintJobId = mPrintJob.getId();
         }
 
         @Override
@@ -305,4 +309,13 @@
             return mBitmap;
         }
     }
+
+    /**
+     * Get the print job id from PrintManager created print job.
+     *
+     * @return A PrintJobId, can be null
+     */
+    public static PrintJobId getLastPrintJobId() {
+        return sPrintJobId;
+    }
 }
diff --git a/src/com/android/bips/LocalPrintJob.java b/src/com/android/bips/LocalPrintJob.java
index 7841127..d9ff70d 100644
--- a/src/com/android/bips/LocalPrintJob.java
+++ b/src/com/android/bips/LocalPrintJob.java
@@ -312,13 +312,12 @@
                                 BackendConstants.PARAM_ERROR_MESSAGES,
                                 getStringifiedBlockedReasons());
                         break;
+                    case BackendConstants.JOB_DONE_BAD_CERTIFICATE:
+                        handleBadCertificate(jobStatus);
+                        break;
                     default:
                         // Job failed
-                        if (jobStatus.getBlockedReasonId() == R.string.printer_bad_certificate) {
-                            handleBadCertificate(jobStatus);
-                        } else {
-                            finish(false, null);
-                        }
+                        finish(false, null);
                         bundle.putString(
                                 BackendConstants.PARAM_ERROR_MESSAGES,
                                 getStringifiedBlockedReasons());
diff --git a/src/com/android/bips/ipp/StartJobTask.java b/src/com/android/bips/ipp/StartJobTask.java
index c176231..10aba16 100755
--- a/src/com/android/bips/ipp/StartJobTask.java
+++ b/src/com/android/bips/ipp/StartJobTask.java
@@ -30,6 +30,7 @@
 import android.util.Log;
 import android.view.Gravity;
 
+import com.android.bips.ImagePrintActivity;
 import com.android.bips.jni.BackendConstants;
 import com.android.bips.jni.LocalJobParams;
 import com.android.bips.jni.LocalPrinterCapabilities;
@@ -113,6 +114,8 @@
         mJobParams.media_type = getMediaType();
         mJobParams.color_space = getColorSpace();
         mJobParams.document_category = getDocumentCategory();
+        mJobParams.shared_photo = isSharedPhoto();
+        mJobParams.preserve_scaling = false;
 
         mJobParams.job_margin_top = Math.max(mJobParams.job_margin_top, 0.0f);
         mJobParams.job_margin_left = Math.max(mJobParams.job_margin_left, 0.0f);
@@ -274,4 +277,8 @@
                 return BackendConstants.PRINT_DOCUMENT_CATEGORY__DOCUMENT;
         }
     }
+
+    private boolean isSharedPhoto() {
+        return mJobInfo.getId().equals(ImagePrintActivity.getLastPrintJobId());
+    }
 }
diff --git a/src/com/android/bips/jni/LocalJobParams.java b/src/com/android/bips/jni/LocalJobParams.java
index c2a0193..1f3af5b 100755
--- a/src/com/android/bips/jni/LocalJobParams.java
+++ b/src/com/android/bips/jni/LocalJobParams.java
@@ -65,6 +65,9 @@
     public float source_width;
     public float source_height;
 
+    public boolean shared_photo;
+    public boolean preserve_scaling;
+
     @Override
     public String toString() {
         return "LocalJobParams{"
@@ -104,6 +107,8 @@
                 + " document_scaling=" + document_scaling
                 + " source_width=" + source_width
                 + " source_height=" + source_height
+                + " shared_photo=" + shared_photo
+                + " preserve_scaling=" + preserve_scaling
                 + "}";
     }
 }
diff --git a/src/com/android/bips/p2p/P2pConnectionProcedure.java b/src/com/android/bips/p2p/P2pConnectionProcedure.java
index 44a6995..5b2df94 100644
--- a/src/com/android/bips/p2p/P2pConnectionProcedure.java
+++ b/src/com/android/bips/p2p/P2pConnectionProcedure.java
@@ -156,7 +156,7 @@
                         listener.onConnectionOpen(mNetwork, mInfo);
                     }
                 }
-            } else if (mInvited) {
+            } else if (mInvited && !network.isConnectedOrConnecting()) {
                 // Only signal connection closure if we reached the invitation phase
                 for (P2pConnectionListener listener : mListeners) {
                     listener.onConnectionClosed();
diff --git a/src/com/android/bips/ui/MoreOptionsActivity.java b/src/com/android/bips/ui/MoreOptionsActivity.java
index a8fad76..970d673 100644
--- a/src/com/android/bips/ui/MoreOptionsActivity.java
+++ b/src/com/android/bips/ui/MoreOptionsActivity.java
@@ -37,6 +37,8 @@
 
 import java.net.InetAddress;
 import java.net.UnknownHostException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 
 /**
  * Launched by system in response to a "More Options" request while tracking a printer.
@@ -51,6 +53,7 @@
     InetAddress mPrinterAddress;
 
     public static final String EXTRA_PRINTER_ID = "EXTRA_PRINTER_ID";
+    private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -100,6 +103,12 @@
     }
 
     @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        mExecutorService.shutdownNow();
+    }
+
+    @Override
     public void onServiceConnected(ComponentName name, IBinder service) {
         mPrintService = BuiltInPrintService.getInstance();
         mPrintService.getDiscovery().start(this);
@@ -116,17 +125,23 @@
             // We discovered a printer matching the job's PrinterId, so show recommendations
             mPrinter = printer;
             setTitle(mPrinter.name);
-            try {
-                mPrinterAddress = InetAddress.getByName(mPrinter.path.getHost());
-                if (getFragmentManager().getFragments().isEmpty()) {
-                    MoreOptionsFragment fragment = new MoreOptionsFragment();
-                    getFragmentManager().beginTransaction()
-                            .replace(android.R.id.content, fragment)
-                            .commit();
-                }
-                // No need for continued discovery after we find the printer.
-                mPrintService.getDiscovery().stop(this);
-            } catch (UnknownHostException ignored) { }
+            mExecutorService.execute(() -> {
+                try {
+                    mPrinterAddress = InetAddress.getByName(mPrinter.path.getHost());
+                    // No need for continued discovery after we find the printer.
+                    mPrintService.getDiscovery().stop(this);
+                    if (!mExecutorService.isShutdown() && mPrintService != null) {
+                        mPrintService.getMainHandler().post(() -> {
+                            if (getFragmentManager().getFragments().isEmpty()) {
+                                MoreOptionsFragment fragment = new MoreOptionsFragment();
+                                getFragmentManager().beginTransaction()
+                                        .replace(android.R.id.content, fragment)
+                                        .commit();
+                            }
+                        });
+                    }
+                } catch (UnknownHostException ignored) { }
+            });
         }
     }
 
diff --git a/src/com/android/bips/util/BroadcastMonitor.java b/src/com/android/bips/util/BroadcastMonitor.java
index 37d53fd..8a68dbf 100644
--- a/src/com/android/bips/util/BroadcastMonitor.java
+++ b/src/com/android/bips/util/BroadcastMonitor.java
@@ -41,7 +41,7 @@
         }
         mContext = context;
         mReceiver = receiver;
-        mContext.registerReceiver(this, filter);
+        mContext.registerReceiver(this, filter, Context.RECEIVER_EXPORTED/*UNAUDITED*/);
     }
 
     /** Stop monitoring for broadcast intents */