Merge "Adjust tests to new android-support-test + espresso libraries."
diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java
index 97187d6..389ae71 100644
--- a/core/java/android/app/SharedPreferencesImpl.java
+++ b/core/java/android/app/SharedPreferencesImpl.java
@@ -50,6 +50,11 @@
 import java.util.Set;
 import java.util.WeakHashMap;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 final class SharedPreferencesImpl implements SharedPreferences {
     private static final String TAG = "SharedPreferencesImpl";
@@ -69,16 +74,12 @@
     private final Object mLock = new Object();
     private final Object mWritingToDiskLock = new Object();
 
-    @GuardedBy("mLock")
-    private Map<String, Object> mMap;
+    private Future<Map<String, Object>> mMap;
 
     @GuardedBy("mLock")
     private int mDiskWritesInFlight = 0;
 
     @GuardedBy("mLock")
-    private boolean mLoaded = false;
-
-    @GuardedBy("mLock")
     private StructTimespec mStatTimestamp;
 
     @GuardedBy("mLock")
@@ -105,27 +106,18 @@
         mFile = file;
         mBackupFile = makeBackupFile(file);
         mMode = mode;
-        mLoaded = false;
         mMap = null;
         startLoadFromDisk();
     }
 
     private void startLoadFromDisk() {
-        synchronized (mLock) {
-            mLoaded = false;
-        }
-        new Thread("SharedPreferencesImpl-load") {
-            public void run() {
-                loadFromDisk();
-            }
-        }.start();
+        FutureTask<Map<String, Object>> futureTask = new FutureTask<>(() -> loadFromDisk());
+        mMap = futureTask;
+        new Thread(futureTask, "SharedPreferencesImpl-load").start();
     }
 
-    private void loadFromDisk() {
+    private Map<String, Object> loadFromDisk() {
         synchronized (mLock) {
-            if (mLoaded) {
-                return;
-            }
             if (mBackupFile.exists()) {
                 mFile.delete();
                 mBackupFile.renameTo(mFile);
@@ -158,16 +150,14 @@
         }
 
         synchronized (mLock) {
-            mLoaded = true;
             if (map != null) {
-                mMap = map;
                 mStatTimestamp = stat.st_mtim;
                 mStatSize = stat.st_size;
             } else {
-                mMap = new HashMap<>();
+                map = new HashMap<>();
             }
-            mLock.notifyAll();
         }
+        return map;
     }
 
     static File makeBackupFile(File prefsFile) {
@@ -226,36 +216,37 @@
         }
     }
 
-    private void awaitLoadedLocked() {
-        if (!mLoaded) {
+    private @GuardedBy("mLock") Map<String, Object> getLoaded() {
+        try {
+            return mMap.get();
+        } catch (InterruptedException | ExecutionException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+    private @GuardedBy("mLock") Map<String, Object> getLoadedWithBlockGuard() {
+        if (!mMap.isDone()) {
             // Raise an explicit StrictMode onReadFromDisk for this
             // thread, since the real read will be in a different
             // thread and otherwise ignored by StrictMode.
             BlockGuard.getThreadPolicy().onReadFromDisk();
         }
-        while (!mLoaded) {
-            try {
-                mLock.wait();
-            } catch (InterruptedException unused) {
-            }
-        }
+        return getLoaded();
     }
 
     @Override
     public Map<String, ?> getAll() {
+        Map<String, Object> map = getLoadedWithBlockGuard();
         synchronized (mLock) {
-            awaitLoadedLocked();
-            //noinspection unchecked
-            return new HashMap<String, Object>(mMap);
+            return new HashMap<String, Object>(map);
         }
     }
 
     @Override
     @Nullable
     public String getString(String key, @Nullable String defValue) {
+        Map<String, Object> map = getLoadedWithBlockGuard();
         synchronized (mLock) {
-            awaitLoadedLocked();
-            String v = (String)mMap.get(key);
+            String v = (String) map.get(key);
             return v != null ? v : defValue;
         }
     }
@@ -263,66 +254,65 @@
     @Override
     @Nullable
     public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {
+        Map<String, Object> map = getLoadedWithBlockGuard();
         synchronized (mLock) {
-            awaitLoadedLocked();
-            Set<String> v = (Set<String>) mMap.get(key);
+            @SuppressWarnings("unchecked")
+            Set<String> v = (Set<String>) map.get(key);
             return v != null ? v : defValues;
         }
     }
 
     @Override
     public int getInt(String key, int defValue) {
+        Map<String, Object> map = getLoadedWithBlockGuard();
         synchronized (mLock) {
-            awaitLoadedLocked();
-            Integer v = (Integer)mMap.get(key);
+            Integer v = (Integer) map.get(key);
             return v != null ? v : defValue;
         }
     }
     @Override
     public long getLong(String key, long defValue) {
+        Map<String, Object> map = getLoadedWithBlockGuard();
         synchronized (mLock) {
-            awaitLoadedLocked();
-            Long v = (Long)mMap.get(key);
+            Long v = (Long) map.get(key);
             return v != null ? v : defValue;
         }
     }
     @Override
     public float getFloat(String key, float defValue) {
+        Map<String, Object> map = getLoadedWithBlockGuard();
         synchronized (mLock) {
-            awaitLoadedLocked();
-            Float v = (Float)mMap.get(key);
+            Float v = (Float) map.get(key);
             return v != null ? v : defValue;
         }
     }
     @Override
     public boolean getBoolean(String key, boolean defValue) {
+        Map<String, Object> map = getLoadedWithBlockGuard();
         synchronized (mLock) {
-            awaitLoadedLocked();
-            Boolean v = (Boolean)mMap.get(key);
+            Boolean v = (Boolean) map.get(key);
             return v != null ? v : defValue;
         }
     }
 
     @Override
     public boolean contains(String key) {
+        Map<String, Object> map = getLoadedWithBlockGuard();
         synchronized (mLock) {
-            awaitLoadedLocked();
-            return mMap.containsKey(key);
+            return map.containsKey(key);
         }
     }
 
     @Override
     public Editor edit() {
-        // TODO: remove the need to call awaitLoadedLocked() when
+        // TODO: remove the need to call getLoaded() when
         // requesting an editor.  will require some work on the
         // Editor, but then we should be able to do:
         //
         //      context.getSharedPreferences(..).edit().putString(..).apply()
         //
         // ... all without blocking.
-        synchronized (mLock) {
-            awaitLoadedLocked();
-        }
+        getLoadedWithBlockGuard();
 
         return new EditorImpl();
     }
@@ -476,13 +466,43 @@
                 // a memory commit comes in when we're already
                 // writing to disk.
                 if (mDiskWritesInFlight > 0) {
-                    // We can't modify our mMap as a currently
+                    // We can't modify our map as a currently
                     // in-flight write owns it.  Clone it before
                     // modifying it.
                     // noinspection unchecked
-                    mMap = new HashMap<String, Object>(mMap);
+                    mMap = new Future<Map<String, Object>>() {
+                        private Map<String, Object> mCopiedMap =
+                                new HashMap<String, Object>(getLoaded());
+
+                        @Override
+                        public boolean cancel(boolean mayInterruptIfRunning) {
+                            return false;
+                        }
+
+                        @Override
+                        public boolean isCancelled() {
+                            return false;
+                        }
+
+                        @Override
+                        public boolean isDone() {
+                            return true;
+                        }
+
+                        @Override
+                        public Map<String, Object> get()
+                                throws InterruptedException, ExecutionException {
+                            return mCopiedMap;
+                        }
+
+                        @Override
+                        public Map<String, Object> get(long timeout, TimeUnit unit)
+                                throws InterruptedException, ExecutionException, TimeoutException {
+                            return mCopiedMap;
+                        }
+                    };
                 }
-                mapToWriteToDisk = mMap;
+                mapToWriteToDisk = getLoaded();
                 mDiskWritesInFlight++;
 
                 boolean hasListeners = mListeners.size() > 0;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7f5beff..2195587 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -17225,6 +17225,15 @@
                 return;
             }
 
+            // check if the new package supports all of the abis which the old package supports
+            boolean oldPkgSupportMultiArch = oldPackage.applicationInfo.secondaryCpuAbi != null;
+            boolean newPkgSupportMultiArch = pkg.applicationInfo.secondaryCpuAbi != null;
+            if (isSystemApp(oldPackage) && oldPkgSupportMultiArch && !newPkgSupportMultiArch) {
+                res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+                        "Update to package " + pkgName + " doesn't support multi arch");
+                return;
+            }
+
             // In case of rollback, remember per-user/profile install state
             allUsers = sUserManager.getUserIds();
             installedUsers = ps.queryInstalledUsers(allUsers, true);
diff --git a/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java
index f392570..a554c69 100644
--- a/telephony/java/android/telephony/MbmsDownloadSession.java
+++ b/telephony/java/android/telephony/MbmsDownloadSession.java
@@ -347,6 +347,7 @@
 
                     @Override
                     public void onServiceDisconnected(ComponentName name) {
+                        Log.w(LOG_TAG, "bindAndInitialize: Remote service disconnected");
                         sIsInitialized.set(false);
                         mService.set(null);
                     }
@@ -385,6 +386,7 @@
         } catch (RemoteException e) {
             Log.w(LOG_TAG, "Remote process died");
             mService.set(null);
+            sIsInitialized.set(false);
             sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
         }
     }
@@ -438,6 +440,7 @@
             }
         } catch (RemoteException e) {
             mService.set(null);
+            sIsInitialized.set(false);
             sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
             return;
         }
@@ -521,6 +524,7 @@
             downloadService.download(request);
         } catch (RemoteException e) {
             mService.set(null);
+            sIsInitialized.set(false);
             sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
         }
     }
@@ -542,6 +546,7 @@
             return downloadService.listPendingDownloads(mSubscriptionId);
         } catch (RemoteException e) {
             mService.set(null);
+            sIsInitialized.set(false);
             sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
             return Collections.emptyList();
         }
@@ -583,6 +588,7 @@
             }
         } catch (RemoteException e) {
             mService.set(null);
+            sIsInitialized.set(false);
             sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
             return;
         }
@@ -622,6 +628,7 @@
                 }
             } catch (RemoteException e) {
                 mService.set(null);
+                sIsInitialized.set(false);
                 sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
             }
         } finally {
@@ -658,6 +665,7 @@
             }
         } catch (RemoteException e) {
             mService.set(null);
+            sIsInitialized.set(false);
             sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
             return;
         }
@@ -686,6 +694,7 @@
             return downloadService.getDownloadStatus(downloadRequest, fileInfo);
         } catch (RemoteException e) {
             mService.set(null);
+            sIsInitialized.set(false);
             sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
             return STATUS_UNKNOWN;
         }
@@ -727,6 +736,7 @@
             }
         } catch (RemoteException e) {
             mService.set(null);
+            sIsInitialized.set(false);
             sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
         }
     }