Merge "Restart NAT port timeout measurement when keepalive fails and other fixes"
diff --git a/cmds/keystore/keystore.cpp b/cmds/keystore/keystore.cpp
index b48be6e..bbd1a1b 100644
--- a/cmds/keystore/keystore.cpp
+++ b/cmds/keystore/keystore.cpp
@@ -363,6 +363,7 @@
                 response = writeMasterKey(pw);
             }
             if (response == NO_ERROR) {
+                memcpy(mMasterKey, masterKeyBlob.getValue(), MASTER_KEY_SIZE_BYTES);
                 setupMasterKeys();
             }
             return response;
diff --git a/cmds/keystore/test-keystore b/cmds/keystore/test-keystore
new file mode 100755
index 0000000..82b276f
--- /dev/null
+++ b/cmds/keystore/test-keystore
@@ -0,0 +1,272 @@
+#!/bin/bash
+#
+# Copyright 2011, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e
+
+prefix=$0
+log_file=$prefix.log
+baseline_file=$prefix.baseline
+
+function cleanup_output() {
+    rm -f $log_file
+    rm -f $baseline_file
+}
+
+function log() {
+    echo "$@"
+    append $log_file \# "$@"
+    append $baseline_file \# "$@"
+}
+
+function expect() {
+    append $baseline_file "$@"
+}
+
+function append() {
+    declare -r file=$1
+    shift
+    echo "$@" >> $file
+}
+
+function run() {
+    # strip out carriage returns from adb
+    # strip out date/time from ls -l
+    "$@" | tr --delete '\r' | sed -E 's/[0-9]{4}-[0-9]{2}-[0-9]{2} +[0-9]{1,2}:[0-9]{2} //' >> $log_file
+}
+
+function keystore() {
+    declare -r user=$1
+    shift
+    run adb shell su $user keystore_cli "$@"
+}
+
+function list_keystore_directory() {
+    run adb shell ls -al /data/misc/keystore
+}
+
+function compare() {
+    log "comparing $baseline_file and $log_file"
+    diff $baseline_file $log_file || (log $tag FAILED && exit 1)
+}
+
+function test_basic() {
+
+    #
+    # reset
+    #
+    log "reset keystore as system user"
+    keystore system r
+    expect "1 No error"
+    list_keystore_directory
+
+    #
+    # basic tests as system/root
+    #
+    log "root does not have permission to run test"
+    keystore root t
+    expect "6 Permission denied"
+    
+    log "but system user does"
+    keystore system t
+    expect "3 Uninitialized"
+    list_keystore_directory
+
+    log "password is now bar"
+    keystore system p bar
+    expect "1 No error"
+    list_keystore_directory
+    expect "-rw------- keystore keystore       84 .masterkey"
+    
+    log "no error implies initialized and unlocked"
+    keystore system t
+    expect "1 No error"
+    
+    log "saw with no argument"
+    keystore system s
+    expect "5 Protocol error"
+
+    log "saw nothing"
+    keystore system s ""
+    expect "1 No error"
+
+    log "add key baz"
+    keystore system i baz quux
+    expect "1 No error"
+
+    log "1000 is uid of system"
+    list_keystore_directory
+    expect "-rw------- keystore keystore       84 .masterkey"
+    expect "-rw------- keystore keystore       52 1000_baz"
+
+    log "saw baz"
+    keystore system s ""
+    expect "1 No error"
+    expect "baz"
+
+    log "system does not have access to read any keys"
+    keystore system g baz
+    expect "6 Permission denied"
+    
+    log "however, root can read system user keys (as can wifi or vpn users)"
+    keystore root g baz
+    expect "1 No error"
+    expect "quux"
+
+    #
+    # app user tests
+    #
+
+    # app_0 has uid 10000, as seen below
+    log "other uses cannot see the system keys"
+    keystore app_0 g baz
+    expect "7 Key not found"
+    
+    log "app user cannot use reset, password, lock, unlock"
+    keystore app_0 r
+    expect "6 Permission denied"
+    keystore app_0 p
+    expect "6 Permission denied"
+    keystore app_0 l
+    expect "6 Permission denied"
+    keystore app_0 u
+    expect "6 Permission denied"
+
+    log "install app_0 key"
+    keystore app_0 i 0x deadbeef
+    expect 1 No error
+    list_keystore_directory
+    expect "-rw------- keystore keystore       84 .masterkey"
+    expect "-rw------- keystore keystore       52 10000_0x"
+    expect "-rw------- keystore keystore       52 1000_baz"
+
+    log "get with no argument"
+    keystore app_0 g
+    expect "5 Protocol error"
+    
+    keystore app_0 g 0x
+    expect "1 No error"
+    expect "deadbeef"
+    
+    keystore app_0 i fred barney
+    expect "1 No error"
+    
+    keystore app_0 s ""
+    expect "1 No error"
+    expect "0x"
+    expect "fred"
+
+    log "note that saw returns the suffix of prefix matches"
+    keystore app_0 s fr # fred
+    expect "1 No error"
+    expect "ed" # fred
+
+    #
+    # lock tests
+    #
+    log "lock the store as system"
+    keystore system l
+    expect "1 No error"
+    keystore system t
+    expect "2 Locked"
+    
+    log "saw works while locked"
+    keystore app_0 s ""
+    expect "1 No error"
+    expect "0x"
+    expect "fred"
+
+    log "...but cannot read keys..."
+    keystore app_0 g 0x
+    expect "2 Locked"
+    
+    log "...but they can be deleted."
+    keystore app_0 e 0x
+    expect "1 No error"
+    keystore app_0 d 0x
+    expect "1 No error"
+    keystore app_0 e 0x
+    expect "7 Key not found"
+
+    #
+    # password
+    #
+    log "wrong password"
+    keystore system u foo
+    expect "13 Wrong password (4 tries left)"
+    log "right password"
+    keystore system u bar
+    expect "1 No error"
+    
+    log "make the password foo"
+    keystore system p foo
+    expect "1 No error"
+    
+    #
+    # final reset
+    #
+    log "reset wipes everything for all users"
+    keystore system r
+    expect "1 No error"
+    list_keystore_directory
+    
+    keystore system t
+    expect "3 Uninitialized"
+
+}
+
+function test_4599735() {
+    # http://b/4599735
+    log "start regression test for b/4599735"
+    keystore system r
+    expect "1 No error"
+
+    keystore system p foo
+    expect "1 No error"
+
+    keystore system i baz quux
+    expect "1 No error"
+    
+    keystore root g baz
+    expect "1 No error"
+    expect "quux"
+
+    keystore system l
+    expect "1 No error"
+
+    keystore system p foo
+    expect "1 No error"
+
+    log "after unlock, regression led to result of '8 Value corrupted'"
+    keystore root g baz
+    expect "1 No error"
+    expect "quux"
+
+    keystore system r
+    expect "1 No error"
+    log "end regression test for b/4599735"
+}
+
+function main() {
+    cleanup_output
+    log $tag START
+    test_basic
+    test_4599735
+    compare
+    log $tag PASSED
+    cleanup_output
+}
+
+main
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index 9294df6..e558dfd 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -56,7 +56,7 @@
 
     // tex indicates the name OpenGL texture to which images are to be streamed.
     // This texture name cannot be changed once the SurfaceTexture is created.
-    SurfaceTexture(GLuint tex);
+    SurfaceTexture(GLuint tex, bool allowSynchronousMode = true);
 
     virtual ~SurfaceTexture();
 
@@ -361,6 +361,9 @@
     // mSynchronousMode whether we're in synchronous mode or not
     bool mSynchronousMode;
 
+    // mAllowSynchronousMode whether we allow synchronous mode or not
+    const bool mAllowSynchronousMode;
+
     // mDequeueCondition condition used for dequeueBuffer in synchronous mode
     mutable Condition mDequeueCondition;
 
diff --git a/include/gui/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h
index e7c6e24..6ce44fc 100644
--- a/include/gui/SurfaceTextureClient.h
+++ b/include/gui/SurfaceTextureClient.h
@@ -80,6 +80,7 @@
     int setUsage(uint32_t reqUsage);
 
     void freeAllBuffers();
+    int getSlotFromBufferLocked(android_native_buffer_t* buffer) const;
 
     int getConnectedApi() const;
 
diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h
index 8845dc9..dc2a845 100644
--- a/include/surfaceflinger/Surface.h
+++ b/include/surfaceflinger/Surface.h
@@ -40,6 +40,7 @@
 class GraphicBuffer;
 class GraphicBufferMapper;
 class IOMX;
+class ISurfaceTexture;
 class Rect;
 class Surface;
 class SurfaceComposerClient;
@@ -154,6 +155,7 @@
     bool        isValid();
     uint32_t    getFlags() const    { return mFlags; }
     uint32_t    getIdentity() const { return mIdentity; }
+    sp<ISurfaceTexture> getSurfaceTexture();
 
     // the lock/unlock APIs must be used from the same thread
     status_t    lock(SurfaceInfo* info, bool blocking = true);
diff --git a/include/utils/BlobCache.h b/include/utils/BlobCache.h
index 8f76d72..dc45ff0 100644
--- a/include/utils/BlobCache.h
+++ b/include/utils/BlobCache.h
@@ -82,6 +82,9 @@
     BlobCache(const BlobCache&);
     void operator=(const BlobCache&);
 
+    // A random function helper to get around MinGW not having nrand48()
+    long int blob_random();
+
     // clean evicts a randomly chosen set of entries from the cache such that
     // the total size of all remaining entries is less than mMaxTotalSize/2.
     void clean();
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 4d1d923..9185e1e 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -421,6 +421,10 @@
     return NO_ERROR;
 }
 
+sp<ISurfaceTexture> Surface::getSurfaceTexture() {
+    return mSurface != NULL ? mSurface->getSurfaceTexture() : NULL;
+}
+
 sp<IBinder> Surface::asBinder() const {
     return mSurface!=0 ? mSurface->asBinder() : 0;
 }
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 3cecdb4..37e6d11 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -78,7 +78,7 @@
 
 static void mtxMul(float out[16], const float a[16], const float b[16]);
 
-SurfaceTexture::SurfaceTexture(GLuint tex) :
+SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode) :
     mDefaultWidth(1),
     mDefaultHeight(1),
     mPixelFormat(PIXEL_FORMAT_RGBA_8888),
@@ -91,7 +91,8 @@
     mCurrentTimestamp(0),
     mNextTransform(0),
     mTexName(tex),
-    mSynchronousMode(false) {
+    mSynchronousMode(false),
+    mAllowSynchronousMode(allowSynchronousMode) {
     LOGV("SurfaceTexture::SurfaceTexture");
     sp<ISurfaceComposer> composer(ComposerService::getComposerService());
     mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
@@ -371,6 +372,9 @@
     Mutex::Autolock lock(mMutex);
 
     status_t err = OK;
+    if (!mAllowSynchronousMode && enabled)
+        return err;
+
     if (!enabled) {
         // going to asynchronous mode, drain the queue
         while (mSynchronousMode != enabled && !mQueue.isEmpty()) {
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index 22b0852..b9b2310 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -143,12 +143,40 @@
 int SurfaceTextureClient::cancelBuffer(android_native_buffer_t* buffer) {
     LOGV("SurfaceTextureClient::cancelBuffer");
     Mutex::Autolock lock(mMutex);
+    int i = getSlotFromBufferLocked(buffer);
+    if (i < 0) {
+        return i;
+    }
+    mSurfaceTexture->cancelBuffer(i);
+    return OK;
+}
+
+int SurfaceTextureClient::getSlotFromBufferLocked(
+        android_native_buffer_t* buffer) const {
+    bool dumpedState = false;
     for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
-        if (mSlots[i]->handle == buffer->handle) {
-            mSurfaceTexture->cancelBuffer(i);
-            return OK;
+        // XXX: Dump the slots whenever we hit a NULL entry while searching for
+        // a buffer.
+        if (mSlots[i] == NULL) {
+            if (!dumpedState) {
+                LOGD("getSlotFromBufferLocked: encountered NULL buffer in slot %d "
+                        "looking for buffer %p", i, buffer->handle);
+                for (int j = 0; j < NUM_BUFFER_SLOTS; j++) {
+                    if (mSlots[j] == NULL) {
+                        LOGD("getSlotFromBufferLocked:   %02d: NULL", j);
+                    } else {
+                        LOGD("getSlotFromBufferLocked:   %02d: %p", j, mSlots[j]->handle);
+                    }
+                }
+                dumpedState = true;
+            }
+        }
+
+        if (mSlots[i] != NULL && mSlots[i]->handle == buffer->handle) {
+            return i;
         }
     }
+    LOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle);
     return BAD_VALUE;
 }
 
@@ -169,13 +197,12 @@
     } else {
         timestamp = mTimestamp;
     }
-    for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
-        if (mSlots[i]->handle == buffer->handle) {
-            return mSurfaceTexture->queueBuffer(i, timestamp);
-        }
+    int i = getSlotFromBufferLocked(buffer);
+    if (i < 0) {
+        return i;
     }
-    LOGE("queueBuffer: unknown buffer queued");
-    return BAD_VALUE;
+    mSurfaceTexture->queueBuffer(i, timestamp);
+    return OK;
 }
 
 int SurfaceTextureClient::query(int what, int* value) const {
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index a4c5b36..519b40e 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -590,7 +590,7 @@
 
 // This test verifies that the buffer format can be queried immediately after
 // it is set.
-TEST_F(SurfaceTextureClientTest, DISABLED_QueryFormatAfterSettingWorks) {
+TEST_F(SurfaceTextureClientTest, QueryFormatAfterSettingWorks) {
     sp<ANativeWindow> anw(mSTC);
     int fmts[] = {
         // RGBA_8888 should not come first, as it's the default
diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp
index 50af3bb..f219639 100644
--- a/libs/gui/tests/SurfaceTexture_test.cpp
+++ b/libs/gui/tests/SurfaceTexture_test.cpp
@@ -173,11 +173,11 @@
     }
 
     virtual EGLint getSurfaceWidth() {
-        return 64;
+        return 512;
     }
 
     virtual EGLint getSurfaceHeight() {
-        return 64;
+        return 512;
     }
 
     void loadShader(GLenum shaderType, const char* pSource, GLuint* outShader) {
@@ -526,27 +526,24 @@
     glClearColor(0.2, 0.2, 0.2, 0.2);
     glClear(GL_COLOR_BUFFER_BIT);
 
+    glViewport(0, 0, texWidth, texHeight);
     drawTexture();
 
     EXPECT_TRUE(checkPixel( 0,  0, 255, 127, 255, 255));
     EXPECT_TRUE(checkPixel(63,  0,   0, 133,   0, 255));
-    EXPECT_TRUE(checkPixel(63, 63,   0, 133,   0, 255));
-    EXPECT_TRUE(checkPixel( 0, 63, 255, 127, 255, 255));
+    EXPECT_TRUE(checkPixel(63, 65,   0, 133,   0, 255));
+    EXPECT_TRUE(checkPixel( 0, 65, 255, 127, 255, 255));
 
-    EXPECT_TRUE(checkPixel(22, 44, 247,  70, 255, 255));
-    EXPECT_TRUE(checkPixel(45, 52, 209,  32, 235, 255));
-    EXPECT_TRUE(checkPixel(52, 51, 100, 255,  73, 255));
+    EXPECT_TRUE(checkPixel(22, 44, 255, 127, 255, 255));
+    EXPECT_TRUE(checkPixel(45, 52, 255, 127, 255, 255));
+    EXPECT_TRUE(checkPixel(52, 51,  98, 255,  73, 255));
     EXPECT_TRUE(checkPixel( 7, 31, 155,   0, 118, 255));
-    EXPECT_TRUE(checkPixel(31,  9, 148,  71, 110, 255));
+    EXPECT_TRUE(checkPixel(31,  9, 107,  24,  87, 255));
     EXPECT_TRUE(checkPixel(29, 35, 255, 127, 255, 255));
     EXPECT_TRUE(checkPixel(36, 22, 155,  29,   0, 255));
 }
 
-// XXX: This test is disabled because it it currently broken on all devices to
-// which I have access.  Some of the checkPixel calls are not correct because
-// I just copied them from the npot test above and haven't bothered to figure
-// out the correct values.
-TEST_F(SurfaceTextureGLTest, DISABLED_TexturingFromCpuFilledYV12BufferPow2) {
+TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferPow2) {
     const int texWidth = 64;
     const int texHeight = 64;
 
@@ -574,20 +571,21 @@
     glClearColor(0.2, 0.2, 0.2, 0.2);
     glClear(GL_COLOR_BUFFER_BIT);
 
+    glViewport(0, 0, texWidth, texHeight);
     drawTexture();
 
-    EXPECT_TRUE(checkPixel( 0,  0, 255, 127, 255, 255));
-    EXPECT_TRUE(checkPixel(63,  0,   0, 133,   0, 255));
+    EXPECT_TRUE(checkPixel( 0,  0,   0, 133,   0, 255));
+    EXPECT_TRUE(checkPixel(63,  0, 255, 127, 255, 255));
     EXPECT_TRUE(checkPixel(63, 63,   0, 133,   0, 255));
     EXPECT_TRUE(checkPixel( 0, 63, 255, 127, 255, 255));
 
-    EXPECT_TRUE(checkPixel(22, 19, 247,  70, 255, 255));
-    EXPECT_TRUE(checkPixel(45, 11, 209,  32, 235, 255));
-    EXPECT_TRUE(checkPixel(52, 12, 100, 255,  73, 255));
-    EXPECT_TRUE(checkPixel( 7, 32, 155,   0, 118, 255));
-    EXPECT_TRUE(checkPixel(31, 54, 148,  71, 110, 255));
-    EXPECT_TRUE(checkPixel(29, 28, 255, 127, 255, 255));
-    EXPECT_TRUE(checkPixel(36, 41, 155,  29,   0, 255));
+    EXPECT_TRUE(checkPixel(22, 19, 100, 255,  74, 255));
+    EXPECT_TRUE(checkPixel(45, 11, 100, 255,  74, 255));
+    EXPECT_TRUE(checkPixel(52, 12, 155,   0, 181, 255));
+    EXPECT_TRUE(checkPixel( 7, 32, 150, 237, 170, 255));
+    EXPECT_TRUE(checkPixel(31, 54,   0,  71, 117, 255));
+    EXPECT_TRUE(checkPixel(29, 28,   0, 133,   0, 255));
+    EXPECT_TRUE(checkPixel(36, 41, 100, 232, 255, 255));
 }
 
 TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) {
@@ -632,6 +630,7 @@
         glClearColor(0.2, 0.2, 0.2, 0.2);
         glClear(GL_COLOR_BUFFER_BIT);
 
+        glViewport(0, 0, 64, 64);
         drawTexture();
 
         EXPECT_TRUE(checkPixel( 0,  0,  82, 255,  35, 255));
@@ -679,28 +678,29 @@
     glClearColor(0.2, 0.2, 0.2, 0.2);
     glClear(GL_COLOR_BUFFER_BIT);
 
+    glViewport(0, 0, texWidth, texHeight);
     drawTexture();
 
     EXPECT_TRUE(checkPixel( 0,  0,  35,  35,  35,  35));
     EXPECT_TRUE(checkPixel(63,  0, 231, 231, 231, 231));
-    EXPECT_TRUE(checkPixel(63, 63, 231, 231, 231, 231));
-    EXPECT_TRUE(checkPixel( 0, 63,  35,  35,  35,  35));
+    EXPECT_TRUE(checkPixel(63, 65, 231, 231, 231, 231));
+    EXPECT_TRUE(checkPixel( 0, 65,  35,  35,  35,  35));
 
     EXPECT_TRUE(checkPixel(15, 10,  35, 231, 231, 231));
-    EXPECT_TRUE(checkPixel(24, 63,  35, 231, 231,  35));
-    EXPECT_TRUE(checkPixel(19, 40,  87, 179,  35,  35));
+    EXPECT_TRUE(checkPixel(24, 63,  38, 228, 231,  35));
+    EXPECT_TRUE(checkPixel(19, 40,  35, 231,  35,  35));
     EXPECT_TRUE(checkPixel(38, 30, 231,  35,  35,  35));
     EXPECT_TRUE(checkPixel(42, 54,  35,  35,  35, 231));
-    EXPECT_TRUE(checkPixel(37, 33,  35, 231, 231, 231));
+    EXPECT_TRUE(checkPixel(37, 33, 228,  38,  38,  38));
     EXPECT_TRUE(checkPixel(31,  8, 231,  35,  35, 231));
-    EXPECT_TRUE(checkPixel(36, 47, 231,  35, 231, 231));
-    EXPECT_TRUE(checkPixel(24, 63,  35, 231, 231,  35));
-    EXPECT_TRUE(checkPixel(48,  3, 231, 231,  35,  35));
+    EXPECT_TRUE(checkPixel(36, 47, 228,  35, 231, 231));
+    EXPECT_TRUE(checkPixel(24, 63,  38, 228, 231,  35));
+    EXPECT_TRUE(checkPixel(48,  3, 228, 228,  38,  35));
     EXPECT_TRUE(checkPixel(54, 50,  35, 231, 231, 231));
-    EXPECT_TRUE(checkPixel(24, 25, 191, 191, 231, 231));
-    EXPECT_TRUE(checkPixel(10,  9,  93,  93, 231, 231));
+    EXPECT_TRUE(checkPixel(24, 25,  41,  41, 231, 231));
+    EXPECT_TRUE(checkPixel(10,  9,  38,  38, 231, 231));
     EXPECT_TRUE(checkPixel(29,  4,  35,  35,  35, 231));
-    EXPECT_TRUE(checkPixel(56, 31,  35, 231, 231,  35));
+    EXPECT_TRUE(checkPixel(56, 31,  38, 228, 231,  35));
     EXPECT_TRUE(checkPixel(58, 55,  35,  35, 231, 231));
 }
 
@@ -734,6 +734,7 @@
     glClearColor(0.2, 0.2, 0.2, 0.2);
     glClear(GL_COLOR_BUFFER_BIT);
 
+    glViewport(0, 0, texWidth, texHeight);
     drawTexture();
 
     EXPECT_TRUE(checkPixel( 0,  0, 231, 231, 231, 231));
@@ -807,6 +808,7 @@
     glClearColor(0.2, 0.2, 0.2, 0.2);
     glClear(GL_COLOR_BUFFER_BIT);
 
+    glViewport(0, 0, texWidth, texHeight);
     drawTexture();
 
     EXPECT_TRUE(checkPixel( 0,  0, 153, 153, 153, 153));
diff --git a/libs/utils/BlobCache.cpp b/libs/utils/BlobCache.cpp
index 1298fa7..590576a 100644
--- a/libs/utils/BlobCache.cpp
+++ b/libs/utils/BlobCache.cpp
@@ -31,9 +31,13 @@
         mMaxTotalSize(maxTotalSize),
         mTotalSize(0) {
     nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+#ifdef _WIN32
+    srand(now);
+#else
     mRandState[0] = (now >> 0) & 0xFFFF;
     mRandState[1] = (now >> 16) & 0xFFFF;
     mRandState[2] = (now >> 32) & 0xFFFF;
+#endif
     LOGV("initializing random seed using %lld", now);
 }
 
@@ -148,11 +152,19 @@
     return valueBlobSize;
 }
 
+long int BlobCache::blob_random() {
+#ifdef _WIN32
+    return rand();
+#else
+    return nrand48(mRandState);
+#endif
+}
+
 void BlobCache::clean() {
     // Remove a random cache entry until the total cache size gets below half
     // the maximum total cache size.
     while (mTotalSize > mMaxTotalSize / 2) {
-        size_t i = size_t(nrand48(mRandState) % (mCacheEntries.size()));
+        size_t i = size_t(blob_random() % (mCacheEntries.size()));
         const CacheEntry& entry(mCacheEntries[i]);
         mTotalSize -= entry.getKey()->getSize() + entry.getValue()->getSize();
         mCacheEntries.removeAt(i);