Merge ab/7061308 into stage.

Bug: 180401296
Merged-In: I47ef91d9d55cf89777dee5020f8aa310dced8557
Change-Id: I211e42f1fee337ce51d432786fe514444e66ce66
diff --git a/Android.bp b/Android.bp
index 1ef830f..4ffb195 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12,6 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 android_app {
     name: "BuiltInPrintService",
     privileged: true,
diff --git a/OWNERS b/OWNERS
index 9d8c08e..e2a1aec 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,2 +1 @@
-moltmann@google.com
 svetoslavganov@google.com
diff --git a/jni/Android.bp b/jni/Android.bp
index 82bf3d6..c25eb46 100644
--- a/jni/Android.bp
+++ b/jni/Android.bp
@@ -13,6 +13,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library_shared {
     name: "libwfds",
 
diff --git a/jni/ipphelper/ipphelper.c b/jni/ipphelper/ipphelper.c
index 27a4090..7daf8af 100644
--- a/jni/ipphelper/ipphelper.c
+++ b/jni/ipphelper/ipphelper.c
@@ -1051,6 +1051,15 @@
             capabilities->faceDownTray = 0;
         }
     }
+    if ((attrptr = ippFindAttribute(response, "printer-output-tray", IPP_TAG_STRING)) != NULL) {
+        for (i = 0; i < ippGetCount(attrptr); i++) {
+            int length = 0;
+            const char *tray_str = ippGetOctetString(attrptr, i, &length);
+            if (strstr(tray_str, "faceUp") != NULL) {
+                capabilities->faceDownTray = 0;
+            }
+        }
+    }
 
     // Determine supported document format details
     if ((attrptr = ippFindAttribute(response, "document-format-details-supported",
diff --git a/jni/ipphelper/ippstatus_capabilities.c b/jni/ipphelper/ippstatus_capabilities.c
index 9c596cb..f6c8ea7 100644
--- a/jni/ipphelper/ippstatus_capabilities.c
+++ b/jni/ipphelper/ippstatus_capabilities.c
@@ -56,6 +56,7 @@
         "output-bin-supported",
         "print-color-mode-supported",
         "print-quality-supported",
+        "printer-output-tray",
         "printer-resolution-supported",
         "sides-supported",
         "printer-device-id",
diff --git a/jni/lib/printable_area.c b/jni/lib/printable_area.c
index 358bd95..ee6781b 100644
--- a/jni/lib/printable_area.c
+++ b/jni/lib/printable_area.c
@@ -41,10 +41,10 @@
             job_params->page_height = SupportedMediaSizes[i].HeightInInches / 1000;
         }
     }
-    // don't adjust for margins if job is borderless and PCLm.  dimensions of image will not
+    // 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->borderless && job_params->pcl_type == PCLm) {
+    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(
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 46b5ce4..f309c88 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -111,10 +111,18 @@
 
     <!-- Channel name for connection-related notifications [CHAR LIMIT=40] -->
     <string name="connections">Connections</string>
-    <!-- Message shown in dialog, toast, or notification if the service cannot get Wi-Fi Direct permissions [CHAR LIMIT=UNLIMITED] -->
+    <!-- Message shown in notification if there is a problem accessing Wi-Fi Direct [CHAR LIMIT=UNLIMITED] -->
+    <string name="wifi_direct_problem">Default Print Service cannot find Wi-Fi Direct printers</string>
+    <!-- Button with negative response to permissions or location request, disabling Wi-Fi Direct [CHAR LIMIT=20] -->
+    <string name="disable_wifi_direct">Disable Wi-Fi Direct</string>
+    <!-- Message shown in dialog or toast if the service cannot get Wi-Fi Direct permissions [CHAR LIMIT=UNLIMITED] -->
     <string name="wifi_direct_permission_rationale">Default Print Service needs location permission to find nearby devices.</string>
     <!-- Button label in a notification or dialog. This button leads to a request to grant permissions [CHAR LIMIT=20] -->
     <string name="fix">Review permission</string>
+    <!-- Message shown in dialog or toast if location is required but disabled [CHAR LIMIT=UNLIMITED] -->
+    <string name="wifi_direct_location_rationale">Default Print Service needs location services enabled to find nearby devices.</string>
+    <!-- Button label in a notification or dialog. This button leads to location settings [CHAR LIMIT=20] -->
+    <string name="enable_location">Enable location</string>
 
     <!-- Share-to-print label [CHAR LIMIT=20] -->
     <string name="print">Print</string>
diff --git a/src/com/android/bips/BuiltInPrintService.java b/src/com/android/bips/BuiltInPrintService.java
index f9931a5..aa8aafa 100644
--- a/src/com/android/bips/BuiltInPrintService.java
+++ b/src/com/android/bips/BuiltInPrintService.java
@@ -68,6 +68,8 @@
             BuiltInPrintService.class.getCanonicalName() + ".CERTIFICATE_REJECT";
     public static final String ACTION_P2P_PERMISSION_CANCEL =
             BuiltInPrintService.class.getCanonicalName() + ".P2P_PERMISSION_CANCEL";
+    public static final String ACTION_P2P_DISABLE =
+            BuiltInPrintService.class.getCanonicalName() + ".ACTION_P2P_DISABLE";
     private static final String EXTRA_CERTIFICATE = "certificate";
     private static final String EXTRA_PRINTER_ID = "printer-id";
     private static final String EXTRA_PRINTER_UUID = "printer-uuid";
@@ -377,6 +379,8 @@
         } else if (ACTION_P2P_PERMISSION_CANCEL.equals(intent.getAction())) {
             // Inform p2pPermissionManager the user canceled the notification (non-permanent)
             mP2pPermissionManager.applyPermissionChange(false);
+        } else if (ACTION_P2P_DISABLE.equals(intent.getAction())) {
+            mP2pPermissionManager.applyPermissionChange(true);
         }
         return START_NOT_STICKY;
     }
diff --git a/src/com/android/bips/P2pPermissionManager.java b/src/com/android/bips/P2pPermissionManager.java
index d055a22..e9be8c2 100644
--- a/src/com/android/bips/P2pPermissionManager.java
+++ b/src/com/android/bips/P2pPermissionManager.java
@@ -25,11 +25,12 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.Context;
-import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Icon;
+import android.location.LocationManager;
+import android.provider.Settings;
 import android.util.Log;
 import android.widget.Toast;
 
@@ -45,6 +46,7 @@
 
     private static final String CHANNEL_ID_CONNECTIONS = "connections";
     public static final int REQUEST_P2P_PERMISSION_CODE = 1000;
+    public static final int REQUEST_LOCATION_ENABLE = 1001;
 
     private static final String STATE_KEY = "state";
 
@@ -73,7 +75,7 @@
      * Update the current P2P permissions request state.
      */
     public void setState(State state) {
-        if (DEBUG) Log.d(TAG, "Setting state=" + state);
+        if (DEBUG) Log.d(TAG, "State from " + mPrefs.getString(STATE_KEY, "?") + " to " + state);
         mPrefs.edit().putString(STATE_KEY, state.name()).apply();
     }
 
@@ -91,11 +93,15 @@
         closeNotification();
         if (hasP2pPermission()) {
             setState(State.ALLOWED);
-        } else {
-            // Inform the user and don't try again for the rest of this session.
-            setState(permanent ? State.DISABLED : State.TEMPORARILY_DISABLED);
-            Toast.makeText(mContext, R.string.wifi_direct_permission_rationale, Toast.LENGTH_LONG)
-                    .show();
+        } else if (getState() != State.DISABLED) {
+            if (permanent) {
+                setState(State.DISABLED);
+            } else {
+                // Inform the user and don't try again for the rest of this session.
+                setState(State.TEMPORARILY_DISABLED);
+                Toast.makeText(mContext, R.string.wifi_direct_permission_rationale,
+                        Toast.LENGTH_LONG).show();
+            }
         }
     }
 
@@ -133,10 +139,15 @@
 
         if (mContext instanceof Activity) {
             Activity activity = (Activity) mContext;
-            if (explain && activity.shouldShowRequestPermissionRationale(ACCESS_FINE_LOCATION)) {
-                explain(activity);
-            } else {
-                request(activity);
+            if (!isLocationEnabled()) {
+                requestLocation(activity);
+            } else if (!hasP2pPermission()) {
+                if (explain && activity.shouldShowRequestPermissionRationale(
+                        ACCESS_FINE_LOCATION)) {
+                    explain(activity);
+                } else {
+                    request(activity);
+                }
             }
         } else {
             showNotification();
@@ -159,18 +170,30 @@
 
     private void explain(Activity activity) {
         // User denied, but asked us to use P2P, so explain and redirect to settings
-        DialogInterface.OnClickListener clickListener = (dialog, which) -> {
-            if (which == DialogInterface.BUTTON_POSITIVE) {
-                request(activity);
-            }
-        };
-
         new AlertDialog.Builder(activity, android.R.style.Theme_DeviceDefault_Light_Dialog_Alert)
                 .setMessage(mContext.getString(R.string.wifi_direct_permission_rationale))
-                .setPositiveButton(R.string.fix, clickListener)
+                .setPositiveButton(R.string.fix, (dialog, which) -> request(activity))
                 .show();
     }
 
+    /**
+     * Request location services be enabled globally.
+     */
+    private void requestLocation(Activity activity) {
+        new AlertDialog.Builder(activity, android.R.style.Theme_DeviceDefault_Light_Dialog_Alert)
+            .setMessage(mContext.getString(R.string.wifi_direct_location_rationale))
+            .setPositiveButton(R.string.enable_location, (dialog, which) ->
+                activity.startActivityForResult(new Intent(
+                        Settings.ACTION_LOCATION_SOURCE_SETTINGS), REQUEST_LOCATION_ENABLE)
+            )
+            .setOnCancelListener(dialog -> {
+                if (getState() == State.DENIED) {
+                    setState(State.TEMPORARILY_DISABLED);
+                }
+            })
+            .show();
+    }
+
     private SharedPreferences.OnSharedPreferenceChangeListener listenForPreferenceChanges(
             P2pPermissionListener listener) {
         SharedPreferences.OnSharedPreferenceChangeListener preferenceListener =
@@ -212,19 +235,26 @@
         PendingIntent cancelPendingIndent = PendingIntent.getService(mContext,
                 BuiltInPrintService.P2P_PERMISSION_REQUEST_ID, cancelIntent,
                 PendingIntent.FLAG_UPDATE_CURRENT);
-        Notification.Action cancelAction = new Notification.Action.Builder(
+
+        Intent disableIntent = new Intent(mContext, BuiltInPrintService.class)
+                .setAction(BuiltInPrintService.ACTION_P2P_DISABLE);
+        PendingIntent disablePendingIndent = PendingIntent.getService(mContext,
+                BuiltInPrintService.P2P_PERMISSION_REQUEST_ID, disableIntent,
+                PendingIntent.FLAG_UPDATE_CURRENT);
+        Notification.Action disableAction = new Notification.Action.Builder(
                 Icon.createWithResource(mContext, R.drawable.ic_printservice),
-                mContext.getString(android.R.string.cancel), cancelPendingIndent).build();
+                mContext.getString(R.string.disable_wifi_direct), disablePendingIndent).build();
 
         Notification notification = new Notification.Builder(mContext, CHANNEL_ID_CONNECTIONS)
                 .setSmallIcon(R.drawable.ic_printservice)
+                .setContentText(mContext.getString(R.string.wifi_direct_problem))
                 .setStyle(new Notification.BigTextStyle().bigText(
-                        mContext.getString(R.string.wifi_direct_permission_rationale)))
+                        mContext.getString(R.string.wifi_direct_problem)))
                 .setAutoCancel(true)
                 .setContentIntent(proceedPendingIntent)
                 .setDeleteIntent(cancelPendingIndent)
                 .addAction(fixAction)
-                .addAction(cancelAction)
+                .addAction(disableAction)
                 .build();
 
         mNotificationManager.notify(BuiltInPrintService.P2P_PERMISSION_REQUEST_ID, notification);
@@ -243,12 +273,12 @@
             return state;
         }
 
-        boolean hasPermission = hasP2pPermission();
-        if (hasPermission && state != State.ALLOWED) {
+        boolean allowed = isLocationEnabled() && hasP2pPermission();
+        if (allowed && state != State.ALLOWED) {
             // Upgrade state if now allowed
             state = State.ALLOWED;
             setState(state);
-        } else if (!hasPermission && state == State.ALLOWED) {
+        } else if (!allowed && state == State.ALLOWED) {
             state = State.DENIED;
             setState(state);
         }
@@ -256,6 +286,15 @@
     }
 
     /**
+     * Return true if location services are enabled.
+     */
+    private boolean isLocationEnabled() {
+        LocationManager manager = (LocationManager) mContext.getSystemService(
+                Context.LOCATION_SERVICE);
+        return manager.isProviderEnabled(LocationManager.PASSIVE_PROVIDER);
+    }
+
+    /**
      * Close any outstanding notification.
      */
     void closeNotification() {
diff --git a/src/com/android/bips/ui/AddPrintersFragment.java b/src/com/android/bips/ui/AddPrintersFragment.java
index e122cad..1bb3703 100644
--- a/src/com/android/bips/ui/AddPrintersFragment.java
+++ b/src/com/android/bips/ui/AddPrintersFragment.java
@@ -16,6 +16,7 @@
 
 package com.android.bips.ui;
 
+import android.app.Activity;
 import android.app.AlertDialog;
 import android.content.ComponentName;
 import android.content.Context;
@@ -96,8 +97,8 @@
             mP2pPermissionManager.request(false, approve -> {
                 updateP2pPreferences();
                 if (!approve) {
-                    // The user is choosing to disable by denying Location.
-                    mP2pPermissionManager.setState(P2pPermissionManager.State.DISABLED);
+                    // Try again next time
+                    mP2pPermissionManager.setState(P2pPermissionManager.State.TEMPORARILY_DISABLED);
                 }
             });
         }
@@ -149,9 +150,11 @@
         if (mP2pPermissionManager.isP2pEnabled()) {
             mP2pEnablePreference.setChecked(true);
             getPreferenceScreen().addPreference(mFindP2pPrintersPreference);
-            if (getActivity().getIntent().getBooleanExtra(EXTRA_FIX_P2P_PERMISSION, false)) {
+            Activity activity = getActivity();
+            if (activity != null && activity.getIntent().getBooleanExtra(EXTRA_FIX_P2P_PERMISSION,
+                    false)) {
                 // If we were only here to enable P2P permissions, go back to the print now.
-                getActivity().finish();
+                activity.finish();
             }
         } else {
             mP2pEnablePreference.setChecked(false);