Merge "Add MODE_MULTI_PROCESS flag to Context.getSharedPreferences()"
diff --git a/api/current.xml b/api/current.xml
index df4b914..8ce9da4 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -48156,6 +48156,17 @@
  visibility="public"
 >
 </field>
+<field name="MODE_MULTI_PROCESS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="MODE_PRIVATE"
  type="int"
  transient="false"
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 083612e..72f7286 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -554,10 +554,13 @@
                 return sp;
             }
         }
-        // If somebody else (some other process) changed the prefs
-        // file behind our back, we reload it.  This has been the
-        // historical (if undocumented) behavior.
-        sp.startReloadIfChangedUnexpectedly();
+        if ((mode & Context.MODE_MULTI_PROCESS) != 0 ||
+            getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.HONEYCOMB) {
+            // If somebody else (some other process) changed the prefs
+            // file behind our back, we reload it.  This has been the
+            // historical (if undocumented) behavior.
+            sp.startReloadIfChangedUnexpectedly();
+        }
         return sp;
     }
 
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 277dc2f..b128d31 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -80,6 +80,25 @@
     public static final int MODE_APPEND = 0x8000;
 
     /**
+     * SharedPreference loading flag: when set, the file on disk will
+     * be checked for modification even if the shared preferences
+     * instance is already loaded in this process.  This behavior is
+     * sometimes desired in cases where the application has multiple
+     * processes, all writing to the same SharedPreferences file.
+     * Generally there are better forms of communication between
+     * processes, though.
+     *
+     * <p>This was the legacy (but undocumented) behavior in and
+     * before Gingerbread (Android 2.3) and this flag is implied when
+     * targetting such releases.  For applications targetting SDK
+     * versions <em>greater than</em> Android 2.3, this flag must be
+     * explicitly set if desired.
+     *
+     * @see #getSharedPreferences
+     */
+    public static final int MODE_MULTI_PROCESS = 0x0004;
+
+    /**
      * Flag for {@link #bindService}: automatically create the service as long
      * as the binding exists.  Note that while this will create the service,
      * its {@link android.app.Service#onStartCommand}
@@ -318,7 +337,11 @@
      * editor (SharedPreferences.edit()) and then commit changes (Editor.commit()).
      * @param mode Operating mode.  Use 0 or {@link #MODE_PRIVATE} for the
      * default operation, {@link #MODE_WORLD_READABLE}
-     * and {@link #MODE_WORLD_WRITEABLE} to control permissions.
+     * and {@link #MODE_WORLD_WRITEABLE} to control permissions.  The bit
+     * {@link #MODE_MULTI_PROCESS} can also be used if multiple processes
+     * are mutating the same SharedPreferences file.  {@link #MODE_MULTI_PROCESS}
+     * is always on in apps targetting Gingerbread (Android 2.3) and below, and
+     * off by default in later versions.
      *
      * @return Returns the single SharedPreferences instance that can be used
      *         to retrieve and modify the preference values.
@@ -326,6 +349,7 @@
      * @see #MODE_PRIVATE
      * @see #MODE_WORLD_READABLE
      * @see #MODE_WORLD_WRITEABLE
+     * @see #MODE_MULTI_PROCESS
      */
     public abstract SharedPreferences getSharedPreferences(String name,
             int mode);
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 9d50fd9..f56f6a9 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -76,7 +76,12 @@
      * @return true if the file exists and false if it does not exist. If you do not have 
      * permission to stat the file, then this method will return false.
      */
-    public static native boolean getFileStatus(String path, FileStatus status);
+    public static boolean getFileStatus(String path, FileStatus status) {
+        StrictMode.noteDiskRead();
+        return getFileStatusNative(path, status);
+    }
+
+    private static native boolean getFileStatusNative(String path, FileStatus status);
 
     /** Regular expression for safe filenames: no spaces or metacharacters */
     private static final Pattern SAFE_FILENAME_PATTERN = Pattern.compile("[\\w%+,./=_-]+");
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index b00b9c9..e314fce 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -669,25 +669,46 @@
         CloseGuard.setEnabled(enabled);
     }
 
-    private static class StrictModeNetworkViolation extends BlockGuard.BlockGuardPolicyException {
+    /**
+     * @hide
+     */
+    public static class StrictModeViolation extends BlockGuard.BlockGuardPolicyException {
+        public StrictModeViolation(int policyState, int policyViolated, String message) {
+            super(policyState, policyViolated, message);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public static class StrictModeNetworkViolation extends StrictModeViolation {
         public StrictModeNetworkViolation(int policyMask) {
-            super(policyMask, DETECT_NETWORK);
+            super(policyMask, DETECT_NETWORK, null);
         }
     }
 
-    private static class StrictModeDiskReadViolation extends BlockGuard.BlockGuardPolicyException {
+    /**
+     * @hide
+     */
+    private static class StrictModeDiskReadViolation extends StrictModeViolation {
         public StrictModeDiskReadViolation(int policyMask) {
-            super(policyMask, DETECT_DISK_READ);
+            super(policyMask, DETECT_DISK_READ, null);
         }
     }
 
-    private static class StrictModeDiskWriteViolation extends BlockGuard.BlockGuardPolicyException {
+     /**
+     * @hide
+     */
+   private static class StrictModeDiskWriteViolation extends StrictModeViolation {
         public StrictModeDiskWriteViolation(int policyMask) {
-            super(policyMask, DETECT_DISK_WRITE);
+            super(policyMask, DETECT_DISK_WRITE, null);
         }
     }
 
-    private static class StrictModeCustomViolation extends BlockGuard.BlockGuardPolicyException {
+    /**
+     * @hide
+     */
+    private static class StrictModeCustomViolation extends StrictModeViolation {
         public StrictModeCustomViolation(int policyMask, String name) {
             super(policyMask, DETECT_CUSTOM, name);
         }
@@ -1007,9 +1028,16 @@
             // thread, in "gather" mode.  In this case, the duration
             // of the violation is computed by the ultimate caller and
             // its Looper, if any.
+            //
+            // Also, as a special short-cut case when the only penalty
+            // bit is death, we die immediately, rather than timing
+            // the violation's duration.  This makes it convenient to
+            // use in unit tests too, rather than waiting on a Looper.
+            //
             // TODO: if in gather mode, ignore Looper.myLooper() and always
             //       go into this immediate mode?
-            if (looper == null) {
+            if (looper == null ||
+                (info.policy & PENALTY_MASK) == PENALTY_DEATH) {
                 info.durationMillis = -1;  // unknown (redundant, already set)
                 handleViolation(info);
                 return;
@@ -1179,13 +1207,16 @@
             }
 
             if ((info.policy & PENALTY_DEATH) != 0) {
-                System.err.println("StrictMode policy violation with POLICY_DEATH; shutting down.");
-                Process.killProcess(Process.myPid());
-                System.exit(10);
+                executeDeathPenalty(info);
             }
         }
     }
 
+    private static void executeDeathPenalty(ViolationInfo info) {
+        int violationBit = parseViolationFromMessage(info.crashInfo.exceptionMessage);
+        throw new StrictModeViolation(info.policy, violationBit, null);
+    }
+
     /**
      * In the common case, as set by conditionallyEnableDebugLogging,
      * we're just dropboxing any violations but not showing a dialog,
@@ -1597,6 +1628,31 @@
     }
 
     /**
+     * @hide
+     */
+    public static void noteDiskRead() {
+        BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
+        Log.d(TAG, "noteDiskRead; policy=" + policy);
+        if (!(policy instanceof AndroidBlockGuardPolicy)) {
+            // StrictMode not enabled.
+            return;
+        }
+        ((AndroidBlockGuardPolicy) policy).onReadFromDisk();
+    }
+
+    /**
+     * @hide
+     */
+    public static void noteDiskWrite() {
+        BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
+        if (!(policy instanceof AndroidBlockGuardPolicy)) {
+            // StrictMode not enabled.
+            return;
+        }
+        ((AndroidBlockGuardPolicy) policy).onWriteToDisk();
+    }
+
+    /**
      * Parcelable that gets sent in Binder call headers back to callers
      * to report violations that happened during a cross-process call.
      *
diff --git a/core/jni/android_os_FileUtils.cpp b/core/jni/android_os_FileUtils.cpp
index d3faa2f..d8a3db3 100644
--- a/core/jni/android_os_FileUtils.cpp
+++ b/core/jni/android_os_FileUtils.cpp
@@ -177,7 +177,7 @@
     {"getPermissions",  "(Ljava/lang/String;[I)I", (void*)android_os_FileUtils_getPermissions},
     {"setUMask",        "(I)I",                    (void*)android_os_FileUtils_setUMask},
     {"getFatVolumeId",  "(Ljava/lang/String;)I", (void*)android_os_FileUtils_getFatVolumeId},
-    {"getFileStatus",  "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z", (void*)android_os_FileUtils_getFileStatus},
+    {"getFileStatusNative", "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z", (void*)android_os_FileUtils_getFileStatus},
 };
 
 static const char* const kFileUtilsPathName = "android/os/FileUtils";
@@ -211,4 +211,3 @@
 }
 
 }
-