Merge change I0743a389 into eclair-mr2

* changes:
  Close popup when Keyboard gets a CANCEL motion event.
diff --git a/include/binder/IBinder.h b/include/binder/IBinder.h
index 884b5c1..749a977 100644
--- a/include/binder/IBinder.h
+++ b/include/binder/IBinder.h
@@ -52,7 +52,7 @@
         DUMP_TRANSACTION        = B_PACK_CHARS('_','D','M','P'),
         INTERFACE_TRANSACTION   = B_PACK_CHARS('_', 'N', 'T', 'F'),
 
-        // Corresponds to tfOneWay -- an asynchronous call.
+        // Corresponds to TF_ONE_WAY -- an asynchronous call.
         FLAG_ONEWAY             = 0x00000001
     };
 
diff --git a/include/binder/IPCThreadState.h b/include/binder/IPCThreadState.h
index 78306b2..3ab985d 100644
--- a/include/binder/IPCThreadState.h
+++ b/include/binder/IPCThreadState.h
@@ -68,6 +68,13 @@
 
     static  void                shutdown();
     
+    // Call this to disable switching threads to background scheduling when
+    // receiving incoming IPC calls.  This is specifically here for the
+    // Android system process, since it expects to have background apps calling
+    // in to it but doesn't want to acquire locks in its services while in
+    // the background.
+    static  void                disableBackgroundScheduling(bool disable);
+    
 private:
                                 IPCThreadState();
                                 ~IPCThreadState();
@@ -93,9 +100,10 @@
                                            void* cookie);
     
     const   sp<ProcessState>    mProcess;
+    const   pid_t               mMyThreadId;
             Vector<BBinder*>    mPendingStrongDerefs;
             Vector<RefBase::weakref_type*> mPendingWeakDerefs;
-                                
+            
             Parcel              mIn;
             Parcel              mOut;
             status_t            mLastError;
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h
index 49145e8..a845908 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/utils/ResourceTypes.h
@@ -393,7 +393,10 @@
     enum {
         // If set, the string index is sorted by the string values (based
         // on strcmp16()).
-        SORTED_FLAG = 1<<0
+        SORTED_FLAG = 1<<0,
+
+        // String pool is encoded in UTF-8
+        UTF8_FLAG = 1<<8
     };
     uint32_t flags;
 
@@ -456,9 +459,11 @@
     void*                       mOwnedData;
     const ResStringPool_header* mHeader;
     size_t                      mSize;
+    mutable Mutex               mDecodeLock;
     const uint32_t*             mEntries;
     const uint32_t*             mEntryStyles;
-    const char16_t*             mStrings;
+    const void*                 mStrings;
+    char16_t**                  mCache;
     uint32_t                    mStringPoolSize;    // number of uint16_t
     const uint32_t*             mStyles;
     uint32_t                    mStylePoolSize;    // number of uint32_t
diff --git a/include/utils/String16.h b/include/utils/String16.h
index a2d22ee..07a0c11 100644
--- a/include/utils/String16.h
+++ b/include/utils/String16.h
@@ -49,12 +49,17 @@
 // Version of strzcmp16 for comparing strings in different endianness.
 int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2);
 
+// Convert UTF-8 to UTF-16 including surrogate pairs
+void utf8_to_utf16(const uint8_t *src, size_t srcLen, char16_t* dst, const size_t dstLen);
+
 }
 
 // ---------------------------------------------------------------------------
 
 namespace android {
 
+// ---------------------------------------------------------------------------
+
 class String8;
 class TextOutput;
 
diff --git a/include/utils/String8.h b/include/utils/String8.h
index ecc5774..c4b18a4 100644
--- a/include/utils/String8.h
+++ b/include/utils/String8.h
@@ -60,6 +60,11 @@
 /*
  * Returns the UTF-8 length of "src".
  */
+size_t utf8_length_from_utf16(const char16_t *src, size_t src_len);
+
+/*
+ * Returns the UTF-8 length of "src".
+ */
 size_t utf8_length_from_utf32(const char32_t *src, size_t src_len);
 
 /*
@@ -120,6 +125,9 @@
 size_t utf32_to_utf8(const char32_t* src, size_t src_len,
                      char* dst, size_t dst_len);
 
+size_t utf16_to_utf8(const char16_t* src, size_t src_len,
+                     char* dst, size_t dst_len);
+
 }
 
 // ---------------------------------------------------------------------------
diff --git a/include/utils/threads.h b/include/utils/threads.h
index 0fc533f..130d83c 100644
--- a/include/utils/threads.h
+++ b/include/utils/threads.h
@@ -124,6 +124,24 @@
 
 extern void androidSetCreateThreadFunc(android_create_thread_fn func);
 
+// ------------------------------------------------------------------
+// Extra functions working with raw pids.
+
+// Get pid for the current thread.
+extern pid_t androidGetTid();
+
+// Change the scheduling group of a particular thread.  The group
+// should be one of the ANDROID_TGROUP constants.  Returns BAD_VALUE if
+// grp is out of range, else another non-zero value with errno set if
+// the operation failed.
+extern int androidSetThreadSchedulingGroup(pid_t tid, int grp);
+
+// Change the priority AND scheduling group of a particular thread.  The priority
+// should be one of the ANDROID_PRIORITY constants.  Returns INVALID_OPERATION
+// if the priority set failed, else another value if just the group set failed;
+// in either case errno is set.
+extern int androidSetThreadPriority(pid_t tid, int prio);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index b2a7db8..473f580 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -292,6 +292,7 @@
 static bool gHaveTLS = false;
 static pthread_key_t gTLS = 0;
 static bool gShutdown = false;
+static bool gDisableBackgroundScheduling = false;
 
 IPCThreadState* IPCThreadState::self()
 {
@@ -332,6 +333,11 @@
     }
 }
 
+void IPCThreadState::disableBackgroundScheduling(bool disable)
+{
+    gDisableBackgroundScheduling = disable;
+}
+
 sp<ProcessState> IPCThreadState::process()
 {
     return mProcess;
@@ -386,6 +392,11 @@
 
     mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
     
+    // This thread may have been spawned by a thread that was in the background
+    // scheduling group, so first we will make sure it is in the default/foreground
+    // one to avoid performing an initial transaction in the background.
+    androidSetThreadSchedulingGroup(mMyThreadId, ANDROID_TGROUP_DEFAULT);
+        
     status_t result;
     do {
         int32_t cmd;
@@ -427,19 +438,13 @@
         }
         
         // After executing the command, ensure that the thread is returned to the
-        // default cgroup and priority before rejoining the pool.  This is a failsafe
-        // in case the command implementation failed to properly restore the thread's
-        // scheduling parameters upon completion.
-        int my_id;
-#ifdef HAVE_GETTID
-        my_id = gettid();
-#else
-        my_id = getpid();
-#endif
-        if (!set_sched_policy(my_id, SP_FOREGROUND)) {
-            // success; reset the priority as well
-            setpriority(PRIO_PROCESS, my_id, ANDROID_PRIORITY_NORMAL);
-        }
+        // default cgroup before rejoining the pool.  The driver takes care of
+        // restoring the priority, but doesn't do anything with cgroups so we
+        // need to take care of that here in userspace.  Note that we do make
+        // sure to go in the foreground after executing a transaction, but
+        // there are other callbacks into user code that could have changed
+        // our group so we want to make absolutely sure it is put back.
+        androidSetThreadSchedulingGroup(mMyThreadId, ANDROID_TGROUP_DEFAULT);
 
         // Let this thread exit the thread pool if it is no longer
         // needed and it is not the main process thread.
@@ -583,10 +588,10 @@
 }
 
 IPCThreadState::IPCThreadState()
-    : mProcess(ProcessState::self())
+    : mProcess(ProcessState::self()), mMyThreadId(androidGetTid())
 {
     pthread_setspecific(gTLS, this);
-        clearCaller();
+    clearCaller();
     mIn.setDataCapacity(256);
     mOut.setDataCapacity(256);
 }
@@ -930,6 +935,17 @@
             mCallingPid = tr.sender_pid;
             mCallingUid = tr.sender_euid;
             
+            bool doBackground = !gDisableBackgroundScheduling &&
+                    getpriority(PRIO_PROCESS, mMyThreadId)
+                            >= ANDROID_PRIORITY_BACKGROUND;
+            if (doBackground) {
+                // We have inherited a background priority from the caller.
+                // Ensure this thread is in the background scheduling class,
+                // since the driver won't modify scheduling classes for us.
+                androidSetThreadSchedulingGroup(mMyThreadId,
+                        ANDROID_TGROUP_BG_NONINTERACT);
+            }
+            
             //LOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid);
             
             Parcel reply;
@@ -967,6 +983,13 @@
             mCallingPid = origPid;
             mCallingUid = origUid;
             
+            if (doBackground) {
+                // We moved to the background scheduling group to execute
+                // this transaction, so now that we are done go back in the
+                // foreground.
+                androidSetThreadSchedulingGroup(mMyThreadId, ANDROID_TGROUP_DEFAULT);
+            }
+            
             IF_LOG_TRANSACTIONS() {
                 TextOutput::Bundle _b(alog);
                 alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj "
diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp
index 450af8d..afca814 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/utils/ResourceTypes.cpp
@@ -229,12 +229,12 @@
 // --------------------------------------------------------------------
 
 ResStringPool::ResStringPool()
-    : mError(NO_INIT), mOwnedData(NULL)
+    : mError(NO_INIT), mOwnedData(NULL), mHeader(NULL), mCache(NULL)
 {
 }
 
 ResStringPool::ResStringPool(const void* data, size_t size, bool copyData)
-    : mError(NO_INIT), mOwnedData(NULL)
+    : mError(NO_INIT), mOwnedData(NULL), mHeader(NULL), mCache(NULL)
 {
     setTo(data, size, copyData);
 }
@@ -296,7 +296,17 @@
                     (int)size);
             return (mError=BAD_TYPE);
         }
-        mStrings = (const char16_t*)
+
+        size_t charSize;
+        if (mHeader->flags&ResStringPool_header::UTF8_FLAG) {
+            charSize = sizeof(uint8_t);
+            mCache = (char16_t**)malloc(sizeof(char16_t**)*mHeader->stringCount);
+            memset(mCache, 0, sizeof(char16_t**)*mHeader->stringCount);
+        } else {
+            charSize = sizeof(char16_t);
+        }
+
+        mStrings = (const void*)
             (((const uint8_t*)data)+mHeader->stringsStart);
         if (mHeader->stringsStart >= (mHeader->header.size-sizeof(uint16_t))) {
             LOGW("Bad string block: string pool starts at %d, after total size %d\n",
@@ -305,7 +315,7 @@
         }
         if (mHeader->styleCount == 0) {
             mStringPoolSize =
-                (mHeader->header.size-mHeader->stringsStart)/sizeof(uint16_t);
+                (mHeader->header.size-mHeader->stringsStart)/charSize;
         } else {
             // check invariant: styles follow the strings
             if (mHeader->stylesStart <= mHeader->stringsStart) {
@@ -314,7 +324,7 @@
                 return (mError=BAD_TYPE);
             }
             mStringPoolSize =
-                (mHeader->stylesStart-mHeader->stringsStart)/sizeof(uint16_t);
+                (mHeader->stylesStart-mHeader->stringsStart)/charSize;
         }
 
         // check invariant: stringCount > 0 requires a string pool to exist
@@ -329,13 +339,19 @@
             for (i=0; i<mHeader->stringCount; i++) {
                 e[i] = dtohl(mEntries[i]);
             }
-            char16_t* s = const_cast<char16_t*>(mStrings);
-            for (i=0; i<mStringPoolSize; i++) {
-                s[i] = dtohs(mStrings[i]);
+            if (!(mHeader->flags&ResStringPool_header::UTF8_FLAG)) {
+                const char16_t* strings = (const char16_t*)mStrings;
+                char16_t* s = const_cast<char16_t*>(strings);
+                for (i=0; i<mStringPoolSize; i++) {
+                    s[i] = dtohs(strings[i]);
+                }
             }
         }
 
-        if (mStrings[mStringPoolSize-1] != 0) {
+        if ((mHeader->flags&ResStringPool_header::UTF8_FLAG &&
+                ((uint8_t*)mStrings)[mStringPoolSize-1] != 0) ||
+                (!mHeader->flags&ResStringPool_header::UTF8_FLAG &&
+                ((char16_t*)mStrings)[mStringPoolSize-1] != 0)) {
             LOGW("Bad string block: last string is not 0-terminated\n");
             return (mError=BAD_TYPE);
         }
@@ -410,24 +426,67 @@
         free(mOwnedData);
         mOwnedData = NULL;
     }
+    if (mHeader != NULL && mCache != NULL) {
+        for (size_t x = 0; x < mHeader->stringCount; x++) {
+            if (mCache[x] != NULL) {
+                free(mCache[x]);
+                mCache[x] = NULL;
+            }
+        }
+        free(mCache);
+        mCache = NULL;
+    }
 }
 
+#define DECODE_LENGTH(str, chrsz, len) \
+    len = *(str); \
+    if (*(str)&(1<<(chrsz*8-1))) { \
+        (str)++; \
+        len = (((len)&((1<<(chrsz*8-1))-1))<<(chrsz*8)) + *(str); \
+    } \
+    (str)++;
+
 const uint16_t* ResStringPool::stringAt(size_t idx, size_t* outLen) const
 {
     if (mError == NO_ERROR && idx < mHeader->stringCount) {
-        const uint32_t off = (mEntries[idx]/sizeof(uint16_t));
+        const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0;
+        const uint32_t off = mEntries[idx]/(isUTF8?sizeof(char):sizeof(char16_t));
         if (off < (mStringPoolSize-1)) {
-            const char16_t* str = mStrings+off;
-            *outLen = *str;
-            if ((*str)&0x8000) {
-                str++;
-                *outLen = (((*outLen)&0x7fff)<<16) + *str;
-            }
-            if ((uint32_t)(str+1+*outLen-mStrings) < mStringPoolSize) {
-                return str+1;
+            if (!isUTF8) {
+                const char16_t* strings = (char16_t*)mStrings;
+                const char16_t* str = strings+off;
+                DECODE_LENGTH(str, sizeof(char16_t), *outLen)
+                if ((uint32_t)(str+*outLen-strings) < mStringPoolSize) {
+                    return str;
+                } else {
+                    LOGW("Bad string block: string #%d extends to %d, past end at %d\n",
+                            (int)idx, (int)(str+*outLen-strings), (int)mStringPoolSize);
+                }
             } else {
-                LOGW("Bad string block: string #%d extends to %d, past end at %d\n",
-                        (int)idx, (int)(str+1+*outLen-mStrings), (int)mStringPoolSize);
+                const uint8_t* strings = (uint8_t*)mStrings;
+                const uint8_t* str = strings+off;
+                DECODE_LENGTH(str, sizeof(uint8_t), *outLen)
+                size_t encLen;
+                DECODE_LENGTH(str, sizeof(uint8_t), encLen)
+                if ((uint32_t)(str+encLen-strings) < mStringPoolSize) {
+                    AutoMutex lock(mDecodeLock);
+                    if (mCache[idx] != NULL) {
+                        return mCache[idx];
+                    }
+                    char16_t *u16str = (char16_t *)calloc(*outLen+1, sizeof(char16_t));
+                    if (!u16str) {
+                        LOGW("No memory when trying to allocate decode cache for string #%d\n",
+                                (int)idx);
+                        return NULL;
+                    }
+                    const unsigned char *u8src = reinterpret_cast<const unsigned char *>(str);
+                    utf8_to_utf16(u8src, encLen, u16str, *outLen);
+                    mCache[idx] = u16str;
+                    return u16str;
+                } else {
+                    LOGW("Bad string block: string #%d extends to %d, past end at %d\n",
+                            (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize);
+                }
             }
         } else {
             LOGW("Bad string block: string #%d entry is at %d, past end at %d\n",
@@ -466,6 +525,10 @@
 
     size_t len;
 
+    // TODO optimize searching for UTF-8 strings taking into account
+    // the cache fill to determine when to convert the searched-for
+    // string key to UTF-8.
+
     if (mHeader->flags&ResStringPool_header::SORTED_FLAG) {
         // Do a binary search for the string...
         ssize_t l = 0;
@@ -1043,6 +1106,7 @@
 void ResXMLTree::uninit()
 {
     mError = NO_INIT;
+    mStrings.uninit();
     if (mOwnedData) {
         free(mOwnedData);
         mOwnedData = NULL;
diff --git a/libs/utils/String16.cpp b/libs/utils/String16.cpp
index aef67f2..eab7b2b 100644
--- a/libs/utils/String16.cpp
+++ b/libs/utils/String16.cpp
@@ -172,10 +172,6 @@
            : 0);
 }
 
-// ---------------------------------------------------------------------------
-
-namespace android {
-
 static inline size_t
 utf8_char_len(uint8_t ch)
 {
@@ -215,8 +211,38 @@
     //printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result);
 }
 
+void
+utf8_to_utf16(const uint8_t *src, size_t srcLen,
+        char16_t* dst, const size_t dstLen)
+{
+    const uint8_t* const end = src + srcLen;
+    const char16_t* const dstEnd = dst + dstLen;
+    while (src < end && dst < dstEnd) {
+        size_t len = utf8_char_len(*src);
+        uint32_t codepoint = utf8_to_utf32((const uint8_t*)src, len);
+
+        // Convert the UTF32 codepoint to one or more UTF16 codepoints
+        if (codepoint <= 0xFFFF) {
+            // Single UTF16 character
+            *dst++ = (char16_t) codepoint;
+        } else {
+            // Multiple UTF16 characters with surrogates
+            codepoint = codepoint - 0x10000;
+            *dst++ = (char16_t) ((codepoint >> 10) + 0xD800);
+            *dst++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00);
+        }
+
+        src += len;
+    }
+    if (dst < dstEnd) {
+        *dst = 0;
+    }
+}
+
 // ---------------------------------------------------------------------------
 
+namespace android {
+
 static SharedBuffer* gEmptyStringBuf = NULL;
 static char16_t* gEmptyString = NULL;
 
@@ -260,30 +286,14 @@
         p += utf8len;
     }
     
-    SharedBuffer* buf = SharedBuffer::alloc((chars+1)*sizeof(char16_t));
+    size_t bufSize = (chars+1)*sizeof(char16_t);
+    SharedBuffer* buf = SharedBuffer::alloc(bufSize);
     if (buf) {
         p = in;
         char16_t* str = (char16_t*)buf->data();
-        char16_t* d = str;
-        while (p < end) {
-            size_t len = utf8_char_len(*p);
-            uint32_t codepoint = utf8_to_utf32((const uint8_t*)p, len);
-
-            // Convert the UTF32 codepoint to one or more UTF16 codepoints
-            if (codepoint <= 0xFFFF) {
-                // Single UTF16 character
-                *d++ = (char16_t) codepoint;
-            } else {
-                // Multiple UTF16 characters with surrogates
-                codepoint = codepoint - 0x10000;
-                *d++ = (char16_t) ((codepoint >> 10) + 0xD800);
-                *d++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00);
-            }
-
-            p += len;
-        }
-        *d = 0;
         
+        utf8_to_utf16((const uint8_t*)p, len, str, bufSize);
+
         //printf("Created UTF-16 string from UTF-8 \"%s\":", in);
         //printHexData(1, str, buf->size(), 16, 1);
         //printf("\n");
diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp
index e908ec1..3a34838 100644
--- a/libs/utils/String8.cpp
+++ b/libs/utils/String8.cpp
@@ -208,10 +208,23 @@
     return getEmptyString();
 }
 
-// Note: not dealing with expanding surrogate pairs.
 static char* allocFromUTF16(const char16_t* in, size_t len)
 {
-    return allocFromUTF16OrUTF32<char16_t, size_t>(in, len);
+    if (len == 0) return getEmptyString();
+
+    const size_t bytes = utf8_length_from_utf16(in, len);
+
+    SharedBuffer* buf = SharedBuffer::alloc(bytes+1);
+    LOG_ASSERT(buf, "Unable to allocate shared buffer");
+    if (buf) {
+        char* str = (char*)buf->data();
+
+        utf16_to_utf8(in, len, str, bytes+1);
+
+        return str;
+    }
+
+    return getEmptyString();
 }
 
 static char* allocFromUTF32(const char32_t* in, size_t len)
@@ -762,6 +775,26 @@
     return ret;
 }
 
+size_t utf8_length_from_utf16(const char16_t *src, size_t src_len)
+{
+    if (src == NULL || src_len == 0) {
+        return 0;
+    }
+    size_t ret = 0;
+    const char16_t* const end = src + src_len;
+    while (src < end) {
+        if ((*src & 0xFC00) == 0xD800 && (src + 1) < end
+                && (*++src & 0xFC00) == 0xDC00) {
+            // surrogate pairs are always 4 bytes.
+            ret += 4;
+            src++;
+        } else {
+            ret += android::utf32_to_utf8_bytes((char32_t) *src++);
+        }
+    }
+    return ret;
+}
+
 static int32_t utf32_at_internal(const char* cur, size_t *num_read)
 {
     const char first_char = *cur;
@@ -848,3 +881,33 @@
     }
     return cur - dst;
 }
+
+size_t utf16_to_utf8(const char16_t* src, size_t src_len,
+                     char* dst, size_t dst_len)
+{
+    if (src == NULL || src_len == 0 || dst == NULL || dst_len == 0) {
+        return 0;
+    }
+    const char16_t* cur_utf16 = src;
+    const char16_t* const end_utf16 = src + src_len;
+    char *cur = dst;
+    const char* const end = dst + dst_len;
+    while (cur_utf16 < end_utf16 && cur < end) {
+        char32_t utf32;
+        // surrogate pairs
+        if ((*cur_utf16 & 0xFC00) == 0xD800 && (cur_utf16 + 1) < end_utf16) {
+            utf32 = (*cur_utf16++ - 0xD800) << 10;
+            utf32 |= *cur_utf16++ - 0xDC00;
+            utf32 += 0x10000;
+        } else {
+            utf32 = (char32_t) *cur_utf16++;
+        }
+        size_t len = android::utf32_to_utf8_bytes(utf32);
+        android::utf32_to_utf8((uint8_t*)cur, utf32, len);
+        cur += len;
+    }
+    if (cur < end) {
+        *cur = '\0';
+    }
+    return cur - dst;
+}
diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp
index ec3db09..6ca2603 100644
--- a/libs/utils/Threads.cpp
+++ b/libs/utils/Threads.cpp
@@ -20,6 +20,8 @@
 #include <utils/threads.h>
 #include <utils/Log.h>
 
+#include <cutils/sched_policy.h>
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <memory.h>
@@ -269,6 +271,53 @@
     gCreateThreadFn = func;
 }
 
+pid_t androidGetTid()
+{
+#ifdef HAVE_GETTID
+    return gettid();
+#else
+    return getpid();
+#endif
+}
+
+int androidSetThreadSchedulingGroup(pid_t tid, int grp)
+{
+    if (grp > ANDROID_TGROUP_MAX || grp < 0) { 
+        return BAD_VALUE;
+    }
+
+    if (set_sched_policy(tid, (grp == ANDROID_TGROUP_BG_NONINTERACT) ?
+                                      SP_BACKGROUND : SP_FOREGROUND)) {
+        return PERMISSION_DENIED;
+    }
+    
+    return NO_ERROR;
+}
+
+int androidSetThreadPriority(pid_t tid, int pri)
+{
+    int rc = 0;
+    int lasterr = 0;
+
+    if (pri >= ANDROID_PRIORITY_BACKGROUND) {
+        rc = set_sched_policy(tid, SP_BACKGROUND);
+    } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) {
+        rc = set_sched_policy(tid, SP_FOREGROUND);
+    }
+
+    if (rc) {
+        lasterr = errno;
+    }
+
+    if (setpriority(PRIO_PROCESS, tid, pri) < 0) {
+        rc = INVALID_OPERATION;
+    } else {
+        errno = lasterr;
+    }
+    
+    return rc;
+}
+
 namespace android {
 
 /*