Merge "Revert "Revert "Add compatibility metadata to OTA package"""
diff --git a/core/install_jni_libs.mk b/core/install_jni_libs.mk
index 625a8a2..35f4f55 100644
--- a/core/install_jni_libs.mk
+++ b/core/install_jni_libs.mk
@@ -18,9 +18,19 @@
 ifneq ($(filter tests samples, $(LOCAL_MODULE_TAGS)),)
 my_embed_jni := true
 endif
-ifeq ($(filter $(TARGET_OUT)/% $(TARGET_OUT_VENDOR)/% $(TARGET_OUT_OEM)/%, $(my_module_path)),)
-# If this app isn't to be installed to system partitions.
-my_embed_jni := true
+ifneq ($(BOARD_VNDK_VERSION),)
+  ifeq ($(filter $(TARGET_OUT)/%, $(my_module_path)),)
+    # If this app isn't to be installed to the system partition, and the device
+    # is fully treble-ized then jni libs are embedded, Otherwise, access to the
+    # directory where the lib is installed to (usually /vendor/lib) needs to be
+    # allowed for system processes, which is a Treble violation.
+    my_embed_jni := true
+  endif
+else
+  ifeq ($(filter $(TARGET_OUT)/% $(TARGET_OUT_VENDOR)/% $(TARGET_OUT_OEM)/%, $(my_module_path)),)
+    # If this app isn't to be installed to system, vendor, or oem partitions.
+    my_embed_jni := true
+  endif
 endif
 
 jni_shared_libraries :=
diff --git a/core/main.mk b/core/main.mk
index 4715a11..4e9a901 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -453,8 +453,8 @@
 
 ADDITIONAL_BUILD_PROPERTIES += net.bt.name=Android
 
-# enable vm tracing in files for now to help track
-# the cause of ANRs in the content process
+# Sets the location that the runtime dumps stack traces to when signalled
+# with SIGQUIT. Stack trace dumping is turned on for all android builds.
 ADDITIONAL_BUILD_PROPERTIES += dalvik.vm.stack-trace-file=/data/anr/traces.txt
 
 # ------------------------------------------------------------
diff --git a/target/product/core_minimal.mk b/target/product/core_minimal.mk
index 701a69c..14d4e6a 100644
--- a/target/product/core_minimal.mk
+++ b/target/product/core_minimal.mk
@@ -128,27 +128,6 @@
 PRODUCT_COPY_FILES += \
     system/core/rootdir/etc/ld.config.txt:system/etc/ld.config.txt
 
-# Different dexopt types for different package update/install times.
-# On eng builds, make "boot" reasons do pure JIT for faster turnaround.
-ifeq (eng,$(TARGET_BUILD_VARIANT))
-    PRODUCT_DEFAULT_PROPERTY_OVERRIDES += \
-        pm.dexopt.first-boot=verify-at-runtime \
-        pm.dexopt.boot=verify-at-runtime
-else
-    PRODUCT_DEFAULT_PROPERTY_OVERRIDES += \
-        pm.dexopt.first-boot=interpret-only \
-        pm.dexopt.boot=verify-profile
-endif
-PRODUCT_DEFAULT_PROPERTY_OVERRIDES += \
-    pm.dexopt.install=interpret-only \
-    pm.dexopt.bg-dexopt=speed-profile \
-    pm.dexopt.ab-ota=speed-profile \
-    pm.dexopt.nsys-library=speed \
-    pm.dexopt.shared-apk=speed \
-    pm.dexopt.forced-dexopt=speed \
-    pm.dexopt.core-app=speed
-
-
 # Enable boot.oat filtering of compiled classes to reduce boot.oat size. b/28026683
 PRODUCT_COPY_FILES += $(call add-to-product-copy-files-if-exists,\
     frameworks/base/compiled-classes-phone:system/etc/compiled-classes)
diff --git a/target/product/core_tiny.mk b/target/product/core_tiny.mk
index ef8794d..319a106 100644
--- a/target/product/core_tiny.mk
+++ b/target/product/core_tiny.mk
@@ -113,26 +113,6 @@
 PRODUCT_PROPERTY_OVERRIDES += \
     ro.carrier=unknown
 
-# Different dexopt types for different package update/install times.
-# On eng builds, make "boot" reasons do pure JIT for faster turnaround.
-ifeq (eng,$(TARGET_BUILD_VARIANT))
-    PRODUCT_DEFAULT_PROPERTY_OVERRIDES += \
-        pm.dexopt.first-boot=verify-at-runtime \
-        pm.dexopt.boot=verify-at-runtime
-else
-    PRODUCT_DEFAULT_PROPERTY_OVERRIDES += \
-        pm.dexopt.first-boot=interpret-only \
-        pm.dexopt.boot=verify-profile
-endif
-PRODUCT_DEFAULT_PROPERTY_OVERRIDES += \
-    pm.dexopt.install=interpret-only \
-    pm.dexopt.bg-dexopt=speed-profile \
-    pm.dexopt.ab-ota=speed-profile \
-    pm.dexopt.nsys-library=speed \
-    pm.dexopt.shared-apk=speed \
-    pm.dexopt.forced-dexopt=speed \
-    pm.dexopt.core-app=speed
-
 $(call inherit-product, $(SRC_TARGET_DIR)/product/runtime_libart.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/product/base.mk)
 $(call inherit-product-if-exists, frameworks/base/data/fonts/fonts.mk)
diff --git a/target/product/product_launched_with_n_mr1.mk b/target/product/product_launched_with_n_mr1.mk
new file mode 100644
index 0000000..65d4d3f
--- /dev/null
+++ b/target/product/product_launched_with_n_mr1.mk
@@ -0,0 +1,2 @@
+#PRODUCT_SHIPPING_API_LEVEL indicates the first api level, device has been commercially launced on.
+PRODUCT_SHIPPING_API_LEVEL := 25
diff --git a/target/product/runtime_libart.mk b/target/product/runtime_libart.mk
index fb52d67..ffdd904 100644
--- a/target/product/runtime_libart.mk
+++ b/target/product/runtime_libart.mk
@@ -82,3 +82,24 @@
     dalvik.vm.usejitprofiles=true \
     dalvik.vm.dexopt.secondary=true \
     dalvik.vm.appimageformat=lz4
+
+# Different dexopt types for different package update/install times.
+# On eng builds, make "boot" reasons only extract for faster turnaround.
+ifeq (eng,$(TARGET_BUILD_VARIANT))
+    PRODUCT_DEFAULT_PROPERTY_OVERRIDES += \
+        pm.dexopt.first-boot=extract \
+        pm.dexopt.boot=extract
+else
+    PRODUCT_DEFAULT_PROPERTY_OVERRIDES += \
+        pm.dexopt.first-boot=quicken \
+        pm.dexopt.boot=verify
+endif
+
+PRODUCT_DEFAULT_PROPERTY_OVERRIDES += \
+    pm.dexopt.install=quicken \
+    pm.dexopt.bg-dexopt=speed-profile \
+    pm.dexopt.ab-ota=speed-profile \
+    pm.dexopt.nsys-library=speed \
+    pm.dexopt.shared-apk=speed \
+    pm.dexopt.forced-dexopt=speed \
+    pm.dexopt.core-app=speed
diff --git a/tools/fs_config/Android.mk b/tools/fs_config/Android.mk
index 65f8a08..dcd41aa 100644
--- a/tools/fs_config/Android.mk
+++ b/tools/fs_config/Android.mk
@@ -113,6 +113,11 @@
 
 include $(BUILD_HOST_EXECUTABLE)
 fs_config_generate_bin := $(LOCAL_INSTALLED_MODULE)
+# List of all supported vendor, oem and odm Partitions
+fs_config_generate_extra_partition_list := $(strip \
+  $(if $(BOARD_USES_VENDORIMAGE)$(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE),vendor) \
+  $(if $(BOARD_USES_OEMIMAGE)$(BOARD_OEMIMAGE_FILE_SYSTEM_TYPE),oem) \
+  $(if $(BOARD_USES_ODMIMAGE)$(BOARD_ODMIMAGE_FILE_SYSTEM_TYPE),odm))
 
 ##################################
 # Generate the system/etc/fs_config_dirs binary file for the target
@@ -121,10 +126,13 @@
 
 LOCAL_MODULE := fs_config_dirs
 LOCAL_MODULE_CLASS := ETC
+LOCAL_REQUIRED_MODULES := $(foreach t,$(fs_config_generate_extra_partition_list),$(LOCAL_MODULE)_$(t))
 include $(BUILD_SYSTEM)/base_rules.mk
 $(LOCAL_BUILT_MODULE): $(fs_config_generate_bin)
 	@mkdir -p $(dir $@)
-	$< -D -o $@
+	$< -D $(if $(fs_config_generate_extra_partition_list), \
+	   -P '$(subst $(space),$(comma),$(addprefix -,$(fs_config_generate_extra_partition_list)))') \
+	   -o $@
 
 ##################################
 # Generate the system/etc/fs_config_files binary file for the target
@@ -133,10 +141,112 @@
 
 LOCAL_MODULE := fs_config_files
 LOCAL_MODULE_CLASS := ETC
+LOCAL_REQUIRED_MODULES := $(foreach t,$(fs_config_generate_extra_partition_list),$(LOCAL_MODULE)_$(t))
 include $(BUILD_SYSTEM)/base_rules.mk
 $(LOCAL_BUILT_MODULE): $(fs_config_generate_bin)
 	@mkdir -p $(dir $@)
-	$< -F -o $@
+	$< -F $(if $(fs_config_generate_extra_partition_list), \
+	   -P '$(subst $(space),$(comma),$(addprefix -,$(fs_config_generate_extra_partition_list)))') \
+	   -o $@
+
+ifneq ($(filter vendor,$(fs_config_generate_extra_partition_list)),)
+##################################
+# Generate the vendor/etc/fs_config_dirs binary file for the target
+# Add fs_config_dirs or fs_config_dirs_vendor to PRODUCT_PACKAGES in
+# the device make file to enable.
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := fs_config_dirs_vendor
+LOCAL_MODULE_CLASS := ETC
+LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs
+LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): $(fs_config_generate_bin)
+	@mkdir -p $(dir $@)
+	$< -D -P vendor -o $@
+
+##################################
+# Generate the vendor/etc/fs_config_files binary file for the target
+# Add fs_config_files or fs_config_files_vendor to PRODUCT_PACKAGES in
+# the device make file to enable
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := fs_config_files_vendor
+LOCAL_MODULE_CLASS := ETC
+LOCAL_INSTALLED_MODULE_STEM := fs_config_files
+LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): $(fs_config_generate_bin)
+	@mkdir -p $(dir $@)
+	$< -F -P vendor -o $@
+
+endif
+
+ifneq ($(filter oem,$(fs_config_generate_extra_partition_list)),)
+##################################
+# Generate the oem/etc/fs_config_dirs binary file for the target
+# Add fs_config_dirs or fs_config_dirs_oem to PRODUCT_PACKAGES in
+# the device make file to enable
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := fs_config_dirs_oem
+LOCAL_MODULE_CLASS := ETC
+LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs
+LOCAL_MODULE_PATH := $(TARGET_OUT_OEM)/etc
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): $(fs_config_generate_bin)
+	@mkdir -p $(dir $@)
+	$< -D -P oem -o $@
+
+##################################
+# Generate the oem/etc/fs_config_files binary file for the target
+# Add fs_config_files or fs_config_files_oem to PRODUCT_PACKAGES in
+# the device make file to enable
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := fs_config_files_oem
+LOCAL_MODULE_CLASS := ETC
+LOCAL_INSTALLED_MODULE_STEM := fs_config_files
+LOCAL_MODULE_PATH := $(TARGET_OUT_OEM)/etc
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): $(fs_config_generate_bin)
+	@mkdir -p $(dir $@)
+	$< -F -P oem -o $@
+
+endif
+
+ifneq ($(filter odm,$(fs_config_generate_extra_partition_list)),)
+##################################
+# Generate the odm/etc/fs_config_dirs binary file for the target
+# Add fs_config_dirs or fs_config_dirs_odm to PRODUCT_PACKAGES in
+# the device make file to enable
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := fs_config_dirs_odm
+LOCAL_MODULE_CLASS := ETC
+LOCAL_INSTALLED_MODULE_STEM := fs_config_dirs
+LOCAL_MODULE_PATH := $(TARGET_OUT_ODM)/etc
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): $(fs_config_generate_bin)
+	@mkdir -p $(dir $@)
+	$< -D -P odm -o $@
+
+##################################
+# Generate the odm/etc/fs_config_files binary file for the target
+# Add fs_config_files of fs_config_files_odm to PRODUCT_PACKAGES in
+# the device make file to enable
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := fs_config_files_odm
+LOCAL_MODULE_CLASS := ETC
+LOCAL_INSTALLED_MODULE_STEM := fs_config_files
+LOCAL_MODULE_PATH := $(TARGET_OUT_ODM)/etc
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): $(fs_config_generate_bin)
+	@mkdir -p $(dir $@)
+	$< -F -P odm -o $@
+
+endif
 
 # The newer passwd/group targets are only generated if you
 # use the new TARGET_FS_CONFIG_GEN method.
@@ -195,3 +305,36 @@
 my_fs_config_h :=
 fs_config_generate_bin :=
 my_gen_oem_aid :=
+
+# -----------------------------------------------------------------------------
+# Unit tests.
+# -----------------------------------------------------------------------------
+
+test_c_flags := \
+    -fstack-protector-all \
+    -g \
+    -Wall \
+    -Wextra \
+    -Werror \
+    -fno-builtin \
+    -DANDROID_FILESYSTEM_CONFIG='"android_filesystem_config_test_data.h"'
+
+##################################
+# test executable
+include $(CLEAR_VARS)
+LOCAL_MODULE := fs_config_generate_test
+LOCAL_SRC_FILES := fs_config_generate.c
+LOCAL_SHARED_LIBRARIES := libcutils
+LOCAL_CFLAGS := $(test_c_flags)
+LOCAL_MODULE_RELATIVE_PATH := fs_config-unit-tests
+LOCAL_GTEST := false
+include $(BUILD_HOST_NATIVE_TEST)
+
+##################################
+# gTest tool
+include $(CLEAR_VARS)
+LOCAL_MODULE := fs_config-unit-tests
+LOCAL_CFLAGS += $(test_c_flags) -DHOST
+LOCAL_SHARED_LIBRARIES := liblog libcutils libbase
+LOCAL_SRC_FILES := fs_config_test.cpp
+include $(BUILD_HOST_NATIVE_TEST)
diff --git a/tools/fs_config/README b/tools/fs_config/README
index 9919131..5af407f 100644
--- a/tools/fs_config/README
+++ b/tools/fs_config/README
@@ -156,9 +156,28 @@
 ${OUT} directory are used in the final stages when building the filesystem
 images to set the file and directory properties.
 
+For systems with separate partition images, such as vendor or oem,
+fs_config_generate can be instructed to filter the specific file references
+to land in each partition's etc/fs_config_dirs or etc/fs_config_files
+locations. The filter can be instructed to blacklist a partition's data by
+providing the comma separated minus sign prefixed partition names. The filter
+can be instructed to whitelist partition data by providing the partition name.
+
+For example:
+- For system.img, but not vendor, oem or odm file references:
+      -P -vendor,-oem,-odm
+  This makes sure the results only contain content associated with the
+  system, and not vendor, oem or odm, blacklisting their content.
+- For vendor.img file references: -P vendor
+- For oem.img file references: -P oem
+- For odm.img file references: -P odm
+
 fs_config_generate --help reports:
 
 Generate binary content for fs_config_dirs (-D) and fs_config_files (-F)
-from device-specific android_filesystem_config.h override
+from device-specific android_filesystem_config.h override. Filter based
+on a comma separated partition list (-P) whitelist or prefixed by a
+minus blacklist. Partitions are identified as path references to
+<partition>/ or system/<partition>
 
-Usage: fs_config_generate -D|-F [-o output-file]
+Usage: fs_config_generate -D|-F [-P list] [-o output-file]
diff --git a/tools/fs_config/android_filesystem_config_test_data.h b/tools/fs_config/android_filesystem_config_test_data.h
new file mode 100644
index 0000000..07bc8e5
--- /dev/null
+++ b/tools/fs_config/android_filesystem_config_test_data.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <private/android_filesystem_config.h>
+
+/* Test Data */
+
+#undef NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS
+#undef NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_FILES
+
+static const struct fs_path_config android_device_dirs[] = {
+    {00555, AID_ROOT, AID_SYSTEM, 0, "system/etc"},
+    {00555, AID_ROOT, AID_SYSTEM, 0, "vendor/etc"},
+    {00555, AID_ROOT, AID_SYSTEM, 0, "oem/etc"},
+    {00555, AID_ROOT, AID_SYSTEM, 0, "odm/etc"},
+    {00755, AID_SYSTEM, AID_ROOT, 0, "system/oem/etc"},
+    {00755, AID_SYSTEM, AID_ROOT, 0, "system/odm/etc"},
+    {00755, AID_SYSTEM, AID_ROOT, 0, "system/vendor/etc"},
+    {00755, AID_SYSTEM, AID_ROOT, 0, "data/misc"},
+    {00755, AID_SYSTEM, AID_ROOT, 0, "oem/data/misc"},
+    {00755, AID_SYSTEM, AID_ROOT, 0, "odm/data/misc"},
+    {00755, AID_SYSTEM, AID_ROOT, 0, "vendor/data/misc"},
+    {00555, AID_SYSTEM, AID_ROOT, 0, "etc"},
+};
+
+static const struct fs_path_config android_device_files[] = {
+    {00444, AID_ROOT, AID_SYSTEM, 0, "system/etc/fs_config_dirs"},
+    {00444, AID_ROOT, AID_SYSTEM, 0, "vendor/etc/fs_config_dirs"},
+    {00444, AID_ROOT, AID_SYSTEM, 0, "oem/etc/fs_config_dirs"},
+    {00444, AID_ROOT, AID_SYSTEM, 0, "odm/etc/fs_config_dirs"},
+    {00444, AID_ROOT, AID_SYSTEM, 0, "system/etc/fs_config_files"},
+    {00444, AID_ROOT, AID_SYSTEM, 0, "vendor/etc/fs_config_files"},
+    {00444, AID_ROOT, AID_SYSTEM, 0, "oem/etc/fs_config_files"},
+    {00444, AID_ROOT, AID_SYSTEM, 0, "odm/etc/fs_config_files"},
+    {00644, AID_SYSTEM, AID_ROOT, 0, "system/vendor/etc/fs_config_dirs"},
+    {00644, AID_SYSTEM, AID_ROOT, 0, "system/oem/etc/fs_config_dirs"},
+    {00644, AID_SYSTEM, AID_ROOT, 0, "system/odm/etc/fs_config_dirs"},
+    {00644, AID_SYSTEM, AID_ROOT, 0, "system/vendor/etc/fs_config_files"},
+    {00644, AID_SYSTEM, AID_ROOT, 0, "system/oem/etc/fs_config_files"},
+    {00644, AID_SYSTEM, AID_ROOT, 0, "system/odm/etc/fs_config_files"},
+    {00644, AID_SYSTEM, AID_ROOT, 0, "etc/fs_config_files"},
+    {00666, AID_ROOT, AID_SYSTEM, 0, "data/misc/oem"},
+};
diff --git a/tools/fs_config/fs_config_generate.c b/tools/fs_config/fs_config_generate.c
index c06213f..cb7ff9d 100644
--- a/tools/fs_config/fs_config_generate.c
+++ b/tools/fs_config/fs_config_generate.c
@@ -14,9 +14,11 @@
  * limitations under the License.
  */
 
+#include <ctype.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 
 #include <private/android_filesystem_config.h>
@@ -28,38 +30,57 @@
  * the binary format used in the /system/etc/fs_config_dirs and
  * the /system/etc/fs_config_files to be used by the runtimes.
  */
+#ifdef ANDROID_FILESYSTEM_CONFIG
+#include ANDROID_FILESYSTEM_CONFIG
+#else
 #include "android_filesystem_config.h"
+#endif
 
 #ifdef NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS
-  static const struct fs_path_config android_device_dirs[] = {
-};
+static const struct fs_path_config android_device_dirs[] = { };
 #endif
 
 #ifdef NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_FILES
 static const struct fs_path_config android_device_files[] = {
 #ifdef NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS
-    { 0, AID_ROOT, AID_ROOT, 0, "system/etc/fs_config_dirs" },
+    {0000, AID_ROOT, AID_ROOT, 0, "system/etc/fs_config_dirs"},
+    {0000, AID_ROOT, AID_ROOT, 0, "vendor/etc/fs_config_dirs"},
+    {0000, AID_ROOT, AID_ROOT, 0, "oem/etc/fs_config_dirs"},
+    {0000, AID_ROOT, AID_ROOT, 0, "odm/etc/fs_config_dirs"},
 #endif
-    { 0, AID_ROOT, AID_ROOT, 0, "system/etc/fs_config_files" },
+    {0000, AID_ROOT, AID_ROOT, 0, "system/etc/fs_config_files"},
+    {0000, AID_ROOT, AID_ROOT, 0, "vendor/etc/fs_config_files"},
+    {0000, AID_ROOT, AID_ROOT, 0, "oem/etc/fs_config_files"},
+    {0000, AID_ROOT, AID_ROOT, 0, "odm/etc/fs_config_files"},
 };
 #endif
 
 static void usage() {
   fprintf(stderr,
     "Generate binary content for fs_config_dirs (-D) and fs_config_files (-F)\n"
-    "from device-specific android_filesystem_config.h override\n\n"
-    "Usage: fs_config_generate -D|-F [-o output-file]\n");
+    "from device-specific android_filesystem_config.h override.  Filter based\n"
+    "on a comma separated partition list (-P) whitelist or prefixed by a\n"
+    "minus blacklist.  Partitions are identified as path references to\n"
+    "<partition>/ or system/<partition>/\n\n"
+    "Usage: fs_config_generate -D|-F [-P list] [-o output-file]\n");
 }
 
-int main(int argc, char** argv) {
-  const struct fs_path_config *pc;
-  const struct fs_path_config *end;
-  bool dir = false, file = false;
-  FILE *fp = stdout;
-  int opt;
+/* If tool switches to C++, use android-base/macros.h array_size() */
+#ifndef ARRAY_SIZE /* popular macro */
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
 
-  while((opt = getopt(argc, argv, "DFho:")) != -1) {
-    switch(opt) {
+int main(int argc, char** argv) {
+  const struct fs_path_config* pc;
+  const struct fs_path_config* end;
+  bool dir = false, file = false;
+  const char* partitions = NULL;
+  FILE* fp = stdout;
+  int opt;
+  static const char optstring[] = "DFP:ho:";
+
+  while ((opt = getopt(argc, argv, optstring)) != -1) {
+    switch (opt) {
     case 'D':
       if (file) {
         fprintf(stderr, "Must specify only -D or -F\n");
@@ -76,6 +97,30 @@
       }
       file = true;
       break;
+    case 'P':
+      if (partitions) {
+        fprintf(stderr, "Specify only one partition list\n");
+        usage();
+        exit(EXIT_FAILURE);
+      }
+      while (*optarg && isspace(*optarg)) ++optarg;
+      if (!optarg[0]) {
+        fprintf(stderr, "Partition list empty\n");
+        usage();
+        exit(EXIT_FAILURE);
+      }
+      if (!optarg[1]) {
+        fprintf(stderr, "Partition list too short \"%s\"\n", optarg);
+        usage();
+        exit(EXIT_FAILURE);
+      }
+      if ((optarg[0] == '-') && strchr(optstring, optarg[1]) && !optarg[2]) {
+        fprintf(stderr, "Partition list is a flag \"%s\"\n", optarg);
+        usage();
+        exit(EXIT_FAILURE);
+      }
+      partitions = optarg;
+      break;
     case 'o':
       if (fp != stdout) {
         fprintf(stderr, "Specify only one output file\n");
@@ -97,6 +142,12 @@
     }
   }
 
+  if (optind < argc) {
+    fprintf(stderr, "Unknown non-argument \"%s\"\n", argv[optind]);
+    usage();
+    exit(EXIT_FAILURE);
+  }
+
   if (!file && !dir) {
     fprintf(stderr, "Must specify either -F or -D\n");
     usage();
@@ -105,19 +156,64 @@
 
   if (dir) {
     pc = android_device_dirs;
-    end = &android_device_dirs[sizeof(android_device_dirs) / sizeof(android_device_dirs[0])];
+    end = &android_device_dirs[ARRAY_SIZE(android_device_dirs)];
   } else {
     pc = android_device_files;
-    end = &android_device_files[sizeof(android_device_files) / sizeof(android_device_files[0])];
+    end = &android_device_files[ARRAY_SIZE(android_device_files)];
   }
-  for(; (pc < end) && pc->prefix; pc++) {
+  for (; (pc < end) && pc->prefix; pc++) {
+    bool submit;
     char buffer[512];
     ssize_t len = fs_config_generate(buffer, sizeof(buffer), pc);
     if (len < 0) {
       fprintf(stderr, "Entry too large\n");
       exit(EXIT_FAILURE);
     }
-    if (fwrite(buffer, 1, len, fp) != (size_t)len) {
+    submit = true;
+    if (partitions) {
+      char* partitions_copy = strdup(partitions);
+      char* arg = partitions_copy;
+      char* sv = NULL; /* Do not leave uninitialized, NULL is known safe. */
+      /* Deal with case all iterated partitions are blacklists with no match */
+      bool all_blacklist_but_no_match = true;
+      submit = false;
+
+      if (!partitions_copy) {
+        fprintf(stderr, "Failed to allocate a copy of %s\n", partitions);
+        exit(EXIT_FAILURE);
+      }
+      /* iterate through (officially) comma separated list of partitions */
+      while (!!(arg = strtok_r(arg, ",:; \t\n\r\f", &sv))) {
+        static const char system[] = "system/";
+        size_t plen;
+        bool blacklist = false;
+        if (*arg == '-') {
+          blacklist = true;
+          ++arg;
+        } else {
+          all_blacklist_but_no_match = false;
+        }
+        plen = strlen(arg);
+        /* deal with evil callers */
+        while (arg[plen - 1] == '/') {
+          --plen;
+        }
+        /* check if we have <partition>/ or /system/<partition>/ */
+        if ((!strncmp(pc->prefix, arg, plen) && (pc->prefix[plen] == '/')) ||
+            (!strncmp(pc->prefix, system, strlen(system)) &&
+             !strncmp(pc->prefix + strlen(system), arg, plen) &&
+             (pc->prefix[strlen(system) + plen] == '/'))) {
+          all_blacklist_but_no_match = false;
+          /* we have a match !!! */
+          if (!blacklist) submit = true;
+          break;
+        }
+        arg = NULL;
+      }
+      free(partitions_copy);
+      if (all_blacklist_but_no_match) submit = true;
+    }
+    if (submit && (fwrite(buffer, 1, len, fp) != (size_t)len)) {
       fprintf(stderr, "Write failure\n");
       exit(EXIT_FAILURE);
     }
diff --git a/tools/fs_config/fs_config_test.cpp b/tools/fs_config/fs_config_test.cpp
new file mode 100644
index 0000000..f95a4ca
--- /dev/null
+++ b/tools/fs_config/fs_config_test.cpp
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <sys/cdefs.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/strings.h>
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+#include <private/android_filesystem_config.h>
+#include <private/fs_config.h>
+
+#include "android_filesystem_config_test_data.h"
+
+// must run test in the test directory
+const static char fs_config_generate_command[] = "./fs_config_generate_test";
+
+static std::string popenToString(std::string command) {
+  std::string ret;
+
+  FILE* fp = popen(command.c_str(), "r");
+  if (fp) {
+    if (!android::base::ReadFdToString(fileno(fp), &ret)) ret = "";
+    pclose(fp);
+  }
+  return ret;
+}
+
+static void confirm(std::string&& data, const fs_path_config* config,
+                    ssize_t num_config) {
+  const struct fs_path_config_from_file* pc =
+      reinterpret_cast<const fs_path_config_from_file*>(data.c_str());
+  size_t len = data.size();
+
+  ASSERT_TRUE(config != NULL);
+  ASSERT_LT(0, num_config);
+
+  while (len > 0) {
+    uint16_t host_len = pc->len;
+    if (host_len > len) break;
+
+    EXPECT_EQ(config->mode, pc->mode);
+    EXPECT_EQ(config->uid, pc->uid);
+    EXPECT_EQ(config->gid, pc->gid);
+    EXPECT_EQ(config->capabilities, pc->capabilities);
+    EXPECT_STREQ(config->prefix, pc->prefix);
+
+    EXPECT_LT(0, num_config);
+    --num_config;
+    if (num_config >= 0) ++config;
+    pc = reinterpret_cast<const fs_path_config_from_file*>(
+        reinterpret_cast<const char*>(pc) + host_len);
+    len -= host_len;
+  }
+  EXPECT_EQ(0, num_config);
+}
+
+/* See local android_filesystem_config.h for test data */
+
+TEST(fs_conf_test, dirs) {
+  confirm(popenToString(
+              android::base::StringPrintf("%s -D", fs_config_generate_command)),
+          android_device_dirs, arraysize(android_device_dirs));
+}
+
+TEST(fs_conf_test, files) {
+  confirm(popenToString(
+              android::base::StringPrintf("%s -F", fs_config_generate_command)),
+          android_device_files, arraysize(android_device_files));
+}
+
+static const char vendor_str[] = "vendor/";
+static const char vendor_alt_str[] = "system/vendor/";
+static const char oem_str[] = "oem/";
+static const char oem_alt_str[] = "system/oem/";
+static const char odm_str[] = "odm/";
+static const char odm_alt_str[] = "system/odm/";
+
+TEST(fs_conf_test, system_dirs) {
+  std::vector<fs_path_config> dirs;
+  const fs_path_config* config = android_device_dirs;
+  for (size_t num = arraysize(android_device_dirs); num; --num) {
+    if (!android::base::StartsWith(config->prefix, vendor_str) &&
+        !android::base::StartsWith(config->prefix, vendor_alt_str) &&
+        !android::base::StartsWith(config->prefix, oem_str) &&
+        !android::base::StartsWith(config->prefix, oem_alt_str) &&
+        !android::base::StartsWith(config->prefix, odm_str) &&
+        !android::base::StartsWith(config->prefix, odm_alt_str)) {
+      dirs.emplace_back(*config);
+    }
+    ++config;
+  }
+  confirm(popenToString(android::base::StringPrintf(
+              "%s -D -P -vendor,-oem,-odm", fs_config_generate_command)),
+          &dirs[0], dirs.size());
+}
+
+TEST(fs_conf_test, vendor_dirs) {
+  std::vector<fs_path_config> dirs;
+  const fs_path_config* config = android_device_dirs;
+  for (size_t num = arraysize(android_device_dirs); num; --num) {
+    if (android::base::StartsWith(config->prefix, vendor_str) ||
+        android::base::StartsWith(config->prefix, vendor_alt_str)) {
+      dirs.emplace_back(*config);
+    }
+    ++config;
+  }
+  confirm(popenToString(android::base::StringPrintf(
+              "%s -D -P vendor", fs_config_generate_command)),
+          &dirs[0], dirs.size());
+}
+
+TEST(fs_conf_test, oem_dirs) {
+  std::vector<fs_path_config> dirs;
+  const fs_path_config* config = android_device_dirs;
+  for (size_t num = arraysize(android_device_dirs); num; --num) {
+    if (android::base::StartsWith(config->prefix, oem_str) ||
+        android::base::StartsWith(config->prefix, oem_alt_str)) {
+      dirs.emplace_back(*config);
+    }
+    ++config;
+  }
+  confirm(popenToString(android::base::StringPrintf(
+              "%s -D -P oem", fs_config_generate_command)),
+          &dirs[0], dirs.size());
+}
+
+TEST(fs_conf_test, odm_dirs) {
+  std::vector<fs_path_config> dirs;
+  const fs_path_config* config = android_device_dirs;
+  for (size_t num = arraysize(android_device_dirs); num; --num) {
+    if (android::base::StartsWith(config->prefix, odm_str) ||
+        android::base::StartsWith(config->prefix, odm_alt_str)) {
+      dirs.emplace_back(*config);
+    }
+    ++config;
+  }
+  confirm(popenToString(android::base::StringPrintf(
+              "%s -D -P odm", fs_config_generate_command)),
+          &dirs[0], dirs.size());
+}
+
+TEST(fs_conf_test, system_files) {
+  std::vector<fs_path_config> files;
+  const fs_path_config* config = android_device_files;
+  for (size_t num = arraysize(android_device_files); num; --num) {
+    if (!android::base::StartsWith(config->prefix, vendor_str) &&
+        !android::base::StartsWith(config->prefix, vendor_alt_str) &&
+        !android::base::StartsWith(config->prefix, oem_str) &&
+        !android::base::StartsWith(config->prefix, oem_alt_str) &&
+        !android::base::StartsWith(config->prefix, odm_str) &&
+        !android::base::StartsWith(config->prefix, odm_alt_str)) {
+      files.emplace_back(*config);
+    }
+    ++config;
+  }
+  confirm(popenToString(android::base::StringPrintf(
+              "%s -F -P -vendor,-oem,-odm", fs_config_generate_command)),
+          &files[0], files.size());
+}
+
+TEST(fs_conf_test, vendor_files) {
+  std::vector<fs_path_config> files;
+  const fs_path_config* config = android_device_files;
+  for (size_t num = arraysize(android_device_files); num; --num) {
+    if (android::base::StartsWith(config->prefix, vendor_str) ||
+        android::base::StartsWith(config->prefix, vendor_alt_str)) {
+      files.emplace_back(*config);
+    }
+    ++config;
+  }
+  confirm(popenToString(android::base::StringPrintf(
+              "%s -F -P vendor", fs_config_generate_command)),
+          &files[0], files.size());
+}
+
+TEST(fs_conf_test, oem_files) {
+  std::vector<fs_path_config> files;
+  const fs_path_config* config = android_device_files;
+  for (size_t num = arraysize(android_device_files); num; --num) {
+    if (android::base::StartsWith(config->prefix, oem_str) ||
+        android::base::StartsWith(config->prefix, oem_alt_str)) {
+      files.emplace_back(*config);
+    }
+    ++config;
+  }
+  confirm(popenToString(android::base::StringPrintf(
+              "%s -F -P oem", fs_config_generate_command)),
+          &files[0], files.size());
+}
+
+TEST(fs_conf_test, odm_files) {
+  std::vector<fs_path_config> files;
+  const fs_path_config* config = android_device_files;
+  for (size_t num = arraysize(android_device_files); num; --num) {
+    if (android::base::StartsWith(config->prefix, odm_str) ||
+        android::base::StartsWith(config->prefix, odm_alt_str)) {
+      files.emplace_back(*config);
+    }
+    ++config;
+  }
+  confirm(popenToString(android::base::StringPrintf(
+              "%s -F -P odm", fs_config_generate_command)),
+          &files[0], files.size());
+}