Merge "Implementing breadcrumb for tablet devices for navigation in the bar."
diff --git a/core/java/android/os/IDeviceIdleController.aidl b/core/java/android/os/IDeviceIdleController.aidl
index 082194b..cc2af21 100644
--- a/core/java/android/os/IDeviceIdleController.aidl
+++ b/core/java/android/os/IDeviceIdleController.aidl
@@ -38,8 +38,6 @@
     long addPowerSaveTempWhitelistAppForMms(String name, int userId, String reason);
     long addPowerSaveTempWhitelistAppForSms(String name, int userId, String reason);
     void exitIdle(String reason);
-    void downloadServiceActive(IBinder token);
-    void downloadServiceInactive();
     boolean registerMaintenanceActivityListener(IMaintenanceActivityListener listener);
     void unregisterMaintenanceActivityListener(IMaintenanceActivityListener listener);
 }
diff --git a/core/res/res/values-watch/config_material.xml b/core/res/res/values-watch/config_material.xml
index 951c088..81b53e7 100644
--- a/core/res/res/values-watch/config_material.xml
+++ b/core/res/res/values-watch/config_material.xml
@@ -27,9 +27,6 @@
     <!-- Use micro alert controller -->
     <integer name="config_alertDialogController">1</integer>
 
-    <!-- Dialog windows in watch should occupy the whole screen and not be floating. -->
-    <bool name="config_dialogWindowIsFloating">false</bool>
-
     <!-- Always overscan by default to ensure onApplyWindowInsets will always be called. -->
     <bool name="config_windowOverscanByDefault">true</bool>
 
diff --git a/core/res/res/values-watch/themes_material.xml b/core/res/res/values-watch/themes_material.xml
index d92a947..4ae4367 100644
--- a/core/res/res/values-watch/themes_material.xml
+++ b/core/res/res/values-watch/themes_material.xml
@@ -38,4 +38,25 @@
         <item name="imeFullscreenBackground">?colorBackground</item>
         <item name="imeExtractEnterAnimation">@anim/input_method_extract_enter</item>
     </style>
+
+    <!-- Override behaviour to set the theme colours for dialogs, keep them the same. -->
+    <style name="ThemeOverlay.Material.Dialog" parent="ThemeOverlay.Material.BaseDialog">
+        <item name="windowIsFloating">false</item>
+    </style>
+
+    <!-- Force the background and floating colours to be the default colours. -->
+    <style name="Theme.Material.Dialog" parent="Theme.Material.BaseDialog">
+        <item name="colorBackground">@color/background_material_dark</item>
+        <item name="colorBackgroundFloating">@color/background_floating_material_dark</item>
+        <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_dark</item>
+        <item name="windowIsFloating">false</item>
+    </style>
+
+    <!-- Force the background and floating colours to be the default colours. -->
+    <style name="Theme.Material.Light.Dialog" parent="Theme.Material.Light.BaseDialog">
+        <item name="colorBackground">@color/background_material_light</item>
+        <item name="colorBackgroundFloating">@color/background_floating_material_light</item>
+        <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_light</item>
+        <item name="windowIsFloating">false</item>
+    </style>
 </resources>
diff --git a/core/res/res/values/config_material.xml b/core/res/res/values/config_material.xml
index f62678a..a37be83 100644
--- a/core/res/res/values/config_material.xml
+++ b/core/res/res/values/config_material.xml
@@ -29,9 +29,6 @@
     <!-- The alert controller to use for alert dialogs. -->
     <integer name="config_alertDialogController">0</integer>
 
-    <!-- True if dialog windows are floating. -->
-    <bool name="config_dialogWindowIsFloating">true</bool>
-
     <!-- True if windowOverscan should be on by default. -->
     <bool name="config_windowOverscanByDefault">false</bool>
 
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 0e4e060..881b9b3 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -864,17 +864,14 @@
         <item name="searchViewStyle">@style/Widget.Material.SearchView.ActionBar</item>
     </style>
 
-    <!-- Theme overlay that overrides window properties to display as a dialog. -->
-    <style name="ThemeOverlay.Material.Dialog">
-        <item name="colorBackgroundCacheHint">@null</item>
-        <item name="colorBackground">?attr/colorBackgroundFloating</item>
-
+    <!-- Base theme for overlay dialogs, customize the colours in the actual dialog theme. -->
+    <style name="ThemeOverlay.Material.BaseDialog">
         <item name="windowFrame">@null</item>
         <item name="windowTitleStyle">@style/DialogWindowTitle.Material</item>
         <item name="windowTitleBackgroundStyle">@style/DialogWindowTitleBackground.Material</item>
         <item name="windowBackground">@drawable/dialog_background_material</item>
         <item name="windowElevation">@dimen/floating_window_z</item>
-        <item name="windowIsFloating">@bool/config_dialogWindowIsFloating</item>
+        <item name="windowIsFloating">true</item>
         <item name="windowContentOverlay">@null</item>
         <item name="windowAnimationStyle">@style/Animation.Material.Dialog</item>
         <item name="windowSoftInputMode">stateUnspecified|adjustPan</item>
@@ -897,6 +894,12 @@
         <item name="windowFixedHeightMinor">@null</item>
     </style>
 
+    <!-- Theme overlay that overrides window properties to display as a dialog. -->
+    <style name="ThemeOverlay.Material.Dialog" parent="ThemeOverlay.Material.BaseDialog">
+        <item name="colorBackgroundCacheHint">@null</item>
+        <item name="colorBackground">?attr/colorBackgroundFloating</item>
+    </style>
+
     <!-- Theme overlay that overrides window properties to display as a date picker dialog. -->
     <style name="ThemeOverlay.Material.Dialog.DatePicker">
         <item name="alertDialogStyle">@style/DatePickerDialog.Material</item>
@@ -1080,7 +1083,7 @@
         <item name="windowTitleBackgroundStyle">@style/DialogWindowTitleBackground.Material</item>
         <item name="windowBackground">@drawable/dialog_background_material</item>
         <item name="windowElevation">@dimen/floating_window_z</item>
-        <item name="windowIsFloating">@bool/config_dialogWindowIsFloating</item>
+        <item name="windowIsFloating">true</item>
         <item name="windowContentOverlay">@null</item>
         <item name="windowAnimationStyle">@style/Animation.Material.Dialog</item>
         <item name="windowSoftInputMode">stateUnspecified|adjustPan</item>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java b/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java
index d6f2e5b..cde9e92 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java
@@ -221,7 +221,7 @@
         try {
             mBatchSize = calculateSize(mSrcs);
         } catch (ResourceException e) {
-            Log.w(TAG, "Failed to calculate total size. Copying without progress.");
+            Log.w(TAG, "Failed to calculate total size. Copying without progress.", e);
             mBatchSize = -1;
         }
 
@@ -230,25 +230,19 @@
         for (int i = 0; i < mSrcs.size() && !isCanceled(); ++i) {
             srcInfo = mSrcs.get(i);
 
-            // Guard unsupported recursive operation.
-            try {
-                if (dstInfo.equals(srcInfo) || isDescendentOf(srcInfo, dstInfo)) {
-                    throw new ResourceException("Cannot copy to itself recursively.");
-                }
-            } catch (ResourceException e) {
-                Log.e(TAG, e.toString());
-                onFileFailed(srcInfo);
-                continue;
-            }
-
             if (DEBUG) Log.d(TAG,
                     "Copying " + srcInfo.displayName + " (" + srcInfo.derivedUri + ")"
                     + " to " + dstInfo.displayName + " (" + dstInfo.derivedUri + ")");
 
             try {
-                processDocument(srcInfo, null, dstInfo);
+                if (dstInfo.equals(srcInfo) || isDescendentOf(srcInfo, dstInfo)) {
+                    Log.e(TAG, "Skipping recursive copy of " + srcInfo.derivedUri);
+                    onFileFailed(srcInfo);
+                } else {
+                    processDocument(srcInfo, null, dstInfo);
+                }
             } catch (ResourceException e) {
-                Log.e(TAG, e.toString());
+                Log.e(TAG, "Failed to copy " + srcInfo.derivedUri, e);
                 onFileFailed(srcInfo);
             }
         }
@@ -296,7 +290,7 @@
                     }
                 } catch (RemoteException | RuntimeException e) {
                     Log.e(TAG, "Provider side copy failed for: " + src.derivedUri
-                            + " due to an exception: " + e);
+                            + " due to an exception.", e);
                 }
 
                 // If optimized copy fails, then fallback to byte-by-byte copy.
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/DeleteJob.java b/packages/DocumentsUI/src/com/android/documentsui/services/DeleteJob.java
index e9bdd2c..8e27d6a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/DeleteJob.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/DeleteJob.java
@@ -104,7 +104,7 @@
                     return;
                 }
             } catch (ResourceException e) {
-                Log.e(TAG, "Failed to delete document @ " + doc.derivedUri);
+                Log.e(TAG, "Failed to delete document @ " + doc.derivedUri, e);
                 onFileFailed(doc);
             }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java b/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java
index aaa7596..1118171 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java
@@ -98,7 +98,7 @@
                     }
                 } catch (RemoteException | RuntimeException e) {
                     Log.e(TAG, "Provider side move failed for: " + src.derivedUri
-                            + " due to an exception: " + e);
+                            + " due to an exception: ", e);
                 }
                 // If optimized move fails, then fallback to byte-by-byte copy.
                 if (DEBUG) Log.d(TAG, "Fallback to byte-by-byte move for: " + src.derivedUri);
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index bb966f7..afed5ef 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -212,7 +212,6 @@
 
     private int mActiveIdleOpCount;
     private PowerManager.WakeLock mActiveIdleWakeLock;
-    private IBinder mDownloadServiceActive;
     private boolean mJobsActive;
     private boolean mAlarmsActive;
     private boolean mReportedMaintenanceActivity;
@@ -607,7 +606,7 @@
          * This is the minimum amount of time that we will stay in maintenance mode after
          * a light doze.  We have this minimum to allow various things to respond to switching
          * in to maintenance mode and scheduling their work -- otherwise we may
-         * see there is nothing to do (no jobs or downloads pending) and go out of maintenance
+         * see there is nothing to do (no jobs pending) and go out of maintenance
          * mode immediately.
          * @see Settings.Global#DEVICE_IDLE_CONSTANTS
          * @see #KEY_MIN_LIGHT_MAINTENANCE_TIME
@@ -618,7 +617,7 @@
          * This is the minimum amount of time that we will stay in maintenance mode after
          * a full doze.  We have this minimum to allow various things to respond to switching
          * in to maintenance mode and scheduling their work -- otherwise we may
-         * see there is nothing to do (no jobs or downloads pending) and go out of maintenance
+         * see there is nothing to do (no jobs pending) and go out of maintenance
          * mode immediately.
          * @see Settings.Global#DEVICE_IDLE_CONSTANTS
          * @see #KEY_MIN_DEEP_MAINTENANCE_TIME
@@ -1220,28 +1219,6 @@
             }
         }
 
-        @Override public void downloadServiceActive(IBinder token) {
-            getContext().enforceCallingOrSelfPermission(
-                    "android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS", null);
-            long ident = Binder.clearCallingIdentity();
-            try {
-                DeviceIdleController.this.downloadServiceActive(token);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-
-        @Override public void downloadServiceInactive() {
-            getContext().enforceCallingOrSelfPermission(
-                    "android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS", null);
-            long ident = Binder.clearCallingIdentity();
-            try {
-                DeviceIdleController.this.downloadServiceInactive();
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-
         @Override public boolean registerMaintenanceActivityListener(
                 IMaintenanceActivityListener listener) {
             return DeviceIdleController.this.registerMaintenanceActivityListener(listener);
@@ -2086,30 +2063,6 @@
         }
     }
 
-    void downloadServiceActive(IBinder token) {
-        synchronized (this) {
-            mDownloadServiceActive = token;
-            reportMaintenanceActivityIfNeededLocked();
-            try {
-                token.linkToDeath(new IBinder.DeathRecipient() {
-                    @Override public void binderDied() {
-                        downloadServiceInactive();
-                    }
-                }, 0);
-            } catch (RemoteException e) {
-                mDownloadServiceActive = null;
-            }
-        }
-    }
-
-    void downloadServiceInactive() {
-        synchronized (this) {
-            mDownloadServiceActive = null;
-            reportMaintenanceActivityIfNeededLocked();
-            exitMaintenanceEarlyIfNeededLocked();
-        }
-    }
-
     void setJobsActive(boolean active) {
         synchronized (this) {
             mJobsActive = active;
@@ -2143,7 +2096,7 @@
     }
 
     void reportMaintenanceActivityIfNeededLocked() {
-        boolean active = mJobsActive | (mDownloadServiceActive != null);
+        boolean active = mJobsActive;
         if (active == mReportedMaintenanceActivity) {
             return;
         }
@@ -2154,8 +2107,7 @@
     }
 
     boolean isOpsInactiveLocked() {
-        return mActiveIdleOpCount <= 0 && mDownloadServiceActive == null
-                && !mJobsActive && !mAlarmsActive;
+        return mActiveIdleOpCount <= 0 && !mJobsActive && !mAlarmsActive;
     }
 
     void exitMaintenanceEarlyIfNeededLocked() {
@@ -3053,9 +3005,6 @@
             if (mAlarmsActive) {
                 pw.print("  mAlarmsActive="); pw.println(mAlarmsActive);
             }
-            if (mDownloadServiceActive != null) {
-                pw.print("  mDownloadServiceActive="); pw.println(mDownloadServiceActive);
-            }
         }
     }
 
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 27b3aa2..8589de1 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -692,8 +692,13 @@
         boolean active = mPendingJobs.size() > 0;
         if (mPendingJobs.size() <= 0) {
             for (int i=0; i<mActiveServices.size(); i++) {
-                JobServiceContext jsc = mActiveServices.get(i);
-                if (jsc.getRunningJob() != null) {
+                final JobServiceContext jsc = mActiveServices.get(i);
+                final JobStatus job = jsc.getRunningJob();
+                if (job != null
+                        && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0
+                        && !job.dozeWhitelisted) {
+                    // We will report active if we have a job running and it is not an exception
+                    // due to being in the foreground or whitelisted.
                     active = true;
                     break;
                 }
diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java
index a23af35..2dbecbd 100644
--- a/services/core/java/com/android/server/job/controllers/AppIdleController.java
+++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java
@@ -142,8 +142,11 @@
                 UserHandle.formatUid(pw, jobStatus.getSourceUid());
                 pw.print(": ");
                 pw.print(jobStatus.getSourcePackageName());
-                pw.print(", runnable=");
-                pw.println((jobStatus.satisfiedConstraints&JobStatus.CONSTRAINT_APP_NOT_IDLE) != 0);
+                if ((jobStatus.satisfiedConstraints&JobStatus.CONSTRAINT_APP_NOT_IDLE) != 0) {
+                    pw.println(" RUNNABLE");
+                } else {
+                    pw.println(" WAITING");
+                }
             }
         });
     }
diff --git a/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java
index bf1297f..f7706d7 100644
--- a/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java
+++ b/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java
@@ -157,8 +157,9 @@
     }
 
     private void updateTaskStateLocked(JobStatus task) {
-        boolean enableTask = !mDeviceIdleMode || isWhitelistedLocked(task);
-        task.setDeviceNotDozingConstraintSatisfied(enableTask);
+        final boolean whitelisted = isWhitelistedLocked(task);
+        final boolean enableTask = !mDeviceIdleMode || whitelisted;
+        task.setDeviceNotDozingConstraintSatisfied(enableTask, whitelisted);
     }
 
     @Override
@@ -186,9 +187,13 @@
                 UserHandle.formatUid(pw, jobStatus.getSourceUid());
                 pw.print(": ");
                 pw.print(jobStatus.getSourcePackageName());
-                pw.print(", runnable=");
-                pw.println((jobStatus.satisfiedConstraints
-                        & JobStatus.CONSTRAINT_DEVICE_NOT_DOZING) != 0);
+                pw.print((jobStatus.satisfiedConstraints
+                        & JobStatus.CONSTRAINT_DEVICE_NOT_DOZING) != 0
+                                ? " RUNNABLE" : " WAITING");
+                if (jobStatus.dozeWhitelisted) {
+                    pw.print(" WHITELISTED");
+                }
+                pw.println();
             }
         });
     }
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 68dd524..8aef471 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -104,6 +104,9 @@
     final int requiredConstraints;
     int satisfiedConstraints = 0;
 
+    // Set to true if doze constraint was satisfied due to app being whitelisted.
+    public boolean dozeWhitelisted;
+
     // These are filled in by controllers when preparing for execution.
     public ArraySet<Uri> changedUris;
     public ArraySet<String> changedAuthorities;
@@ -408,7 +411,8 @@
         return setConstraintSatisfied(CONSTRAINT_CONTENT_TRIGGER, state);
     }
 
-    boolean setDeviceNotDozingConstraintSatisfied(boolean state) {
+    boolean setDeviceNotDozingConstraintSatisfied(boolean state, boolean whitelisted) {
+        dozeWhitelisted = whitelisted;
         return setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state);
     }
 
@@ -656,6 +660,9 @@
             pw.print(prefix); pw.print("Unsatisfied constraints:");
             dumpConstraints(pw, (requiredConstraints & ~satisfiedConstraints));
             pw.println();
+            if (dozeWhitelisted) {
+                pw.print(prefix); pw.println("Doze whitelisted: true");
+            }
         }
         if (changedAuthorities != null) {
             pw.print(prefix); pw.println("Changed authorities:");
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 45b5f26..50f6ec7 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2614,17 +2614,18 @@
     private static void setPendingIntentWhitelistDuration(ActivityManagerInternal am, long duration,
             Bundle extras) {
         for (String key : extras.keySet()) {
-            setPendingIntentWhitelistDuration(am, duration, extras.getParcelable(key));
-            final Parcelable[] parcelableArray = extras.getParcelableArray(key);
-            if (parcelableArray != null) {
-                for (Parcelable parcelable: parcelableArray) {
+            final Object value = extras.get(key);
+            if (value instanceof Parcelable) {
+                setPendingIntentWhitelistDuration(am, duration, (Parcelable) value);
+            } else if (value instanceof Parcelable[]) {
+                for (Parcelable parcelable : (Parcelable[]) value) {
                     setPendingIntentWhitelistDuration(am, duration, parcelable);
                 }
-            }
-            final ArrayList<Parcelable> parcelableList = extras.getParcelableArrayList(key);
-            if (parcelableList != null) {
-                for (Parcelable parcelable: parcelableList) {
-                    setPendingIntentWhitelistDuration(am, duration, parcelable);
+            } else if (value instanceof List) {
+                for (Object element : (List <?>) value) {
+                    if (element instanceof Parcelable) {
+                        setPendingIntentWhitelistDuration(am, duration, (Parcelable) element);
+                    }
                 }
             }
         }