Merge "Layer user restrictions"
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 6ae32d0..fce1b2e 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1200,6 +1200,12 @@
         public int id;
 
         /**
+         * The stack that currently contains this task.
+         * @hide
+         */
+        public int stackId;
+
+        /**
          * The component launched as the first activity in the task.  This can
          * be considered the "application" of this task.
          */
@@ -1248,6 +1254,7 @@
 
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(id);
+            dest.writeInt(stackId);
             ComponentName.writeToParcel(baseActivity, dest);
             ComponentName.writeToParcel(topActivity, dest);
             if (thumbnail != null) {
@@ -1264,6 +1271,7 @@
 
         public void readFromParcel(Parcel source) {
             id = source.readInt();
+            stackId = source.readInt();
             baseActivity = ComponentName.readFromParcel(source);
             topActivity = ComponentName.readFromParcel(source);
             if (source.readInt() != 0) {
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index bf3387b..bca9be6 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -257,11 +257,13 @@
             int deg = Integer.parseInt(degrees);
             double min;
             double sec = 0.0;
+            boolean secPresent = false;
 
             if (st.hasMoreTokens()) {
                 min = Integer.parseInt(minutes);
                 String seconds = st.nextToken();
                 sec = Double.parseDouble(seconds);
+                secPresent = true;
             } else {
                 min = Double.parseDouble(minutes);
             }
@@ -273,11 +275,15 @@
             if ((deg < 0.0) || (deg > 179 && !isNegative180)) {
                 throw new IllegalArgumentException("coordinate=" + coordinate);
             }
-            if (min < 0 || min > 59) {
+
+            // min must be in [0, 59] if seconds are present, otherwise [0.0, 60.0)
+            if (min < 0 || min >= 60 || (secPresent && (min > 59))) {
                 throw new IllegalArgumentException("coordinate=" +
                         coordinate);
             }
-            if (sec < 0 || sec > 59) {
+
+            // sec must be in [0.0, 60.0)
+            if (sec < 0 || sec >= 60) {
                 throw new IllegalArgumentException("coordinate=" +
                         coordinate);
             }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index 0dcd02d..d75b6fd 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -287,33 +287,24 @@
             super(context, 0);
 
             final List<RootItem> libraries = new ArrayList<>();
-            final List<RootItem> clouds = new ArrayList<>();
-            final List<RootItem> locals = new ArrayList<>();
+            final List<RootItem> others = new ArrayList<>();
 
-            for (RootInfo root : roots) {
-                RootItem item = new RootItem(root);
-                switch (root.derivedType) {
-                    case RootInfo.TYPE_LOCAL:
-                        locals.add(item);
-                        break;
-                    case RootInfo.TYPE_CLOUD:
-                        clouds.add(item);
-                        break;
-                    default:
-                        libraries.add(item);
-                        break;
+            for (final RootInfo root : roots) {
+                final RootItem item = new RootItem(root);
+                if (root.isLibrary()) {
+                    libraries.add(item);
+                } else {
+                    others.add(item);
                 }
             }
 
             final RootComparator comp = new RootComparator();
             Collections.sort(libraries, comp);
-            Collections.sort(locals, comp);
-            Collections.sort(clouds, comp);
+            Collections.sort(others, comp);
 
             addAll(libraries);
             add(new SpacerItem());
-            addAll(locals);
-            addAll(clouds);
+            addAll(others);
 
             if (includeApps != null) {
                 final PackageManager pm = context.getPackageManager();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
index 17e6a80..501392c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -51,7 +51,8 @@
     public static final int TYPE_RECENTS = 4;
     public static final int TYPE_DOWNLOADS = 5;
     public static final int TYPE_LOCAL = 6;
-    public static final int TYPE_CLOUD = 7;
+    public static final int TYPE_MTP = 7;
+    public static final int TYPE_CLOUD = 8;
 
     public String authority;
     public String rootId;
@@ -184,6 +185,8 @@
             derivedType = TYPE_AUDIO;
         } else if (isRecents()) {
             derivedType = TYPE_RECENTS;
+        } else if (isMtp()) {
+            derivedType = TYPE_MTP;
         } else {
             derivedType = TYPE_CLOUD;
         }
@@ -216,6 +219,15 @@
                 && "audio_root".equals(rootId);
     }
 
+    public boolean isMtp() {
+        return "com.android.mtp.documents".equals(authority);
+    }
+
+    public boolean isLibrary() {
+        return derivedType == TYPE_IMAGES || derivedType == TYPE_VIDEO || derivedType == TYPE_AUDIO
+                || derivedType == TYPE_RECENTS || derivedType == TYPE_DOWNLOADS;
+    }
+
     @Override
     public String toString() {
         return "Root{authority=" + authority + ", rootId=" + rootId + ", title=" + title + "}";
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 2c8937a..2f0030c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -346,7 +346,7 @@
         // Return early if there is no running task (can't determine affiliated tasks in this case)
         if (runningTask == null) return;
         // Return early if the running task is in the home stack (optimization)
-        if (ssp.isInHomeStack(runningTask.id)) return;
+        if (SystemServicesProxy.isHomeStack(runningTask.stackId)) return;
 
         // Find the task in the recents list
         ArrayList<Task> tasks = focusedStack.getTasks();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 221af15..e7fca6e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -247,8 +247,10 @@
                 return true;
             }
 
+            // Note, this is only valid because we currently only allow the recents and home
+            // activities in the home stack
             if (isHomeTopMost != null) {
-                isHomeTopMost.value = isInHomeStack(topTask.id);
+                isHomeTopMost.value = SystemServicesProxy.isHomeStack(topTask.stackId);
             }
         }
         return false;
@@ -313,16 +315,18 @@
         }
     }
 
-    /** Returns whether the specified task is in the home stack */
-    public boolean isInHomeStack(int taskId) {
-        if (mAm == null) return false;
+    /**
+     * Returns whether the given stack id is the home stack id.
+     */
+    public static boolean isHomeStack(int stackId) {
+        return stackId == ActivityManager.HOME_STACK_ID;
+    }
 
-        // If we are mocking, then just return false
-        if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
-            return false;
-        }
-
-        return mAm.isInHomeStack(taskId);
+    /**
+     * Returns whether the given stack id is the freeform workspace stack id.
+     */
+    public static boolean isFreeformStack(int stackId) {
+        return stackId == ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 99539e4..1cd3758 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -21,6 +21,7 @@
 import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.HOME_STACK_ID;
+import static android.app.ActivityManager.INVALID_STACK_ID;
 import static android.app.ActivityManager.LAST_STATIC_STACK_ID;
 import static android.app.ActivityManager.PINNED_STACK_ID;
 import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
@@ -4375,6 +4376,7 @@
 
             RunningTaskInfo ci = new RunningTaskInfo();
             ci.id = task.taskId;
+            ci.stackId = (task.stack == null) ? INVALID_STACK_ID : task.stack.getStackId();
             ci.baseActivity = r.intent.getComponent();
             ci.topActivity = top.intent.getComponent();
             ci.lastActiveTime = task.lastActiveTime;