Define an OEM directory, scan features and APKs.

To support OEM customizations, define a new top-level directory
that roughly mirrors the layout of the system partition.  Scan this
location for (non-privileged) apps, and for additional features.

Bug: 13340779
Change-Id: Idb6d6626655061ee31ad952cab734d30ea6130b9
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 54e2c0b..e96398a 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -41,6 +41,7 @@
     private static final String ENV_MEDIA_STORAGE = "MEDIA_STORAGE";
     private static final String ENV_SECONDARY_STORAGE = "SECONDARY_STORAGE";
     private static final String ENV_ANDROID_ROOT = "ANDROID_ROOT";
+    private static final String ENV_OEM_ROOT = "OEM_ROOT";
 
     /** {@hide} */
     public static final String DIR_ANDROID = "Android";
@@ -55,6 +56,7 @@
     public static final String DIRECTORY_ANDROID = DIR_ANDROID;
 
     private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system");
+    private static final File DIR_OEM_ROOT = getDirectory(ENV_OEM_ROOT, "/oem");
     private static final File DIR_MEDIA_STORAGE = getDirectory(ENV_MEDIA_STORAGE, "/data/media");
 
     private static final String CANONCIAL_EMULATED_STORAGE_TARGET = getCanonicalPathOrNull(
@@ -205,13 +207,24 @@
     }
 
     /**
-     * Gets the Android root directory.
+     * Return root of the "system" partition holding the core Android OS.
+     * Always present and mounted read-only.
      */
     public static File getRootDirectory() {
         return DIR_ANDROID_ROOT;
     }
 
     /**
+     * Return root directory of the "oem" partition holding OEM customizations,
+     * if any. If present, the partition is mounted read-only.
+     *
+     * @hide
+     */
+    public static File getOemDirectory() {
+        return DIR_OEM_ROOT;
+    }
+
+    /**
      * Gets the system directory available for secure storage.
      * If Encrypted File system is enabled, it returns an encrypted directory (/data/secure/system).
      * Otherwise, it returns the unencrypted /data/system directory.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index cbcf408..32de6cd 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -302,12 +302,15 @@
     // This is the object monitoring the privileged system app dir.
     final FileObserver mPrivilegedInstallObserver;
 
-    // This is the object monitoring the system app dir.
+    // This is the object monitoring the vendor app dir.
     final FileObserver mVendorInstallObserver;
 
     // This is the object monitoring the vendor overlay package dir.
     final FileObserver mVendorOverlayInstallObserver;
 
+    // This is the object monitoring the OEM app dir.
+    final FileObserver mOemInstallObserver;
+
     // This is the object monitoring mAppInstallDir.
     final FileObserver mAppInstallObserver;
 
@@ -1157,7 +1160,12 @@
             sUserManager = new UserManagerService(context, this,
                     mInstallLock, mPackages);
 
-            readPermissions();
+            // Read permissions and features from system
+            readPermissions(Environment.buildPath(
+                    Environment.getRootDirectory(), "etc", "permissions"), false);
+            // Only read features from OEM
+            readPermissions(Environment.buildPath(
+                    Environment.getOemDirectory(), "etc", "permissions"), true);
 
             mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
 
@@ -1343,6 +1351,14 @@
             scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
                     | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
 
+            // Collect all OEM packages.
+            File oemAppDir = new File(Environment.getOemDirectory(), "app");
+            mOemInstallObserver = new AppDirObserver(
+                    oemAppDir.getPath(), OBSERVER_EVENTS, true, false);
+            mOemInstallObserver.startWatching();
+            scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
+                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
+
             if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
             mInstaller.moveFiles();
 
@@ -1581,9 +1597,8 @@
         mSettings.removePackageLPw(ps.name);
     }
 
-    void readPermissions() {
+    void readPermissions(File libraryDir, boolean onlyFeatures) {
         // Read permissions from .../etc/permission directory.
-        File libraryDir = new File(Environment.getRootDirectory(), "etc/permissions");
         if (!libraryDir.exists() || !libraryDir.isDirectory()) {
             Slog.w(TAG, "No directory " + libraryDir + ", skipping");
             return;
@@ -1609,16 +1624,16 @@
                 continue;
             }
 
-            readPermissionsFromXml(f);
+            readPermissionsFromXml(f, onlyFeatures);
         }
 
         // Read permissions from .../etc/permissions/platform.xml last so it will take precedence
         final File permFile = new File(Environment.getRootDirectory(),
                 "etc/permissions/platform.xml");
-        readPermissionsFromXml(permFile);
+        readPermissionsFromXml(permFile, onlyFeatures);
     }
 
-    private void readPermissionsFromXml(File permFile) {
+    private void readPermissionsFromXml(File permFile, boolean onlyFeatures) {
         FileReader permReader = null;
         try {
             permReader = new FileReader(permFile);
@@ -1640,7 +1655,7 @@
                 }
 
                 String name = parser.getName();
-                if ("group".equals(name)) {
+                if ("group".equals(name) && !onlyFeatures) {
                     String gidStr = parser.getAttributeValue(null, "gid");
                     if (gidStr != null) {
                         int gid = Process.getGidForName(gidStr);
@@ -1652,7 +1667,7 @@
 
                     XmlUtils.skipCurrentTag(parser);
                     continue;
-                } else if ("permission".equals(name)) {
+                } else if ("permission".equals(name) && !onlyFeatures) {
                     String perm = parser.getAttributeValue(null, "name");
                     if (perm == null) {
                         Slog.w(TAG, "<permission> without name at "
@@ -1663,7 +1678,7 @@
                     perm = perm.intern();
                     readPermission(parser, perm);
 
-                } else if ("assign-permission".equals(name)) {
+                } else if ("assign-permission".equals(name) && !onlyFeatures) {
                     String perm = parser.getAttributeValue(null, "name");
                     if (perm == null) {
                         Slog.w(TAG, "<assign-permission> without name at "
@@ -1695,7 +1710,7 @@
                     perms.add(perm);
                     XmlUtils.skipCurrentTag(parser);
 
-                } else if ("library".equals(name)) {
+                } else if ("library".equals(name) && !onlyFeatures) {
                     String lname = parser.getAttributeValue(null, "name");
                     String lfile = parser.getAttributeValue(null, "file");
                     if (lname == null) {