Support niceness-based priority calls

Cache niceness values rather than Java priorities, so that we can also
cache priorities set by frameworks code. Add the necessary priority
to niceness and vice-versa conversion infrastructure.

Cache the correct niceness value for system daemons, ensuring that with
the normal priority assignments this still reads back as the same
Java priority value.

Add an API to access the niceness value directly, instead of via Java
priorities.

Preserve the old Thread.priority field, but only to support apps that
read it directly via reflection.

See also go/thread-priorities-in-android and aosp/3287177 .

This is intended to be followed by aosp/3312350 and an updated version
of aosp/3314070 .

Test: Treehugger
Test: atest CtsLibcoreTestCases:libcore.java.lang.ThreadTest

Bug: 389104950
Change-Id: I9497c6c5b1d46402ff747e6f20166385e211dc75
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 064d35b..604af66 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -338,6 +338,7 @@
     method public static String getInstructionSet(String);
     method public static dalvik.system.VMRuntime getRuntime();
     method public int getTargetSdkVersion();
+    method @FlaggedApi("com.android.libcore.niceness_api") public int getThreadNiceness(Thread);
     method public boolean is64Bit();
     method public static boolean is64BitAbi(String);
     method public static boolean is64BitInstructionSet(String);
@@ -366,6 +367,7 @@
     method public static void setProcessDataDirectory(String);
     method public static void setProcessPackageName(String);
     method public void setTargetSdkVersion(int);
+    method @FlaggedApi("com.android.libcore.niceness_api") public boolean setThreadNiceness(Thread, int);
     method public void updateProcessState(int);
     method public String vmInstructionSet();
     method public String vmLibrary();
diff --git a/libart/src/main/java/dalvik/system/VMRuntime.java b/libart/src/main/java/dalvik/system/VMRuntime.java
index 88cf8a3..a11bbe5 100644
--- a/libart/src/main/java/dalvik/system/VMRuntime.java
+++ b/libart/src/main/java/dalvik/system/VMRuntime.java
@@ -28,6 +28,7 @@
 import com.android.libcore.Flags;
 
 import dalvik.annotation.compat.VersionCodes;
+import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
 
 import java.lang.ref.FinalizerReference;
@@ -801,6 +802,43 @@
     }
 
     /**
+     * Returns t's Posix niceness, as cached by {@code Thread}. Only reflects values
+     * set via java's Thread API, or via {@code setThreadNiceness()} below. Does not reflect
+     * values set directly using Posix calls in JNI code or by another process.
+     *
+     * @param t the threaad being queried
+     * @return Linux niceness value for t
+     *
+     * @hide
+     */
+    @android.annotation.FlaggedApi(com.android.libcore.Flags.FLAG_NICENESS_API)
+    @SystemApi(client = MODULE_LIBRARIES)
+    public int getThreadNiceness(Thread t) {
+      return t.getPosixNicenessInternal();
+    }
+
+    /**
+     * Sets t's Posix niceness, updating {@code Thread}'s cache. Subsequently t's
+     * Java thread priority will appear to be the nearest corresponding Java thread priority.
+     *
+     * @param t the thread whose niceness is to be set
+     * @param newNiceness new Linux niceness value, in the range of -20 to 19
+     * @return true on success. Attempts to set niceness on unstarted threads succeed
+     *         even if the deferred system call later fails.         .
+     *
+     * @hide
+     */
+    @android.annotation.FlaggedApi(com.android.libcore.Flags.FLAG_NICENESS_API)
+    @SystemApi(client = MODULE_LIBRARIES)
+    public boolean setThreadNiceness(Thread t, int newNiceness) {
+      if (newNiceness < -20 || newNiceness > 19) {
+        return false;
+      }
+      return t.setPosixNicenessInternal(newNiceness) == 0;
+    }
+
+
+    /**
      * Request that a garbage collection gets started on a different thread.
      *
      * @hide
@@ -1025,7 +1063,8 @@
      *
      * @hide
      */
-    public static native void setSystemDaemonThreadPriority();
+    @CriticalNative
+    public static native int getSystemDaemonNiceness();
 
     /**
      * Sets a callback that the runtime can call whenever a usage of a non SDK API is detected.
diff --git a/libart/src/main/java/java/lang/Daemons.java b/libart/src/main/java/java/lang/Daemons.java
index ba38844..c9f0bd5 100644
--- a/libart/src/main/java/java/lang/Daemons.java
+++ b/libart/src/main/java/java/lang/Daemons.java
@@ -125,7 +125,8 @@
                 // Thread.start() will call SetNativePriority and overwrite the desired native
                 // priority. We (may) use a native priority that doesn't have a corresponding
                 // java.lang.Thread-level priority (native priorities are more coarse-grained.)
-                VMRuntime.getRuntime().setSystemDaemonThreadPriority();
+                Thread.currentThread().setPosixNicenessInternal(
+                    VMRuntime.getRuntime().getSystemDaemonNiceness());
             }
             zygoteStartLatch.countDown();
             try {
diff --git a/libcore.aconfig b/libcore.aconfig
index 51c49b7..1fff8b6 100644
--- a/libcore.aconfig
+++ b/libcore.aconfig
@@ -135,3 +135,14 @@
     # APIs provided by a mainline module can only use a frozen flag.
     is_fixed_read_only: true
 }
+
+flag {
+    namespace: "core_libraries"
+    name: "niceness_api"
+    is_exported: true
+    description: "Java APIs get/set niceness"
+    bug: "389104950"
+    # APIs provided by a mainline module can only use a frozen flag.
+    is_fixed_read_only: true
+}
+
diff --git a/luni/src/test/java/libcore/java/lang/ThreadTest.java b/luni/src/test/java/libcore/java/lang/ThreadTest.java
index 7b0dec9..d89ff18 100644
--- a/luni/src/test/java/libcore/java/lang/ThreadTest.java
+++ b/luni/src/test/java/libcore/java/lang/ThreadTest.java
@@ -179,6 +179,7 @@
         Thread thread = new Thread("starting thread") {
             @Override public void run() { try { latch.await(); } catch (Exception e) { } }
         };
+        assertEquals(Thread.currentThread().getPriority(), thread.getPriority());
         // priority set while thread was not started should carry over to started thread
         int priority = thread.getPriority() + 1;
         if (priority > Thread.MAX_PRIORITY) {
diff --git a/ojluni/annotations/hiddenapi/java/lang/Thread.java b/ojluni/annotations/hiddenapi/java/lang/Thread.java
index 235743c..a7cb283 100644
--- a/ojluni/annotations/hiddenapi/java/lang/Thread.java
+++ b/ojluni/annotations/hiddenapi/java/lang/Thread.java
@@ -183,10 +183,18 @@
         throw new RuntimeException("Stub!");
     }
 
+    public final int setPosixNicenessInternal(int newNiceness) {
+        throw new RuntimeException("Stub!");
+    }
+
     public final int getPriority() {
         throw new RuntimeException("Stub!");
     }
 
+    public final int getPosixNicenessInternal() {
+        throw new RuntimeException("Stub!");
+    }
+
     public final synchronized void setName(java.lang.String name) {
         throw new RuntimeException("Stub!");
     }
@@ -319,7 +327,13 @@
         throw new RuntimeException("Stub!");
     }
 
-    private native void setPriority0(int newPriority);
+    native void setPriority0(int newPriority);
+
+    private native int setNiceness0(int niceness);
+
+    public static native int priorityForNiceness(int niceness);
+
+    public static native int nicenessForPriority(int priority);
 
     private native void interrupt0();
 
@@ -402,6 +416,8 @@
     @UnsupportedAppUsage
     private volatile long nativePeer;
 
+    private int niceness;
+
     @UnsupportedAppUsage
     volatile java.lang.Object parkBlocker;
 
diff --git a/ojluni/src/main/java/java/lang/Thread.java b/ojluni/src/main/java/java/lang/Thread.java
index effb7c8..e9fc480 100644
--- a/ojluni/src/main/java/java/lang/Thread.java
+++ b/ojluni/src/main/java/java/lang/Thread.java
@@ -26,6 +26,7 @@
 
 package java.lang;
 
+import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
 import java.lang.ref.Reference;
 import java.lang.ref.ReferenceQueue;
@@ -167,7 +168,15 @@
     // END Android-added: Android specific fields lock, nativePeer.
 
     private volatile String name;
-    private int priority;
+
+    // Android-changed: Cache Posix niceness instead of managed thread priority.
+    private int niceness;
+
+    private int priority;  // Only for reading via reflection. Avoid.
+
+    // cachedPriorityForNiceness[n + PFN_INDEX_OFFSET] = 0 or Java priority for niceness n.
+    private static final byte[] cachedPriorityForNiceness = new byte[40];
+    static final int PFN_INDEX_OFFSET = 20;
 
     /* Whether or not to single_step this thread. */
     private boolean     single_step;
@@ -558,7 +567,8 @@
 
         this.group = g;
         this.daemon = parent.isDaemon();
-        this.priority = parent.getPriority();
+        this.niceness = parent.getPosixNicenessInternal();
+        this.priority = parent.priority;
         // Android-changed: Moved into init2(Thread, boolean) helper method.
         /*
         if (security == null || isCCLOverridden(parent.getClass()))
@@ -787,7 +797,7 @@
 
     // BEGIN Android-added: Private constructor - used by the runtime.
     /** @hide */
-    Thread(ThreadGroup group, String name, int priority, boolean daemon) {
+    Thread(ThreadGroup group, String name, int niceness, boolean daemon) {
         this.group = group;
         this.group.addUnstarted();
         // Must be tolerant of threads without a name.
@@ -800,7 +810,8 @@
         // undesirable to clobber their natively set name.
         this.name = name;
 
-        this.priority = priority;
+        this.niceness = niceness;
+        this.priority = cachingPriorityForNiceness(niceness);
         this.daemon = daemon;
         init2(currentThread(), true);
         this.stackSize = 0;
@@ -1449,22 +1460,72 @@
             // Android-changed: Avoid native call if Thread is not yet started.
             // setPriority0(priority = newPriority);
             synchronized(this) {
-                this.priority = newPriority;
+                this.priority = newPriority;  // Ignored by us.
                 if (isAlive()) {
-                    setPriority0(newPriority);
+                    this.niceness = setPriority0(newPriority);
+                } else {
+                    this.niceness = nicenessForPriority(newPriority);
                 }
             }
         }
     }
 
     /**
+     * Android-added: An internal version of setPriority that takes niceness rather than priority.
+     * We do not bounds check. This does affect getPriority() calls. The results of such
+     * getPriority() will be limited to [MIN_PRIORITY, MAX_PRIORITY] even if the actual niceness
+     * value is outside that range. Such a value may be outside the ThreadGroup limit.
+     *
+     * @return  Linux errno, 0 on success or if thread has not yet been started.
+     *
+     * @hide
+     */
+    public final int setPosixNicenessInternal(int newNiceness) {
+        synchronized(this) {
+            this.niceness = newNiceness;
+            // Don't bother setting priority field here; it's only for backward compatibility, and
+            // historically we didn't set priority in this case.
+            if (isAlive()) {
+                return setNiceness0(newNiceness);
+            }
+        }
+        return 0;
+    }
+
+    /**
+     * Android-added: Fast niceness to priority conversion.
+     * Return Java priority for Posix niceness `n`, caching previously computed results.
+     */
+    private int cachingPriorityForNiceness(int n) {
+        byte[] pfn = cachedPriorityForNiceness;
+        int p = cachedPriorityForNiceness[n + PFN_INDEX_OFFSET];
+        if (p == 0) {
+            p = priorityForNiceness(n);
+            // Data race here is OK by Java rules.
+            cachedPriorityForNiceness[n + PFN_INDEX_OFFSET] = (byte) p;
+        }
+        return p;
+    }
+
+    /**
      * Returns this thread's priority.
      *
      * @return  this thread's priority.
      * @see     #setPriority
      */
     public final int getPriority() {
-        return priority;
+        // Android-changed: Convert from stored niceness.
+        return cachingPriorityForNiceness(niceness);
+    }
+
+    /**
+     * Android-added: Access to cached niceness value.
+     * Returns this thread's cached niceness value.
+     *
+     * @hide
+     */
+    public final int getPosixNicenessInternal() {
+      return niceness;
     }
 
     /**
@@ -2562,9 +2623,44 @@
 
     /* Some private helper methods */
     /**
-     * Android-changed: Make accessible to Daemons.java for internal use.
+     * Android-changed: Make accessible to Daemons.java for internal use. Return signed niceness
+     * value corresponding to newPriority. The argument is still a Java priority.
+     *
+     * Equivalent to
+     *
+     *   int n = nicenessForPriority(newPriority);
+     *   setNiceness0(nicenessForPriority(n));
+     *   return n;
+     *
+     * But it allows us to implement Thread.setPriority() with a single native call.
      */
-    native void setPriority0(int newPriority);
+    native int setPriority0(int newPriority);
+
+    /**
+     * Android-added: Helper methods allowing us to understand the priority to niceness mapping,
+     * so that we can process franeworks requests trafficking in niceness as well.
+     */
+
+    /**
+     * setPriority0, but with nicenes argument and returns an errno value.
+     */
+    private native int setNiceness0(int niceness);
+
+    /**
+     * A somewhat inefficient way to map Linux niceness to Java priority. We cache the results here.
+     * @hide
+     */
+    // VisibleForTesting
+    @CriticalNative
+    public static native int priorityForNiceness(int niceness);
+
+    /**
+     * A more efficient way to map Java priority to Posix niceness.
+     * @hide
+     */
+    // VisibleForTesting
+    @CriticalNative
+    public static native int nicenessForPriority(int priority);
 
     // BEGIN Android-removed: Native methods that are unused on Android.
     /*