diff --git a/tools/README b/tools/README
new file mode 100644
index 0000000000000000000000000000000000000000..9b329f6e8d227452bf0ed0584b924a835e2aefcd
--- /dev/null
+++ b/tools/README
@@ -0,0 +1,83 @@
+This directory contains a number of tools related to policy, some of
+which are used in building and validating the policy and others are
+available for help in auditing and analyzing policy.  The tools are
+described further below.
+
+checkfc
+   A utility for checking the validity of a file_contexts or a
+   property_contexts configuration file.  Used as part of the policy
+   build to validate both files.  Requires the sepolicy file as an
+   argument in order to check the validity of the security contexts
+   in the file_contexts or property_contexts file.
+
+   Usage:
+   checkfc sepolicy file_contexts
+   checkfc -p sepolicy property_contexts
+
+checkseapp
+    A utility for merging together the main seapp_contexts
+    configuration and the device-specific one, and simultaneously
+    checking the validity of the configurations. Used as part of the
+    policy build process to merge and validate the configuration.
+
+    Usage:
+    checkseapp -p sepolicy input_seapp_contexts0 [input_seapp_contexts1...] -o seapp_contexts
+
+insertkeys.py
+    A helper script for mapping tags in the signature stanzas of
+    mac_permissions.xml to public keys found in pem files.  This
+    script is described further in the top-level sepolicy/README.
+
+sepolicy-check
+    A tool for auditing a sepolicy file for any allow rule that grants
+    a given permission.
+
+    Usage:
+    sepolicy-check -s <domain> -t <type> -c <class> -p <permission> -P out/target/product/<board>/root/sepolicy
+
+sepolicy-analyze
+    A tool for performing various kinds of analysis on a sepolicy
+    file.  The current kinds of analysis that are currently supported
+    include:
+
+    TYPE EQUIVALENCE
+    sepolicy-analyze -e -P out/target/product/<board>/root/sepolicy
+
+    Display all type pairs that are "equivalent", i.e. they are
+    identical with respect to allow rules, including indirect allow
+    rules via attributes and default-enabled conditional rules
+    (i.e. default boolean values yield a true conditional expression).
+
+    Equivalent types are candidates for being coalesced into a single
+    type.  However, there may be legitimate reasons for them to remain
+    separate, for example: - the types may differ in a respect not
+    included in the current analysis, such as default-disabled
+    conditional rules, audit-related rules (auditallow or dontaudit),
+    default type transitions, or constraints (e.g. mls), or - the
+    current policy may be overly permissive with respect to one or the
+    other of the types and thus the correct action may be to tighten
+    access to one or the other rather than coalescing them together,
+    or - the domains that would in fact have different accesses to the
+    types may not yet be defined or may be unconfined in the policy
+    you are analyzing.
+
+    TYPE DIFFERENCE
+    sepolicy-analyze -d -P out/target/product/<board>/root/sepolicy
+
+    Display type pairs that differ and the first difference found
+    between the two types.  This may be used in looking for similar
+    types that are not equivalent but may be candidates for coalescing.
+
+    DUPLICATE ALLOW RULES
+    sepolicy-analyze -D -P out/target/product/<board>/root/sepolicy
+
+    Displays duplicate allow rules, i.e. pairs of allow rules that
+    grant the same permissions where one allow rule is written
+    directly in terms of individual types and the other is written in
+    terms of attributes associated with those same types.  The rule
+    with individual types is a candidate for removal.  The rule with
+    individual types may be directly represented in the source policy
+    or may be a result of expansion of a type negation (e.g. domain
+    -foo -bar is expanded to individual allow rules by the policy
+    compiler).  Domains with unconfineddomain will typically have such
+    duplicate rules as a natural side effect and can be ignored.
diff --git a/tools/sepolicy-analyze.c b/tools/sepolicy-analyze.c
index 9b3d444c940effb51bb7e05424fa5570ceef6bb0..1901033a4868ff9437bd34ace9b74b02c974c1de 100644
--- a/tools/sepolicy-analyze.c
+++ b/tools/sepolicy-analyze.c
@@ -14,7 +14,7 @@
 
 void usage(char *arg0)
 {
-    fprintf(stderr, "%s [-e|--equiv] [-d|--diff] -P <policy file>\n", arg0);
+    fprintf(stderr, "%s [-e|--equiv] [-d|--diff] [-D|--dups] -P <policy file>\n", arg0);
     exit(1);
 }
 
@@ -173,18 +173,18 @@ static void free_type_rules(struct avtab_node *l)
     }
 }
 
-static void display_allow(policydb_t *policydb, struct avtab_node *n, int idx,
-                         uint32_t perms)
+static void display_allow(policydb_t *policydb, avtab_key_t *key, int idx,
+                          uint32_t perms)
 {
     printf("    allow %s %s:%s { %s };\n",
-           policydb->p_type_val_to_name[n->key.source_type
-                                        ? n->key.source_type - 1 : idx],
-           n->key.target_type == n->key.source_type ? "self" :
-           policydb->p_type_val_to_name[n->key.target_type
-                                        ? n->key.target_type - 1 : idx],
-           policydb->p_class_val_to_name[n->key.target_class - 1],
+           policydb->p_type_val_to_name[key->source_type
+                                        ? key->source_type - 1 : idx],
+           key->target_type == key->source_type ? "self" :
+           policydb->p_type_val_to_name[key->target_type
+                                        ? key->target_type - 1 : idx],
+           policydb->p_class_val_to_name[key->target_class - 1],
            sepol_av_to_string
-           (policydb, n->key.target_class, perms));
+           (policydb, key->target_class, perms));
 }
 
 static int find_match(policydb_t *policydb, struct avtab_node *l1,
@@ -213,9 +213,9 @@ static int find_match(policydb_t *policydb, struct avtab_node *l1,
         perms2 = c->datum.data & ~l1->datum.data;
         if (perms1 || perms2) {
             if (perms1)
-                display_allow(policydb, l1, idx1, perms1);
+                display_allow(policydb, &l1->key, idx1, perms1);
             if (perms2)
-                display_allow(policydb, c, idx2, perms2);
+                display_allow(policydb, &c->key, idx2, perms2);
             printf("\n");
             return 1;
         }
@@ -311,9 +311,9 @@ static int analyze_types(policydb_t * policydb, char equiv, char diff)
                             continue;
                     }
                     if (l1)
-                        display_allow(policydb, l1, i, l1->datum.data);
+                        display_allow(policydb, &l1->key, i, l1->datum.data);
                     if (l2)
-                        display_allow(policydb, l2, j, l2->datum.data);
+                        display_allow(policydb, &l2->key, j, l2->datum.data);
                     printf("\n");
                 }
                 continue;
@@ -334,22 +334,76 @@ static int analyze_types(policydb_t * policydb, char equiv, char diff)
     return 0;
 }
 
+static int find_dups_helper(avtab_key_t * k, avtab_datum_t * d,
+                            void *args)
+{
+    policydb_t *policydb = args;
+    ebitmap_t *sattr, *tattr;
+    ebitmap_node_t *snode, *tnode;
+    unsigned int i, j;
+    avtab_key_t avkey;
+    avtab_ptr_t node;
+
+    if (!(k->specified & AVTAB_ALLOWED))
+        return 0;
+
+    avkey.target_class = k->target_class;
+    avkey.specified = k->specified;
+
+    sattr = &policydb->type_attr_map[k->source_type - 1];
+    tattr = &policydb->type_attr_map[k->target_type - 1];
+    ebitmap_for_each_bit(sattr, snode, i) {
+        if (!ebitmap_node_get_bit(snode, i))
+            continue;
+        ebitmap_for_each_bit(tattr, tnode, j) {
+            if (!ebitmap_node_get_bit(tnode, j))
+                continue;
+            avkey.source_type = i + 1;
+            avkey.target_type = j + 1;
+            if (avkey.source_type == k->source_type &&
+                avkey.target_type == k->target_type)
+                continue;
+            for (node = avtab_search_node(&policydb->te_avtab, &avkey);
+                 node != NULL;
+                 node = avtab_search_node_next(node, avkey.specified)) {
+                if (node->datum.data & d->data) {
+                    uint32_t perms = node->datum.data & d->data;
+                    printf("Duplicate allow rule found:\n");
+                    display_allow(policydb, k, i, perms);
+                    display_allow(policydb, &node->key, i, perms);
+                    printf("\n");
+                }
+            }
+        }
+    }
+
+    return 0;
+}
+
+static int find_dups(policydb_t * policydb)
+{
+    if (avtab_map(&policydb->te_avtab, find_dups_helper, policydb))
+        return -1;
+    return 0;
+}
+
 int main(int argc, char **argv)
 {
     char *policy = NULL;
     struct policy_file pf;
     policydb_t policydb;
     char ch;
-    char equiv = 0, diff = 0;
+    char equiv = 0, diff = 0, dups = 0;
 
     struct option long_options[] = {
         {"equiv", no_argument, NULL, 'e'},
         {"diff", no_argument, NULL, 'd'},
+        {"dups", no_argument, NULL, 'D'},
         {"policy", required_argument, NULL, 'P'},
         {NULL, 0, NULL, 0}
     };
 
-    while ((ch = getopt_long(argc, argv, "edP:", long_options, NULL)) != -1) {
+    while ((ch = getopt_long(argc, argv, "edDP:", long_options, NULL)) != -1) {
         switch (ch) {
         case 'e':
             equiv = 1;
@@ -357,6 +411,9 @@ int main(int argc, char **argv)
         case 'd':
             diff = 1;
             break;
+        case 'D':
+            dups = 1;
+            break;
         case 'P':
             policy = optarg;
             break;
@@ -365,13 +422,17 @@ int main(int argc, char **argv)
         }
     }
 
-    if (!policy || (!equiv && !diff))
+    if (!policy || (!equiv && !diff && !dups))
         usage(argv[0]);
 
     if (load_policy(policy, &policydb, &pf))
         exit(1);
 
-    analyze_types(&policydb, equiv, diff);
+    if (equiv || diff)
+        analyze_types(&policydb, equiv, diff);
+
+    if (dups)
+        find_dups(&policydb);
 
     policydb_destroy(&policydb);