More StrictMode work, keeping Binder & BlockGuard's thread-locals in-sync.

Change-Id: Ia67cabcc17a73a0f15907ffea683d06bc41b90e5
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index c9df567..4c40982 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -101,7 +101,30 @@
      * @see #clearCallingIdentity
      */
     public static final native void restoreCallingIdentity(long token);
-    
+
+    /**
+     * Sets the native thread-local StrictMode policy mask.
+     *
+     * <p>The StrictMode settings are kept in two places: a Java-level
+     * threadlocal for libcore/Dalvik, and a native threadlocal (set
+     * here) for propagation via Binder calls.  This is a little
+     * unfortunate, but necessary to break otherwise more unfortunate
+     * dependencies either of Dalvik on Android, or Android
+     * native-only code on Dalvik.
+     *
+     * @see StrictMode
+     * @hide
+     */
+    public static final native void setThreadStrictModePolicy(int policyMask);
+
+    /**
+     * Gets the current native thread-local StrictMode policy mask.
+     *
+     * @see #setThreadStrictModePolicy
+     * @hide
+     */
+    public static final native int getThreadStrictModePolicy();
+
     /**
      * Flush any Binder commands pending in the current thread to the kernel
      * driver.  This can be
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 9b18719..f7ad884 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -69,6 +69,17 @@
      */
     public static final int PENALTY_DROPBOX = 0x80;
 
+    /**
+     * Non-public penalty mode which overrides all the other penalty
+     * bits and signals that we're in a Binder call and we should
+     * ignore the other penalty bits and instead serialize back all
+     * our offending stack traces to the caller to ultimately handle
+     * in the originating process.
+     *
+     * @hide
+     */
+    public static final int PENALTY_GATHER = 0x100;
+
     /** @hide */
     public static final int PENALTY_MASK =
             PENALTY_LOG | PENALTY_DIALOG |
@@ -81,6 +92,19 @@
      * @param policyMask a bitmask of DISALLOW_* and PENALTY_* values.
      */
     public static void setThreadBlockingPolicy(final int policyMask) {
+        // In addition to the Java-level thread-local in Dalvik's
+        // BlockGuard, we also need to keep a native thread-local in
+        // Binder in order to propagate the value across Binder calls,
+        // even across native-only processes.  The two are kept in
+        // sync via the callback to onStrictModePolicyChange, below.
+        setBlockGuardPolicy(policyMask);
+
+        // And set the Android native version...
+        Binder.setThreadStrictModePolicy(policyMask);
+    }
+
+    // Sets the policy in Dalvik/libcore (BlockGuard)
+    private static void setBlockGuardPolicy(final int policyMask) {
         if (policyMask == 0) {
             BlockGuard.setThreadPolicy(BlockGuard.LAX_POLICY);
             return;
@@ -189,6 +213,11 @@
                     new ApplicationErrorReport.CrashInfo(violation);
             crashInfo.durationMillis = durationMillis;
 
+            if ((policy & PENALTY_GATHER) != 0) {
+                Log.d(TAG, "StrictMode violation via Binder call; ignoring for now.");
+                return;
+            }
+
             // Not perfect, but fast and good enough for dup suppression.
             Integer crashFingerprint = crashInfo.stackTrace.hashCode();
             long lastViolationTime = 0;
@@ -227,13 +256,23 @@
 
             if (violationMask != 0) {
                 violationMask |= violation.getPolicyViolation();
+                final int savedPolicy = getThreadBlockingPolicy();
                 try {
+                    // First, remove any policy before we call into the Activity Manager,
+                    // otherwise we'll infinite recurse as we try to log policy violations
+                    // to disk, thus violating policy, thus requiring logging, etc...
+                    // We restore the current policy below, in the finally block.
+                    setThreadBlockingPolicy(0);
+
                     ActivityManagerNative.getDefault().handleApplicationStrictModeViolation(
                         RuntimeInit.getApplicationObject(),
                         violationMask,
                         new ApplicationErrorReport.CrashInfo(violation));
                 } catch (RemoteException e) {
                     Log.e(TAG, "RemoteException trying to handle StrictMode violation", e);
+                } finally {
+                    // Restore the policy.
+                    setThreadBlockingPolicy(savedPolicy);
                 }
             }
 
@@ -244,4 +283,16 @@
             }
         }
     }
+
+    /**
+     * Called from android_util_Binder.cpp's
+     * android_os_Parcel_enforceInterface when an incoming Binder call
+     * requires changing the StrictMode policy mask.  The role of this
+     * function is to ask Binder for its current (native) thread-local
+     * policy value and synchronize it to libcore's (Java)
+     * thread-local policy value.
+     */
+    private static void onBinderStrictModePolicyChange(int newPolicy) {
+        setBlockGuardPolicy(newPolicy);
+    }
 }
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index d26cd28..040b324 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -134,6 +134,12 @@
     jmethodID mConstructor;
 } gParcelFileDescriptorOffsets;
 
+static struct strict_mode_callback_offsets_t
+{
+    jclass mClass;
+    jmethodID mCallback;
+} gStrictModeCallbackOffsets;
+
 // ****************************************************************************
 // ****************************************************************************
 // ****************************************************************************
@@ -214,6 +220,15 @@
     env->DeleteLocalRef(msgstr);
 }
 
+static void set_dalvik_blockguard_policy(JNIEnv* env, jint strict_policy)
+{
+    // Call back into android.os.StrictMode#onBinderStrictModePolicyChange
+    // to sync our state back to it.  See the comments in StrictMode.java.
+    env->CallStaticVoidMethod(gStrictModeCallbackOffsets.mClass,
+                              gStrictModeCallbackOffsets.mCallback,
+                              strict_policy);
+}
+
 class JavaBBinderHolder;
 
 class JavaBBinder : public BBinder
@@ -253,12 +268,28 @@
 
         LOGV("onTransact() on %p calling object %p in env %p vm %p\n", this, mObject, env, mVM);
 
+        IPCThreadState* thread_state = IPCThreadState::self();
+        const int strict_policy_before = thread_state->getStrictModePolicy();
+
         //printf("Transact from %p to Java code sending: ", this);
         //data.print();
         //printf("\n");
         jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
             code, (int32_t)&data, (int32_t)reply, flags);
         jthrowable excep = env->ExceptionOccurred();
+
+        // Restore the Java binder thread's state if it changed while
+        // processing a call (as it would if the Parcel's header had a
+        // new policy mask and Parcel.enforceInterface() changed
+        // it...)
+        const int strict_policy_after = thread_state->getStrictModePolicy();
+        if (strict_policy_after != strict_policy_before) {
+            // Our thread-local...
+            thread_state->setStrictModePolicy(strict_policy_before);
+            // And the Java-level thread-local...
+            set_dalvik_blockguard_policy(env, strict_policy_before);
+        }
+
         if (excep) {
             report_exception(env, excep,
                 "*** Uncaught remote exception!  "
@@ -574,6 +605,16 @@
     IPCThreadState::self()->restoreCallingIdentity(token);
 }
 
+static void android_os_Binder_setThreadStrictModePolicy(JNIEnv* env, jobject clazz, jint policyMask)
+{
+    IPCThreadState::self()->setStrictModePolicy(policyMask);
+}
+
+static jint android_os_Binder_getThreadStrictModePolicy(JNIEnv* env, jobject clazz)
+{
+    return IPCThreadState::self()->getStrictModePolicy();
+}
+
 static void android_os_Binder_flushPendingCommands(JNIEnv* env, jobject clazz)
 {
     IPCThreadState::self()->flushCommands();
@@ -618,6 +659,8 @@
     { "getCallingUid", "()I", (void*)android_os_Binder_getCallingUid },
     { "clearCallingIdentity", "()J", (void*)android_os_Binder_clearCallingIdentity },
     { "restoreCallingIdentity", "(J)V", (void*)android_os_Binder_restoreCallingIdentity },
+    { "setThreadStrictModePolicy", "(I)V", (void*)android_os_Binder_setThreadStrictModePolicy },
+    { "getThreadStrictModePolicy", "()I", (void*)android_os_Binder_getThreadStrictModePolicy },
     { "flushPendingCommands", "()V", (void*)android_os_Binder_flushPendingCommands },
     { "init", "()V", (void*)android_os_Binder_init },
     { "destroy", "()V", (void*)android_os_Binder_destroy }
@@ -1523,9 +1566,24 @@
     if (parcel != NULL) {
         const jchar* str = env->GetStringCritical(name, 0);
         if (str) {
-            bool isValid = parcel->enforceInterface(String16(str, env->GetStringLength(name)));
+            const int32_t old_strict_policy =
+                IPCThreadState::self()->getStrictModePolicy();
+            int32_t strict_policy;
+            bool isValid = parcel->enforceInterface(
+                String16(str, env->GetStringLength(name)),
+                &strict_policy);
             env->ReleaseStringCritical(name, str);
             if (isValid) {
+                if (old_strict_policy != strict_policy) {
+                    // Need to keep the Java-level thread-local strict
+                    // mode policy in sync for the libcore
+                    // enforcements, which involves an upcall back
+                    // into Java.  (We can't modify the
+                    // Parcel.enforceInterface signature, as it's
+                    // pseudo-public, and used via AIDL
+                    // auto-generation...)
+                    set_dalvik_blockguard_policy(env, strict_policy);
+                }
                 return;     // everything was correct -> return silently
             }
         }
@@ -1611,6 +1669,14 @@
     gParcelOffsets.mOwnObject
         = env->GetFieldID(clazz, "mOwnObject", "I");
 
+    clazz = env->FindClass("android/os/StrictMode");
+    LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.StrictMode");
+    gStrictModeCallbackOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
+    gStrictModeCallbackOffsets.mCallback = env->GetStaticMethodID(
+        clazz, "onBinderStrictModePolicyChange", "(I)V");
+    LOG_FATAL_IF(gStrictModeCallbackOffsets.mCallback == NULL,
+                 "Unable to find strict mode callback.");
+
     return AndroidRuntime::registerNativeMethods(
         env, kParcelPathName,
         gParcelMethods, NELEM(gParcelMethods));
diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h
index 3aba5f6..fd0fc1f 100644
--- a/include/binder/Parcel.h
+++ b/include/binder/Parcel.h
@@ -58,9 +58,13 @@
 
     // Writes the RPC header.
     status_t            writeInterfaceToken(const String16& interface);
+
     // Parses the RPC header, returning true if the interface name
     // in the header matches the expected interface from the caller.
-    bool                enforceInterface(const String16& interface) const;
+    // If strict_policy_out is non-NULL, the RPC header's StrictMode policy
+    // mask is returned.
+    bool                enforceInterface(const String16& interface,
+                                         int32_t* strict_policy_out = NULL) const;
     bool                checkInterface(IBinder*) const;
 
     void                freeData();
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 28706ba..f6582e6 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -372,8 +372,8 @@
     mStrictModePolicy = policy;
 }
 
-
-int32_t IPCThreadState::getStrictModePolicy() const {
+int32_t IPCThreadState::getStrictModePolicy() const
+{
     return mStrictModePolicy;
 }
 
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 47be1bf..bed893a 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -48,6 +48,9 @@
 
 #define PAD_SIZE(s) (((s)+3)&~3)
 
+// Note: must be kept in sync with android/os/StrictMode.java's PENALTY_GATHER
+#define STRICT_MODE_PENALTY_GATHER 0x100
+
 // XXX This can be made public if we want to provide
 // support for typed data.
 struct small_flat_data
@@ -440,7 +443,8 @@
 // Write RPC headers.  (previously just the interface token)
 status_t Parcel::writeInterfaceToken(const String16& interface)
 {
-    writeInt32(IPCThreadState::self()->getStrictModePolicy());
+    writeInt32(IPCThreadState::self()->getStrictModePolicy() |
+               STRICT_MODE_PENALTY_GATHER);
     // currently the interface identification token is just its name as a string
     return writeString16(interface);
 }
@@ -450,9 +454,14 @@
     return enforceInterface(binder->getInterfaceDescriptor());
 }
 
-bool Parcel::enforceInterface(const String16& interface) const
+bool Parcel::enforceInterface(const String16& interface,
+                              int32_t* strict_policy_out) const
 {
     int32_t strict_policy = readInt32();
+    IPCThreadState::self()->setStrictModePolicy(strict_policy);
+    if (strict_policy_out != NULL) {
+      *strict_policy_out = strict_policy;
+    }
     const String16 str(readString16());
     if (str == interface) {
         return true;