Merge "Create a new mount mode for installer packages."
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index f928501..b42d53a 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -306,6 +306,6 @@
     public abstract void setDebugFlagsForStartingActivity(ActivityInfo aInfo, int startFlags,
             ProfilerInfo profilerInfo, Object wmLock);
 
-    /** Checks if process running with given pid has access to full external storage or not */
-    public abstract boolean isAppStorageSandboxed(int pid, int uid);
+    /** Returns mount mode for process running with given pid */
+    public abstract int getStorageMountMode(int pid, int uid);
 }
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 7fd0a4b..f136cd6 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -398,6 +398,8 @@
             argsForZygote.add("--mount-external-write");
         } else if (mountExternal == Zygote.MOUNT_EXTERNAL_FULL) {
             argsForZygote.add("--mount-external-full");
+        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_INSTALLER) {
+            argsForZygote.add("--mount-external-installer");
         }
 
         argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 65213c0..65b9fad 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -81,6 +81,11 @@
     public static final int MOUNT_EXTERNAL_READ = IVold.REMOUNT_MODE_READ;
     /** Read-write external storage should be mounted. */
     public static final int MOUNT_EXTERNAL_WRITE = IVold.REMOUNT_MODE_WRITE;
+    /**
+     * Mount mode for package installers which should give them access to
+     * all obb dirs in addition to their package sandboxes
+     */
+    public static final int MOUNT_EXTERNAL_INSTALLER = IVold.REMOUNT_MODE_INSTALLER;
     /** Read-write external storage should be mounted instead of package sandbox */
     public static final int MOUNT_EXTERNAL_FULL = IVold.REMOUNT_MODE_FULL;
 
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 4a94ec4..f182c4d 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -656,7 +656,9 @@
                     mountExternal = Zygote.MOUNT_EXTERNAL_WRITE;
                 } else if (arg.equals("--mount-external-full")) {
                     mountExternal = Zygote.MOUNT_EXTERNAL_FULL;
-                }  else if (arg.equals("--query-abi-list")) {
+                }  else if (arg.equals("--mount-external-installer")) {
+                    mountExternal = Zygote.MOUNT_EXTERNAL_INSTALLER;
+                } else if (arg.equals("--query-abi-list")) {
                     abiListQuery = true;
                 } else if (arg.equals("--get-pid")) {
                     pidQuery = true;
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 4aa88e7..7032081 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -99,7 +99,8 @@
   MOUNT_EXTERNAL_DEFAULT = 1,
   MOUNT_EXTERNAL_READ = 2,
   MOUNT_EXTERNAL_WRITE = 3,
-  MOUNT_EXTERNAL_FULL = 4,
+  MOUNT_EXTERNAL_INSTALLER = 4,
+  MOUNT_EXTERNAL_FULL = 5,
 };
 
 // Must match values in com.android.internal.os.Zygote.
@@ -446,6 +447,22 @@
     return true;
 }
 
+static bool bindMount(const std::string& sourceDir, const std::string& targetDir,
+        std::string* error_msg) {
+    if (TEMP_FAILURE_RETRY(mount(sourceDir.c_str(), targetDir.c_str(),
+            nullptr, MS_BIND | MS_REC, nullptr)) == -1) {
+        *error_msg = CREATE_ERROR("Failed to mount %s to %s: %s",
+                sourceDir.c_str(), targetDir.c_str(), strerror(errno));
+        return false;
+    }
+    if (TEMP_FAILURE_RETRY(mount(nullptr, targetDir.c_str(),
+            nullptr, MS_SLAVE | MS_REC, nullptr)) == -1) {
+        *error_msg = CREATE_ERROR("Failed to set MS_SLAVE for %s", targetDir.c_str());
+        return false;
+    }
+    return true;
+}
+
 static bool mountPkgSpecificDir(const std::string& mntSourceRoot,
         const std::string& mntTargetRoot, const std::string& packageName,
         const char* dirName, std::string* error_msg) {
@@ -453,22 +470,12 @@
             mntSourceRoot.c_str(), dirName, packageName.c_str());
     std::string mntTargetDir = StringPrintf("%s/Android/%s/%s",
             mntTargetRoot.c_str(), dirName, packageName.c_str());
-    if (TEMP_FAILURE_RETRY(mount(mntSourceDir.c_str(), mntTargetDir.c_str(),
-            nullptr, MS_BIND | MS_REC, nullptr)) == -1) {
-        *error_msg = CREATE_ERROR("Failed to mount %s to %s: %s",
-                mntSourceDir.c_str(), mntTargetDir.c_str(), strerror(errno));
-        return false;
-    }
-    if (TEMP_FAILURE_RETRY(mount(nullptr, mntTargetDir.c_str(),
-            nullptr, MS_SLAVE | MS_REC, nullptr)) == -1) {
-        *error_msg = CREATE_ERROR("Failed to set MS_SLAVE for %s", mntTargetDir.c_str());
-        return false;
-    }
-    return true;
+    return bindMount(mntSourceDir, mntTargetDir, error_msg);
 }
 
 static bool preparePkgSpecificDirs(const std::vector<std::string>& packageNames,
-        const std::vector<std::string>& volumeLabels, userid_t userId, std::string* error_msg) {
+        const std::vector<std::string>& volumeLabels, bool mountAllObbs,
+        userid_t userId, std::string* error_msg) {
     for (auto& label : volumeLabels) {
         std::string mntSource = StringPrintf("/mnt/runtime/write/%s", label.c_str());
         std::string mntTarget = StringPrintf("/storage/%s", label.c_str());
@@ -479,7 +486,14 @@
         for (auto& package : packageNames) {
             mountPkgSpecificDir(mntSource, mntTarget, package, "data", error_msg);
             mountPkgSpecificDir(mntSource, mntTarget, package, "media", error_msg);
-            mountPkgSpecificDir(mntSource, mntTarget, package, "obb", error_msg);
+            if (!mountAllObbs) {
+                mountPkgSpecificDir(mntSource, mntTarget, package, "obb", error_msg);
+            }
+        }
+        if (mountAllObbs) {
+            StringAppendF(&mntSource, "/Android/obb");
+            StringAppendF(&mntTarget, "/Android/obb");
+            bindMount(mntSource, mntTarget, error_msg);
         }
     }
     return true;
@@ -500,7 +514,7 @@
         storageSource = "/mnt/runtime/read";
     } else if (mount_mode == MOUNT_EXTERNAL_WRITE) {
         storageSource = "/mnt/runtime/write";
-    } else if (mount_mode != MOUNT_EXTERNAL_FULL && !force_mount_namespace) {
+    } else if (mount_mode == MOUNT_EXTERNAL_NONE && !force_mount_namespace) {
         // Sane default of no storage visible
         return true;
     }
@@ -568,12 +582,28 @@
                         pkgSandboxDir.c_str(), strerror(errno));
                 return false;
             }
+            if (access("/storage/obb_mount", F_OK) == 0) {
+                if (mount_mode != MOUNT_EXTERNAL_INSTALLER) {
+                    remove("/storage/obb_mount");
+                }
+            } else {
+                if (mount_mode == MOUNT_EXTERNAL_INSTALLER) {
+                    int fd = TEMP_FAILURE_RETRY(open("/storage/obb_mount",
+                            O_RDWR | O_CREAT, 0660));
+                    if (fd == -1) {
+                        *error_msg = CREATE_ERROR("Couldn't create /storage/obb_mount: %s",
+                                strerror(errno));
+                        return false;
+                    }
+                    close(fd);
+                }
+            }
             // If the sandbox was already created by vold, only then set up the bind mounts for
             // pkg specific directories. Otherwise, leave as is and bind mounts will be taken
             // care of by vold later.
             if (sandboxAlreadyCreated) {
                 if (!preparePkgSpecificDirs(packages_for_uid, visible_vol_ids,
-                        user_id, error_msg)) {
+                        mount_mode == MOUNT_EXTERNAL_INSTALLER, user_id, error_msg)) {
                     return false;
                 }
             }
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 4b092b2..5901ece 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -16,6 +16,11 @@
 
 package com.android.server;
 
+import static android.Manifest.permission.INSTALL_PACKAGES;
+import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.OP_REQUEST_INSTALL_PACKAGES;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
 import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
 import static android.os.storage.OnObbStateChangeListener.ERROR_ALREADY_MOUNTED;
@@ -51,6 +56,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
@@ -116,6 +122,7 @@
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.IAppOpsService;
 import com.android.internal.os.AppFuseMount;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.FuseUnavailableMountException;
@@ -453,6 +460,9 @@
     private UserManagerInternal mUmInternal;
     private ActivityManagerInternal mAmInternal;
 
+    private IPackageManager mIPackageManager;
+    private IAppOpsService mIAppOpsService;
+
     private final Callbacks mCallbacks;
     private final LockPatternUtils mLockPatternUtils;
 
@@ -1570,6 +1580,10 @@
                 .registerScreenObserver(this);
 
         mSystemReady = true;
+        mIPackageManager = IPackageManager.Stub.asInterface(
+                ServiceManager.getService("package"));
+        mIAppOpsService = IAppOpsService.Stub.asInterface(
+                ServiceManager.getService(Context.APP_OPS_SERVICE));
         mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
     }
 
@@ -3117,7 +3131,8 @@
             throw new SecurityException("Shady looking path " + path);
         }
 
-        if (!mAmInternal.isAppStorageSandboxed(pid, uid)) {
+        final int mountMode = mAmInternal.getStorageMountMode(pid, uid);
+        if (mountMode == Zygote.MOUNT_EXTERNAL_FULL) {
             return path;
         }
 
@@ -3126,6 +3141,11 @@
             final String device = m.group(1);
             final String devicePath = m.group(2);
 
+            if (mountMode == Zygote.MOUNT_EXTERNAL_INSTALLER
+                    && devicePath.startsWith("Android/obb/")) {
+                return path;
+            }
+
             // Does path belong to any packages belonging to this UID? If so,
             // they get to go straight through to legacy paths.
             final String[] pkgs = mContext.getPackageManager().getPackagesForUid(uid);
@@ -3477,6 +3497,27 @@
         }
     }
 
+    private int getMountMode(int uid, String packageName) {
+        try {
+            if (Process.isIsolated(uid)) {
+                return Zygote.MOUNT_EXTERNAL_NONE;
+            }
+            if (mIPackageManager.checkUidPermission(WRITE_MEDIA_STORAGE, uid)
+                    == PERMISSION_GRANTED) {
+                return Zygote.MOUNT_EXTERNAL_FULL;
+            } else if (mIPackageManager.checkUidPermission(INSTALL_PACKAGES, uid)
+                    == PERMISSION_GRANTED || mIAppOpsService.checkOperation(
+                            OP_REQUEST_INSTALL_PACKAGES, uid, packageName) == MODE_ALLOWED) {
+                return Zygote.MOUNT_EXTERNAL_INSTALLER;
+            } else {
+                return Zygote.MOUNT_EXTERNAL_WRITE;
+            }
+        } catch (RemoteException e) {
+            // Should not happen
+        }
+        return Zygote.MOUNT_EXTERNAL_NONE;
+    }
+
     private static class Callbacks extends Handler {
         private static final int MSG_STORAGE_STATE_CHANGED = 1;
         private static final int MSG_VOLUME_STATE_CHANGED = 2;
@@ -3718,6 +3759,9 @@
 
         @Override
         public int getExternalStorageMountMode(int uid, String packageName) {
+            if (ENABLE_ISOLATED_STORAGE) {
+                return getMountMode(uid, packageName);
+            }
             // No locking - CopyOnWriteArrayList
             int mountMode = Integer.MAX_VALUE;
             for (ExternalStorageMountPolicy policy : mPolicies) {
@@ -3754,6 +3798,9 @@
             if (uid == Process.SYSTEM_UID) {
                 return true;
             }
+            if (ENABLE_ISOLATED_STORAGE) {
+                return getMountMode(uid, packageName) != Zygote.MOUNT_EXTERNAL_NONE;
+            }
             // No locking - CopyOnWriteArrayList
             for (ExternalStorageMountPolicy policy : mPolicies) {
                 final boolean policyHasStorage = policy.hasExternalStorage(uid, packageName);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 69cfcc2..8ce37a5 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -19441,16 +19441,13 @@
         }
 
         @Override
-        public boolean isAppStorageSandboxed(int pid, int uid) {
-            if (!StorageManager.hasIsolatedStorage()) {
-                return false;
-            }
+        public int getStorageMountMode(int pid, int uid) {
             if (uid == SHELL_UID || uid == ROOT_UID) {
-                return false;
+                return Zygote.MOUNT_EXTERNAL_FULL;
             }
             synchronized (mPidsSelfLocked) {
                 final ProcessRecord pr = mPidsSelfLocked.get(pid);
-                return pr == null || pr.mountMode != Zygote.MOUNT_EXTERNAL_FULL;
+                return pr == null ? Zygote.MOUNT_EXTERNAL_NONE : pr.mountMode;
             }
         }
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7d0f98c..6cfb846 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -24,7 +24,6 @@
 import static android.Manifest.permission.REQUEST_DELETE_PACKAGES;
 import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS;
 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
-import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
 import static android.content.Intent.ACTION_MAIN;
 import static android.content.Intent.CATEGORY_DEFAULT;
 import static android.content.Intent.CATEGORY_HOME;
@@ -20181,11 +20180,6 @@
                 if (Process.isIsolated(uid)) {
                     return Zygote.MOUNT_EXTERNAL_NONE;
                 }
-                if (StorageManager.hasIsolatedStorage()) {
-                    return checkUidPermission(WRITE_MEDIA_STORAGE, uid) == PERMISSION_GRANTED
-                            ? Zygote.MOUNT_EXTERNAL_FULL
-                            : Zygote.MOUNT_EXTERNAL_WRITE;
-                }
                 if (checkUidPermission(READ_EXTERNAL_STORAGE, uid) == PERMISSION_DENIED) {
                     return Zygote.MOUNT_EXTERNAL_DEFAULT;
                 }
diff --git a/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
index e6b328a..ec5d93e 100644
--- a/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
@@ -29,8 +29,7 @@
 import android.os.UserManagerInternal;
 import android.os.storage.StorageManagerInternal;
 
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.os.Zygote;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -38,6 +37,9 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class StorageManagerServiceTest {
@@ -97,15 +99,15 @@
         when(mPm.getPackagesForUid(eq(UID_GREY))).thenReturn(new String[] { PKG_GREY });
         when(mPm.getPackagesForUid(eq(UID_COLORS))).thenReturn(new String[] { PKG_RED, PKG_BLUE });
 
-        setIsAppStorageSandboxed(PID_BLUE, UID_COLORS, true);
-        setIsAppStorageSandboxed(PID_GREY, UID_GREY, true);
-        setIsAppStorageSandboxed(PID_RED, UID_COLORS, true);
+        setStorageMountMode(PID_BLUE, UID_COLORS, Zygote.MOUNT_EXTERNAL_WRITE);
+        setStorageMountMode(PID_GREY, UID_GREY, Zygote.MOUNT_EXTERNAL_WRITE);
+        setStorageMountMode(PID_RED, UID_COLORS, Zygote.MOUNT_EXTERNAL_WRITE);
 
         mService = new StorageManagerService(mContext);
     }
 
-    private void setIsAppStorageSandboxed(int pid, int uid, boolean sandboxed) {
-        when(mAmi.isAppStorageSandboxed(pid, uid)).thenReturn(sandboxed);
+    private void setStorageMountMode(int pid, int uid, int mountMode) {
+        when(mAmi.getStorageMountMode(pid, uid)).thenReturn(mountMode);
     }
 
     @Test
@@ -210,7 +212,7 @@
 
     @Test
     public void testPackageNotSandboxed() throws Exception {
-        setIsAppStorageSandboxed(PID_RED, UID_COLORS, false);
+        setStorageMountMode(PID_RED, UID_COLORS, Zygote.MOUNT_EXTERNAL_FULL);
 
         // Both app and system have the same view
         assertTranslation(
@@ -224,6 +226,29 @@
                 PID_RED, UID_COLORS);
     }
 
+    @Test
+    public void testInstallerPackage() throws Exception {
+        setStorageMountMode(PID_GREY, UID_GREY, Zygote.MOUNT_EXTERNAL_INSTALLER);
+
+        assertTranslation(
+                "/storage/emulated/0/Android/obb/com.grey/foo.jpg",
+                "/storage/emulated/0/Android/obb/com.grey/foo.jpg",
+                PID_GREY, UID_GREY);
+        assertTranslation(
+                "/storage/emulated/0/Android/obb/com.blue/bar.jpg",
+                "/storage/emulated/0/Android/obb/com.blue/bar.jpg",
+                PID_GREY, UID_GREY);
+
+        assertTranslation(
+                "/storage/emulated/0/Android/data/com.grey/foo.jpg",
+                "/storage/emulated/0/Android/data/com.grey/foo.jpg",
+                PID_GREY, UID_GREY);
+        assertTranslation(
+                "/storage/emulated/0/Android/sandbox/com.grey/Android/data/com.blue/bar.jpg",
+                "/storage/emulated/0/Android/data/com.blue/bar.jpg",
+                PID_GREY, UID_GREY);
+    }
+
     private void assertTranslation(String system, String sandbox,
             int pid, int uid) throws Exception {
         assertEquals(system,