Make settings cahches generation mechanism robust.

Settings is using a MemoryIntArray to communicate the settings table
version enabling apps to have up-to-date local caches. However, ashmem
allows an arbitrary process with a handle to the fd (even in read only
mode) to unpin the memory which can then be garbage collected. Here we
make this mechanism fault tolerant against bad apps unpinning the ashmem
region. First, we no longer unpin the ashmem on the client side and if
the ashmem region is purged and cannot be pinned we recreate it and
hook up again with the local app caches. The change also adds a test
that clients can only read while owner can read/write.

bug:28764789

Change-Id: I1ef79b4b21e976124b268c9126a55d614157059b
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 370cd57..a4c0d67 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1462,12 +1462,15 @@
 
     private static final class GenerationTracker {
         private final MemoryIntArray mArray;
+        private final Runnable mErrorHandler;
         private final int mIndex;
         private int mCurrentGeneration;
 
-        public GenerationTracker(@NonNull MemoryIntArray array, int index) {
+        public GenerationTracker(@NonNull MemoryIntArray array, int index,
+                Runnable errorHandler) {
             mArray = array;
             mIndex = index;
+            mErrorHandler = errorHandler;
             mCurrentGeneration = readCurrentGeneration();
         }
 
@@ -1487,9 +1490,23 @@
                 return mArray.get(mIndex);
             } catch (IOException e) {
                 Log.e(TAG, "Error getting current generation", e);
+                if (mErrorHandler != null) {
+                    mErrorHandler.run();
+                }
             }
             return -1;
         }
+
+        public void destroy() {
+            try {
+                mArray.close();
+            } catch (IOException e) {
+                Log.e(TAG, "Error closing backing array", e);
+                if (mErrorHandler != null) {
+                    mErrorHandler.run();
+                }
+            }
+        }
     }
 
     // Thread-safe.
@@ -1616,7 +1633,20 @@
                                                     + cr.getPackageName() + " and user:"
                                                     + userHandle + " with index:" + index);
                                         }
-                                        mGenerationTracker = new GenerationTracker(array, index);
+                                        mGenerationTracker = new GenerationTracker(array, index,
+                                                () -> {
+                                            synchronized (this) {
+                                                Log.e(TAG, "Error accessing generation"
+                                                        + " tracker - removing");
+                                                if (mGenerationTracker != null) {
+                                                    GenerationTracker generationTracker =
+                                                            mGenerationTracker;
+                                                    mGenerationTracker = null;
+                                                    generationTracker.destroy();
+                                                    mValues.clear();
+                                                }
+                                            }
+                                        });
                                     }
                                 }
                                 mValues.put(name, value);