Merge remote-tracking branch 'aosp/upstream-master' into mymerge

Followed the following steps:

  # In repo client
  cd external/selinux
  repo sync .
  repo start mymerge .
  git merge aosp/upstream-master --no-ff # resolve any conflicts
  repo upload .

Test: compiles/boots no problems.
Change-Id: I4e7ee3fddfe021d128ebd70a663374367914b825
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..22e6568
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,6 @@
+subdirs = [
+    "checkpolicy",
+    "libselinux",
+    "libsepol",
+    "secilc",
+]
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 1ac5a62..5658a40 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -1,2 +1,5 @@
 # This empty CleanSpec.mk file will prevent the build system
 # from descending into subdirs.
+#
+$(call add-clean-step, rm -rf $(OUT_DIR)/host/linux-x86/bin/audit2allow)
+$(call add-clean-step, rm -rf $(OUT_DIR)/host/linux-x86/bin/audit2why)
diff --git a/MODULE_LICENSE_GPL b/MODULE_LICENSE_GPL
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_GPL
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..0b4d310
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,2060 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+All files are licensed under the FreeBSD license, excepet for thid party
+components, which are subject to their respective licenses as specified in
+their source files.
+
+                          FreeBSD License
+
+Copyright 2011 Tresys Technology, LLC. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+   1. Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+
+   2. Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY TRESYS TECHNOLOGY, LLC ``AS IS'' AND ANY EXPRESS
+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL TRESYS TECHNOLOGY, LLC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those
+of the authors and should not be interpreted as representing official policies,
+either expressed or implied, of Tresys Technology, LLC.
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..f263654
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,3 @@
+dcashman@google.com
+jeffv@google.com
+nnk@google.com
diff --git a/README.android b/README.android
new file mode 100644
index 0000000..df8a60f
--- /dev/null
+++ b/README.android
@@ -0,0 +1,41 @@
+This fork of Android differs in the following ways:
+ - README.android
+ - All Android.mk and Android.bp files
+ - ALL MODULE_LICENSE_* files
+ - libselinux/include/selinux/android.h
+ - libselinux/src/android/android.c
+
+All other changes should be upstreamed to selinux as
+Android no longer carries changes outside of those files.
+
+The upstream project can be found at:
+https://github.com/SELinuxProject/selinux
+
+Thus, since all changes are in separate files, updates merged from
+upstream should occur with no merge conflicts.
+
+This fork differs from upstream libselinux in at least the following ways:
+
+* The Android fork omits compiling many of the src files and specifies
+  custom build configurations. The exact details, are encoded in the
+  Android.bp and Android.mk files.
+
+* The SELinux policy files are all located in / rather than under
+  /etc/selinux since /etc is not available in Android until /system
+  is mounted and use fixed paths, not dependent on /etc/selinux/config.
+
+* The kernel policy file (sepolicy in Android, policy.N in Linux) does
+  not include a version suffix since Android does not need to support
+  booting multiple kernels.
+
+* The policy loading logic does not support automatic downgrading of
+  the kernel policy file to a version known to the kernel, since this
+  requires libsepol on the device and is only needed to support mixing
+  and matching kernels and userspace easily.
+
+* restorecon functionality, including recursive restorecon, has been
+  been upstreamed as selinux_restorecon(), but there are residual
+  differences between it and selinux_android_restorecon().
+
+* Support for seapp_contexts, a new Android-specific SELinux
+  configuration file has been added within android.c.
diff --git a/checkpolicy/Android.bp b/checkpolicy/Android.bp
new file mode 100644
index 0000000..1ca0e07
--- /dev/null
+++ b/checkpolicy/Android.bp
@@ -0,0 +1,26 @@
+common_CFLAGS = [
+    "-Wall",
+    "-Werror",
+    "-Wshadow",
+]
+
+cc_binary_host {
+    name: "checkpolicy",
+    cflags: common_CFLAGS,
+    srcs: [
+        "policy_parse.y",
+        "policy_scan.l",
+        "queue.c",
+        "module_compiler.c",
+        "parse_util.c",
+        "policy_define.c",
+        "checkpolicy.c",
+    ],
+    static_libs: ["libsepol"],
+}
+
+cc_binary_host {
+    name: "dispol",
+    srcs: ["test/dispol.c"],
+    static_libs: ["libsepol"],
+}
diff --git a/libselinux/Android.bp b/libselinux/Android.bp
new file mode 100644
index 0000000..b2eba43
--- /dev/null
+++ b/libselinux/Android.bp
@@ -0,0 +1,211 @@
+common_CFLAGS = [
+    // Persistently stored patterns (pcre2) are architecture dependent.
+    // In particular paterns built on amd64 can not run on devices with armv7
+    // (32bit). Therefore, this feature stays off for now.
+    "-DNO_PERSISTENTLY_STORED_PATTERNS",
+    "-DDISABLE_SETRANS",
+    "-DDISABLE_BOOL",
+    "-D_GNU_SOURCE",
+    "-DNO_MEDIA_BACKEND",
+    "-DNO_X_BACKEND",
+    "-DNO_DB_BACKEND",
+]
+
+cc_defaults {
+    name: "libselinux_defaults",
+
+    cflags: common_CFLAGS,
+
+    srcs: [
+        "src/booleans.c",
+        "src/callbacks.c",
+        "src/freecon.c",
+        "src/label_backends_android.c",
+        "src/label.c",
+        "src/label_support.c",
+        "src/matchpathcon.c",
+        "src/setrans_client.c",
+        "src/sha1.c",
+    ],
+
+    target: {
+        host: {
+            cflags: [
+                "-DBUILD_HOST",
+            ],
+        },
+
+        android: {
+            srcs: [
+                "src/android/android.c",
+                "src/avc.c",
+                "src/avc_internal.c",
+                "src/avc_sidtab.c",
+                "src/canonicalize_context.c",
+                "src/checkAccess.c",
+                "src/check_context.c",
+                "src/compute_av.c",
+                "src/compute_create.c",
+                "src/compute_member.c",
+                "src/context.c",
+                "src/deny_unknown.c",
+                "src/disable.c",
+                "src/enabled.c",
+                "src/fgetfilecon.c",
+                "src/fsetfilecon.c",
+                "src/getenforce.c",
+                "src/getfilecon.c",
+                "src/get_initial_context.c",
+                "src/getpeercon.c",
+                "src/init.c",
+                "src/lgetfilecon.c",
+                "src/load_policy.c",
+                "src/lsetfilecon.c",
+                "src/mapping.c",
+                "src/policyvers.c",
+                "src/procattr.c",
+                "src/sestatus.c",
+                "src/setenforce.c",
+                "src/setfilecon.c",
+                "src/stringrep.c",
+            ],
+
+            shared_libs: [
+                "libcrypto",
+                "liblog",
+            ],
+
+            local_include_dirs: [ "src" ],
+
+            // 1003 corresponds to auditd, from system/core/logd/event.logtags
+            cflags: [
+                "-DAUDITD_LOG_TAG=1003",
+            ],
+
+            // mapping.c has redundant check of array p_in->perms.
+            clang_cflags: ["-Wno-pointer-bool-conversion"],
+        }
+    },
+
+    product_variables: {
+        treble: {
+            cflags: ["-DFULL_TREBLE"],
+        },
+    },
+
+    local_include_dirs: ["include"],
+    export_include_dirs: ["include"],
+}
+
+cc_library_shared {
+    name: "libselinux_vendor",
+    defaults: ["libselinux_defaults"],
+    vendor: true,
+    cflags: ["-DNO_FILE_BACKEND"],
+
+    target: {
+        android: {
+            version_script: "exported_vendor.map",
+        },
+    },
+
+    srcs: [
+        "src/android/android_vendor.c",
+    ],
+}
+
+cc_library {
+    name: "libselinux",
+    defaults: ["libselinux_defaults"],
+    host_supported: true,
+    cflags: ["-DUSE_PCRE2"],
+
+    srcs: [
+        "src/label_file.c",
+        "src/regex.c",
+    ],
+
+    target: {
+        linux_glibc: {
+            srcs: [
+                "src/android/android_host.c",
+                "src/avc.c",
+                "src/avc_internal.c",
+                "src/avc_sidtab.c",
+                "src/compute_av.c",
+                "src/compute_create.c",
+                "src/compute_member.c",
+                "src/context.c",
+                "src/enabled.c",
+                "src/getenforce.c",
+                "src/getfilecon.c",
+                "src/get_initial_context.c",
+                "src/init.c",
+                "src/load_policy.c",
+                "src/mapping.c",
+                "src/procattr.c",
+                "src/setexecfilecon.c",
+                "src/stringrep.c",
+            ],
+        },
+        linux_bionic: {
+            enabled: true,
+            srcs: [
+                "src/android/android_host.c",
+                "src/avc.c",
+                "src/avc_internal.c",
+                "src/avc_sidtab.c",
+                "src/compute_av.c",
+                "src/compute_create.c",
+                "src/compute_member.c",
+                "src/context.c",
+                "src/enabled.c",
+                "src/getenforce.c",
+                "src/getfilecon.c",
+                "src/get_initial_context.c",
+                "src/init.c",
+                "src/load_policy.c",
+                "src/mapping.c",
+                "src/procattr.c",
+                "src/setexecfilecon.c",
+                "src/stringrep.c",
+            ],
+        },
+
+        android: {
+            srcs: [
+                "src/android/android_platform.c",
+            ],
+
+            static: {
+                whole_static_libs: ["libpackagelistparser"],
+            },
+
+            shared: {
+                shared_libs: ["libpackagelistparser"],
+            },
+
+            version_script: "exported.map",
+        },
+    },
+
+    static: {
+        whole_static_libs: ["libpcre2"],
+    },
+    shared: {
+        shared_libs: ["libpcre2"],
+    },
+}
+
+cc_binary_host {
+    name: "sefcontext_compile",
+    defaults: ["libselinux_defaults"],
+    cflags: ["-DUSE_PCRE2"],
+    srcs: ["utils/sefcontext_compile.c"],
+
+    static_libs: [
+        "libselinux",
+        "libsepol",
+    ],
+    whole_static_libs: ["libpcre2"],
+}
diff --git a/libselinux/MODULE_LICENSE_PUBLIC_DOMAIN b/libselinux/MODULE_LICENSE_PUBLIC_DOMAIN
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libselinux/MODULE_LICENSE_PUBLIC_DOMAIN
diff --git a/libselinux/exported.map b/libselinux/exported.map
new file mode 100644
index 0000000..d2a0357
--- /dev/null
+++ b/libselinux/exported.map
@@ -0,0 +1,39 @@
+{
+  global:
+    fgetfilecon;
+    fsetfilecon;
+    freecon;
+    getcon;
+    getfilecon;
+    getpeercon;
+    getpidcon;
+    is_selinux_enabled;
+    lgetfilecon;
+    lsetfilecon;
+    selabel_close;
+    selabel_lookup;
+    selabel_open;
+    selinux_android_file_context_handle;
+    selinux_android_prop_context_handle;
+    selinux_android_restorecon;
+    selinux_android_restorecon_pkgdir;
+    selinux_android_setcontext;
+    selinux_android_set_sehandle;
+    selinux_android_service_context_handle;
+    selinux_android_hw_service_context_handle;
+    selinux_android_vendor_service_context_handle;
+    selinux_check_access;
+    security_getenforce;
+    security_setenforce;
+    security_load_policy;
+    security_policyvers;
+    selinux_log_callback;
+    selinux_set_callback;
+    selinux_status_open;
+    selinux_status_updated;
+    setcon;
+    setexeccon;
+    setfilecon;
+    setfscreatecon;
+  local: *;
+};
diff --git a/libselinux/exported_vendor.map b/libselinux/exported_vendor.map
new file mode 100644
index 0000000..05608e7
--- /dev/null
+++ b/libselinux/exported_vendor.map
@@ -0,0 +1,34 @@
+{
+  global:
+    fgetfilecon;
+    fsetfilecon;
+    freecon;
+    getcon;
+    getfilecon;
+    getpeercon;
+    getpidcon;
+    is_selinux_enabled;
+    lgetfilecon;
+    lsetfilecon;
+    selabel_close;
+    selabel_lookup;
+    selabel_open;
+    selinux_android_prop_context_handle;
+    selinux_android_restorecon;
+    selinux_android_service_context_handle;
+    selinux_android_vendor_service_context_handle;
+    selinux_check_access;
+    security_getenforce;
+    security_setenforce;
+    security_load_policy;
+    security_policyvers;
+    selinux_log_callback;
+    selinux_set_callback;
+    selinux_status_open;
+    selinux_status_updated;
+    setcon;
+    setexeccon;
+    setfilecon;
+    setfscreatecon;
+  local: *;
+};
diff --git a/libselinux/include/selinux/android.h b/libselinux/include/selinux/android.h
new file mode 100644
index 0000000..bc98392
--- /dev/null
+++ b/libselinux/include/selinux/android.h
@@ -0,0 +1,64 @@
+#ifndef _SELINUX_ANDROID_H_
+#define _SELINUX_ANDROID_H_
+
+#include <stdbool.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <selinux/label.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct selabel_handle* selinux_android_file_context_handle(void);
+
+extern struct selabel_handle* selinux_android_prop_context_handle(void);
+
+extern struct selabel_handle* selinux_android_service_context_handle(void);
+
+extern struct selabel_handle* selinux_android_hw_service_context_handle(void);
+
+extern struct selabel_handle* selinux_android_vendor_service_context_handle(void);
+
+extern void selinux_android_set_sehandle(const struct selabel_handle *hndl);
+
+extern int selinux_android_load_policy(void);
+
+extern int selinux_android_load_policy_from_fd(int fd, const char *description);
+
+extern int selinux_android_setcon(const char *con);
+
+extern int selinux_android_setcontext(uid_t uid,
+				      bool isSystemServer,
+				      const char *seinfo,
+				      const char *name);
+
+extern int selinux_android_setfilecon(const char *pkgdir,
+				       const char *pkgname,
+				       const char *seinfo,
+				       uid_t uid);
+
+extern int selinux_log_callback(int type, const char *fmt, ...)
+    __attribute__ ((format(printf, 2, 3)));
+
+#define SELINUX_ANDROID_RESTORECON_NOCHANGE 1
+#define SELINUX_ANDROID_RESTORECON_VERBOSE  2
+#define SELINUX_ANDROID_RESTORECON_RECURSE  4
+#define SELINUX_ANDROID_RESTORECON_FORCE    8
+#define SELINUX_ANDROID_RESTORECON_DATADATA 16
+#define SELINUX_ANDROID_RESTORECON_SKIPCE   32
+#define SELINUX_ANDROID_RESTORECON_CROSS_FILESYSTEMS   64
+extern int selinux_android_restorecon(const char *file, unsigned int flags);
+
+extern int selinux_android_restorecon_pkgdir(const char *pkgdir,
+                                             const char *seinfo,
+                                             uid_t uid,
+                                             unsigned int flags);
+
+extern int selinux_android_seapp_context_reload(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/libselinux/src/android/android.c b/libselinux/src/android/android.c
new file mode 100644
index 0000000..b79b463
--- /dev/null
+++ b/libselinux/src/android/android.c
@@ -0,0 +1,124 @@
+#include "android_common.h"
+
+#ifdef __ANDROID_VNDK__
+#ifndef LOG_EVENT_STRING
+#define LOG_EVENT_STRING(...)
+#endif  // LOG_EVENT_STRING
+#endif  // __ANDROID_VNDK__
+
+static const struct selinux_opt seopts_service_split[] = {
+    { SELABEL_OPT_PATH, "/system/etc/selinux/plat_service_contexts" },
+    { SELABEL_OPT_PATH, "/vendor/etc/selinux/nonplat_service_contexts" }
+};
+
+static const struct selinux_opt seopts_service_rootfs[] = {
+    { SELABEL_OPT_PATH, "/plat_service_contexts" },
+    { SELABEL_OPT_PATH, "/nonplat_service_contexts" }
+};
+
+static const struct selinux_opt seopts_hwservice_split[] = {
+    { SELABEL_OPT_PATH, "/system/etc/selinux/plat_hwservice_contexts" },
+    { SELABEL_OPT_PATH, "/vendor/etc/selinux/nonplat_hwservice_contexts" }
+};
+
+static const struct selinux_opt seopts_hwservice_rootfs[] = {
+    { SELABEL_OPT_PATH, "/plat_hwservice_contexts" },
+    { SELABEL_OPT_PATH, "/nonplat_hwservice_contexts" }
+};
+
+static const struct selinux_opt seopts_vndservice =
+    { SELABEL_OPT_PATH, "/vendor/etc/selinux/vndservice_contexts" };
+
+static const struct selinux_opt seopts_vndservice_rootfs =
+    { SELABEL_OPT_PATH, "/vndservice_contexts" };
+
+struct selabel_handle* selinux_android_service_open_context_handle(const struct selinux_opt* seopts_service,
+                                                                   unsigned nopts)
+{
+    struct selabel_handle* sehandle;
+
+    sehandle = selabel_open(SELABEL_CTX_ANDROID_SERVICE,
+            seopts_service, nopts);
+
+    if (!sehandle) {
+        selinux_log(SELINUX_ERROR, "%s: Error getting service context handle (%s)\n",
+                __FUNCTION__, strerror(errno));
+        return NULL;
+    }
+    selinux_log(SELINUX_INFO, "SELinux: Loaded service_contexts from:\n");
+    for (unsigned i = 0; i < nopts; i++) {
+        selinux_log(SELINUX_INFO, "    %s\n", seopts_service[i].value);
+    }
+    return sehandle;
+}
+
+struct selabel_handle* selinux_android_service_context_handle(void)
+{
+    const struct selinux_opt* seopts_service;
+
+    // Prefer files from /system & /vendor, fall back to files from /
+    if (access(seopts_service_split[0].value, R_OK) != -1) {
+        seopts_service = seopts_service_split;
+    } else {
+        seopts_service = seopts_service_rootfs;
+    }
+
+#ifdef FULL_TREBLE
+    // Treble compliant devices can only serve plat_service_contexts from servicemanager
+    return selinux_android_service_open_context_handle(seopts_service, 1);
+#else
+    return selinux_android_service_open_context_handle(seopts_service, 2);
+#endif
+}
+
+struct selabel_handle* selinux_android_hw_service_context_handle(void)
+{
+    const struct selinux_opt* seopts_service;
+    if (access(seopts_hwservice_split[0].value, R_OK) != -1) {
+        seopts_service = seopts_hwservice_split;
+    } else {
+        seopts_service = seopts_hwservice_rootfs;
+    }
+
+    return selinux_android_service_open_context_handle(seopts_service, 2);
+}
+
+struct selabel_handle* selinux_android_vendor_service_context_handle(void)
+{
+    const struct selinux_opt* seopts_service;
+    if (access(seopts_vndservice.value, R_OK) != -1) {
+        seopts_service = &seopts_vndservice;
+    } else {
+        seopts_service = &seopts_vndservice_rootfs;
+    }
+
+    return selinux_android_service_open_context_handle(seopts_service, 1);
+}
+
+int selinux_log_callback(int type, const char *fmt, ...)
+{
+    va_list ap;
+    int priority;
+    char *strp;
+
+    switch(type) {
+    case SELINUX_WARNING:
+        priority = ANDROID_LOG_WARN;
+        break;
+    case SELINUX_INFO:
+        priority = ANDROID_LOG_INFO;
+        break;
+    default:
+        priority = ANDROID_LOG_ERROR;
+        break;
+    }
+
+    va_start(ap, fmt);
+    if (vasprintf(&strp, fmt, ap) != -1) {
+        LOG_PRI(priority, "SELinux", "%s", strp);
+        LOG_EVENT_STRING(AUDITD_LOG_TAG, strp);
+        free(strp);
+    }
+    va_end(ap);
+    return 0;
+}
diff --git a/libselinux/src/android/android_common.h b/libselinux/src/android/android_common.h
new file mode 100644
index 0000000..637d4e8
--- /dev/null
+++ b/libselinux/src/android/android_common.h
@@ -0,0 +1,44 @@
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <ctype.h>
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/xattr.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <selinux/selinux.h>
+#include <selinux/context.h>
+#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/avc.h>
+#include <openssl/sha.h>
+#ifndef __ANDROID_VNDK__
+#include <private/android_filesystem_config.h>
+#endif
+#include <log/log.h>
+#include "policy.h"
+#include "callbacks.h"
+#include "selinux_internal.h"
+#include "label_internal.h"
+#include <fnmatch.h>
+#include <limits.h>
+#include <sys/vfs.h>
+#include <linux/magic.h>
+#include <libgen.h>
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#define FC_DIGEST_SIZE SHA_DIGEST_LENGTH
diff --git a/libselinux/src/android/android_host.c b/libselinux/src/android/android_host.c
new file mode 100644
index 0000000..8b55aa7
--- /dev/null
+++ b/libselinux/src/android/android_host.c
@@ -0,0 +1,8 @@
+#include <stdlib.h>
+
+// HACK: placeholder for a library the python bindings expect.
+// Delete after b/33170640 is fixed.
+const char *selinux_openssh_contexts_path(void)
+{
+  abort();
+}
diff --git a/libselinux/src/android/android_platform.c b/libselinux/src/android/android_platform.c
new file mode 100644
index 0000000..0325113
--- /dev/null
+++ b/libselinux/src/android/android_platform.c
@@ -0,0 +1,1667 @@
+#include "android_common.h"
+#include <packagelistparser/packagelistparser.h>
+
+static const char *const sepolicy_file = "/sepolicy";
+
+static const struct selinux_opt seopts_file_split[] = {
+    { SELABEL_OPT_PATH, "/system/etc/selinux/plat_file_contexts" },
+    { SELABEL_OPT_PATH, "/vendor/etc/selinux/nonplat_file_contexts" }
+};
+
+static const struct selinux_opt seopts_file_rootfs[] = {
+    { SELABEL_OPT_PATH, "/plat_file_contexts" },
+    { SELABEL_OPT_PATH, "/nonplat_file_contexts" }
+};
+
+static const struct selinux_opt seopts_prop_split[] = {
+    { SELABEL_OPT_PATH, "/system/etc/selinux/plat_property_contexts" },
+    { SELABEL_OPT_PATH, "/vendor/etc/selinux/nonplat_property_contexts"}
+};
+
+static const struct selinux_opt seopts_prop_rootfs[] = {
+    { SELABEL_OPT_PATH, "/plat_property_contexts" },
+    { SELABEL_OPT_PATH, "/nonplat_property_contexts"}
+};
+
+/*
+ * XXX Where should this configuration file be located?
+ * Needs to be accessible by zygote and installd when
+ * setting credentials for app processes and setting permissions
+ * on app data directories.
+ */
+static char const * const seapp_contexts_split[] = {
+	"/system/etc/selinux/plat_seapp_contexts",
+	"/vendor/etc/selinux/nonplat_seapp_contexts"
+};
+
+static char const * const seapp_contexts_rootfs[] = {
+	"/plat_seapp_contexts",
+	"/nonplat_seapp_contexts"
+};
+
+uint8_t fc_digest[FC_DIGEST_SIZE];
+
+static bool compute_file_contexts_hash(uint8_t c_digest[], const struct selinux_opt *opts, unsigned nopts)
+{
+    int fd = -1;
+    void *map = MAP_FAILED;
+    bool ret = false;
+    uint8_t *fc_data = NULL;
+    size_t total_size = 0;
+    struct stat sb;
+    size_t i;
+
+    for (i = 0; i < nopts; i++) {
+        fd = open(opts[i].value, O_CLOEXEC | O_RDONLY);
+        if (fd < 0) {
+            selinux_log(SELINUX_ERROR, "SELinux:  Could not open %s:  %s\n",
+                    opts[i].value, strerror(errno));
+            goto cleanup;
+        }
+
+        if (fstat(fd, &sb) < 0) {
+            selinux_log(SELINUX_ERROR, "SELinux:  Could not stat %s:  %s\n",
+                    opts[i].value, strerror(errno));
+            goto cleanup;
+        }
+
+        map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+        if (map == MAP_FAILED) {
+            selinux_log(SELINUX_ERROR, "SELinux:  Could not map %s:  %s\n",
+                    opts[i].value, strerror(errno));
+            goto cleanup;
+        }
+
+        fc_data = realloc(fc_data, total_size + sb.st_size);
+        if (!fc_data) {
+            selinux_log(SELINUX_ERROR, "SELinux: Count not re-alloc for %s:  %s\n",
+                     opts[i].value, strerror(errno));
+            goto cleanup;
+        }
+
+        memcpy(fc_data + total_size, map, sb.st_size);
+        total_size += sb.st_size;
+
+        /* reset everything for next file */
+        munmap(map, sb.st_size);
+        close(fd);
+        map = MAP_FAILED;
+        fd = -1;
+    }
+
+    SHA1(fc_data, total_size, c_digest);
+    ret = true;
+
+cleanup:
+    if (map != MAP_FAILED)
+        munmap(map, sb.st_size);
+    if (fd >= 0)
+        close(fd);
+    free(fc_data);
+
+    return ret;
+}
+
+static struct selabel_handle* selinux_android_file_context(const struct selinux_opt *opts,
+                                                    unsigned nopts)
+{
+    struct selabel_handle *sehandle;
+    struct selinux_opt fc_opts[nopts + 1];
+
+    memcpy(fc_opts, opts, nopts*sizeof(struct selinux_opt));
+    fc_opts[nopts].type = SELABEL_OPT_BASEONLY;
+    fc_opts[nopts].value = (char *)1;
+
+    sehandle = selabel_open(SELABEL_CTX_FILE, fc_opts, ARRAY_SIZE(fc_opts));
+    if (!sehandle) {
+        selinux_log(SELINUX_ERROR, "%s: Error getting file context handle (%s)\n",
+                __FUNCTION__, strerror(errno));
+        return NULL;
+    }
+    if (!compute_file_contexts_hash(fc_digest, opts, nopts)) {
+        selabel_close(sehandle);
+        return NULL;
+    }
+
+    selinux_log(SELINUX_INFO, "SELinux: Loaded file_contexts\n");
+
+    return sehandle;
+}
+
+static bool selinux_android_opts_file_exists(const struct selinux_opt *opt)
+{
+    return (access(opt[0].value, R_OK) != -1);
+}
+
+struct selabel_handle* selinux_android_file_context_handle(void)
+{
+    if (selinux_android_opts_file_exists(seopts_file_split)) {
+        return selinux_android_file_context(seopts_file_split,
+                                            ARRAY_SIZE(seopts_file_split));
+    } else {
+        return selinux_android_file_context(seopts_file_rootfs,
+                                            ARRAY_SIZE(seopts_file_rootfs));
+    }
+}
+
+struct selabel_handle* selinux_android_prop_context_handle(void)
+{
+    struct selabel_handle* sehandle;
+    const struct selinux_opt* seopts_prop;
+
+    // Prefer files from /system & /vendor, fall back to files from /
+    if (access(seopts_prop_split[0].value, R_OK) != -1) {
+        seopts_prop = seopts_prop_split;
+    } else {
+        seopts_prop = seopts_prop_rootfs;
+    }
+
+    sehandle = selabel_open(SELABEL_CTX_ANDROID_PROP,
+            seopts_prop, 2);
+    if (!sehandle) {
+        selinux_log(SELINUX_ERROR, "%s: Error getting property context handle (%s)\n",
+                __FUNCTION__, strerror(errno));
+        return NULL;
+    }
+    selinux_log(SELINUX_INFO, "SELinux: Loaded property_contexts from %s & %s.\n",
+            seopts_prop[0].value, seopts_prop[1].value);
+
+    return sehandle;
+}
+
+enum levelFrom {
+	LEVELFROM_NONE,
+	LEVELFROM_APP,
+	LEVELFROM_USER,
+	LEVELFROM_ALL
+};
+
+#if DEBUG
+static char const * const levelFromName[] = {
+	"none",
+	"app",
+	"user",
+	"all"
+};
+#endif
+
+struct prefix_str {
+	size_t len;
+	char *str;
+	char is_prefix;
+};
+
+static void free_prefix_str(struct prefix_str *p)
+{
+	if (!p)
+		return;
+	free(p->str);
+}
+
+struct seapp_context {
+	/* input selectors */
+	bool isSystemServer;
+	bool isEphemeralAppSet;
+	bool isEphemeralApp;
+	bool isV2AppSet;
+	bool isV2App;
+	bool isOwnerSet;
+	bool isOwner;
+	struct prefix_str user;
+	char *seinfo;
+	struct prefix_str name;
+	struct prefix_str path;
+	bool isPrivAppSet;
+	bool isPrivApp;
+	int32_t minTargetSdkVersion;
+	/* outputs */
+	char *domain;
+	char *type;
+	char *level;
+	enum levelFrom levelFrom;
+};
+
+static void free_seapp_context(struct seapp_context *s)
+{
+	if (!s)
+		return;
+
+	free_prefix_str(&s->user);
+	free(s->seinfo);
+	free_prefix_str(&s->name);
+	free_prefix_str(&s->path);
+	free(s->domain);
+	free(s->type);
+	free(s->level);
+}
+
+static bool seapp_contexts_dup = false;
+
+static int seapp_context_cmp(const void *A, const void *B)
+{
+	const struct seapp_context *const *sp1 = (const struct seapp_context *const *) A;
+	const struct seapp_context *const *sp2 = (const struct seapp_context *const *) B;
+	const struct seapp_context *s1 = *sp1, *s2 = *sp2;
+	bool dup;
+
+	/* Give precedence to isSystemServer=true. */
+	if (s1->isSystemServer != s2->isSystemServer)
+		return (s1->isSystemServer ? -1 : 1);
+
+	/* Give precedence to a specified isEphemeral= over an
+	 * unspecified isEphemeral=. */
+	if (s1->isEphemeralAppSet != s2->isEphemeralAppSet)
+		return (s1->isEphemeralAppSet ? -1 : 1);
+
+	/* Give precedence to a specified isV2= over an
+	 * unspecified isV2=. */
+	if (s1->isV2AppSet != s2->isV2AppSet)
+		return (s1->isV2AppSet ? -1 : 1);
+
+
+	/* Give precedence to a specified isOwner= over an unspecified isOwner=. */
+	if (s1->isOwnerSet != s2->isOwnerSet)
+		return (s1->isOwnerSet ? -1 : 1);
+
+	/* Give precedence to a specified user= over an unspecified user=. */
+	if (s1->user.str && !s2->user.str)
+		return -1;
+	if (!s1->user.str && s2->user.str)
+		return 1;
+
+	if (s1->user.str) {
+		/* Give precedence to a fixed user= string over a prefix. */
+		if (s1->user.is_prefix != s2->user.is_prefix)
+			return (s2->user.is_prefix ? -1 : 1);
+
+		/* Give precedence to a longer prefix over a shorter prefix. */
+		if (s1->user.is_prefix && s1->user.len != s2->user.len)
+			return (s1->user.len > s2->user.len) ? -1 : 1;
+	}
+
+	/* Give precedence to a specified seinfo= over an unspecified seinfo=. */
+	if (s1->seinfo && !s2->seinfo)
+		return -1;
+	if (!s1->seinfo && s2->seinfo)
+		return 1;
+
+	/* Give precedence to a specified name= over an unspecified name=. */
+	if (s1->name.str && !s2->name.str)
+		return -1;
+	if (!s1->name.str && s2->name.str)
+		return 1;
+
+	if (s1->name.str) {
+		/* Give precedence to a fixed name= string over a prefix. */
+		if (s1->name.is_prefix != s2->name.is_prefix)
+			return (s2->name.is_prefix ? -1 : 1);
+
+		/* Give precedence to a longer prefix over a shorter prefix. */
+		if (s1->name.is_prefix && s1->name.len != s2->name.len)
+			return (s1->name.len > s2->name.len) ? -1 : 1;
+	}
+
+	/* Give precedence to a specified path= over an unspecified path=. */
+	if (s1->path.str && !s2->path.str)
+		return -1;
+	if (!s1->path.str && s2->path.str)
+		return 1;
+
+	if (s1->path.str) {
+		/* Give precedence to a fixed path= string over a prefix. */
+		if (s1->path.is_prefix != s2->path.is_prefix)
+			return (s2->path.is_prefix ? -1 : 1);
+
+		/* Give precedence to a longer prefix over a shorter prefix. */
+		if (s1->path.is_prefix && s1->path.len != s2->path.len)
+			return (s1->path.len > s2->path.len) ? -1 : 1;
+	}
+
+	/* Give precedence to a specified isPrivApp= over an unspecified isPrivApp=. */
+	if (s1->isPrivAppSet != s2->isPrivAppSet)
+		return (s1->isPrivAppSet ? -1 : 1);
+
+	/* Give precedence to a higher minTargetSdkVersion= over a lower minTargetSdkVersion=.
+	 * If unspecified, minTargetSdkVersion has a default value of 0.
+	 */
+	if (s1->minTargetSdkVersion > s2->minTargetSdkVersion)
+		return -1;
+	else if (s1->minTargetSdkVersion < s2->minTargetSdkVersion)
+		return 1;
+
+	/*
+	 * Check for a duplicated entry on the input selectors.
+	 * We already compared isSystemServer, isOwnerSet, and isOwner above.
+	 * We also have already checked that both entries specify the same
+	 * string fields, so if s1 has a non-NULL string, then so does s2.
+	 */
+	dup = (!s1->user.str || !strcmp(s1->user.str, s2->user.str)) &&
+		(!s1->seinfo || !strcmp(s1->seinfo, s2->seinfo)) &&
+		(!s1->name.str || !strcmp(s1->name.str, s2->name.str)) &&
+		(!s1->path.str || !strcmp(s1->path.str, s2->path.str)) &&
+		(s1->isPrivAppSet && s1->isPrivApp == s2->isPrivApp) &&
+		(s1->isOwnerSet && s1->isOwner == s2->isOwner) &&
+		(s1->isSystemServer && s1->isSystemServer == s2->isSystemServer) &&
+		(s1->isV2AppSet && s1->isV2App == s2->isV2App) &&
+		(s1->isEphemeralAppSet && s1->isEphemeralApp == s2->isEphemeralApp);
+
+	if (dup) {
+		seapp_contexts_dup = true;
+		selinux_log(SELINUX_ERROR, "seapp_contexts:  Duplicated entry\n");
+		if (s1->user.str)
+			selinux_log(SELINUX_ERROR, " user=%s\n", s1->user.str);
+		if (s1->seinfo)
+			selinux_log(SELINUX_ERROR, " seinfo=%s\n", s1->seinfo);
+		if (s1->name.str)
+			selinux_log(SELINUX_ERROR, " name=%s\n", s1->name.str);
+		if (s1->path.str)
+			selinux_log(SELINUX_ERROR, " path=%s\n", s1->path.str);
+	}
+
+	/* Anything else has equal precedence. */
+	return 0;
+}
+
+static struct seapp_context **seapp_contexts = NULL;
+static int nspec = 0;
+
+static void free_seapp_contexts(void)
+{
+	int n;
+
+	if (!seapp_contexts)
+		return;
+
+	for (n = 0; n < nspec; n++)
+		free_seapp_context(seapp_contexts[n]);
+
+	free(seapp_contexts);
+	seapp_contexts = NULL;
+	nspec = 0;
+}
+
+static int32_t get_minTargetSdkVersion(const char *value)
+{
+	char *endptr;
+	long minTargetSdkVersion;
+	minTargetSdkVersion = strtol(value, &endptr, 10);
+	if (('\0' != *endptr) || (minTargetSdkVersion < 0) || (minTargetSdkVersion > INT32_MAX)) {
+		return -1; /* error parsing minTargetSdkVersion */
+	} else {
+		return (int32_t) minTargetSdkVersion;
+	}
+}
+
+int selinux_android_seapp_context_reload(void)
+{
+	FILE *fp = NULL;
+	char line_buf[BUFSIZ];
+	char *token;
+	unsigned lineno;
+	struct seapp_context *cur;
+	char *p, *name = NULL, *value = NULL, *saveptr;
+	size_t i, len, files_len;
+	int n, ret;
+	const char *const *seapp_contexts_files;
+
+	// Prefer files from /system & /vendor, fall back to files from /
+	if (access(seapp_contexts_split[0], R_OK) != -1) {
+		seapp_contexts_files = seapp_contexts_split;
+		files_len = sizeof(seapp_contexts_split)/sizeof(seapp_contexts_split[0]);
+	} else {
+		seapp_contexts_files = seapp_contexts_rootfs;
+		files_len = sizeof(seapp_contexts_rootfs)/sizeof(seapp_contexts_rootfs[0]);
+	}
+
+	free_seapp_contexts();
+
+	nspec = 0;
+	for (i = 0; i < files_len; i++) {
+		fp = fopen(seapp_contexts_files[i], "re");
+		if (!fp) {
+			selinux_log(SELINUX_ERROR, "%s:  could not open seapp_contexts file: %s",
+				    __FUNCTION__, seapp_contexts_files[i]);
+			return -1;
+		}
+		while (fgets(line_buf, sizeof line_buf - 1, fp)) {
+			p = line_buf;
+			while (isspace(*p))
+				p++;
+			if (*p == '#' || *p == 0)
+				continue;
+			nspec++;
+		}
+		fclose(fp);
+	}
+
+	seapp_contexts = (struct seapp_context **) calloc(nspec, sizeof(struct seapp_context *));
+	if (!seapp_contexts)
+		goto oom;
+
+	nspec = 0;
+	for (i = 0; i < files_len; i++) {
+		lineno = 1;
+		fp = fopen(seapp_contexts_files[i], "re");
+		if (!fp) {
+			selinux_log(SELINUX_ERROR, "%s:  could not open seapp_contexts file: %s",
+				    __FUNCTION__, seapp_contexts_files[i]);
+			free_seapp_contexts();
+			return -1;
+		}
+		while (fgets(line_buf, sizeof line_buf - 1, fp)) {
+			len = strlen(line_buf);
+			if (line_buf[len - 1] == '\n')
+				line_buf[len - 1] = 0;
+			p = line_buf;
+			while (isspace(*p))
+				p++;
+			if (*p == '#' || *p == 0)
+				continue;
+
+			cur = (struct seapp_context *) calloc(1, sizeof(struct seapp_context));
+			if (!cur)
+				goto oom;
+
+			token = strtok_r(p, " \t", &saveptr);
+			if (!token) {
+				free_seapp_context(cur);
+				goto err;
+			}
+
+			while (1) {
+				name = token;
+				value = strchr(name, '=');
+				if (!value) {
+					free_seapp_context(cur);
+					goto err;
+				}
+				*value++ = 0;
+
+				if (!strcasecmp(name, "isSystemServer")) {
+					if (!strcasecmp(value, "true"))
+						cur->isSystemServer = true;
+					else if (!strcasecmp(value, "false"))
+						cur->isSystemServer = false;
+					else {
+						free_seapp_context(cur);
+						goto err;
+					}
+				} else if (!strcasecmp(name, "isEphemeralApp")) {
+					cur->isEphemeralAppSet = true;
+					if (!strcasecmp(value, "true"))
+						cur->isEphemeralApp = true;
+					else if (!strcasecmp(value, "false"))
+						cur->isEphemeralApp = false;
+					else {
+						free_seapp_context(cur);
+						goto err;
+					}
+				} else if (!strcasecmp(name, "isV2App")) {
+					cur->isV2AppSet = true;
+					if (!strcasecmp(value, "true"))
+						cur->isV2App = true;
+					else if (!strcasecmp(value, "false"))
+						cur->isV2App = false;
+					else {
+						free_seapp_context(cur);
+						goto err;
+					}
+				} else if (!strcasecmp(name, "isOwner")) {
+					cur->isOwnerSet = true;
+					if (!strcasecmp(value, "true"))
+						cur->isOwner = true;
+					else if (!strcasecmp(value, "false"))
+						cur->isOwner = false;
+					else {
+						free_seapp_context(cur);
+						goto err;
+					}
+				} else if (!strcasecmp(name, "user")) {
+					if (cur->user.str) {
+						free_seapp_context(cur);
+						goto err;
+					}
+					cur->user.str = strdup(value);
+					if (!cur->user.str) {
+						free_seapp_context(cur);
+						goto oom;
+					}
+					cur->user.len = strlen(cur->user.str);
+					if (cur->user.str[cur->user.len-1] == '*')
+						cur->user.is_prefix = 1;
+				} else if (!strcasecmp(name, "seinfo")) {
+					if (cur->seinfo) {
+						free_seapp_context(cur);
+						goto err;
+					}
+					cur->seinfo = strdup(value);
+					if (!cur->seinfo) {
+						free_seapp_context(cur);
+						goto oom;
+					}
+					if (strstr(value, ":")) {
+						free_seapp_context(cur);
+						goto err;
+					}
+				} else if (!strcasecmp(name, "name")) {
+					if (cur->name.str) {
+						free_seapp_context(cur);
+						goto err;
+					}
+					cur->name.str = strdup(value);
+					if (!cur->name.str) {
+						free_seapp_context(cur);
+						goto oom;
+					}
+					cur->name.len = strlen(cur->name.str);
+					if (cur->name.str[cur->name.len-1] == '*')
+						cur->name.is_prefix = 1;
+				} else if (!strcasecmp(name, "domain")) {
+					if (cur->domain) {
+						free_seapp_context(cur);
+						goto err;
+					}
+					cur->domain = strdup(value);
+					if (!cur->domain) {
+						free_seapp_context(cur);
+						goto oom;
+					}
+				} else if (!strcasecmp(name, "type")) {
+					if (cur->type) {
+						free_seapp_context(cur);
+						goto err;
+					}
+					cur->type = strdup(value);
+					if (!cur->type) {
+						free_seapp_context(cur);
+						goto oom;
+					}
+				} else if (!strcasecmp(name, "levelFromUid")) {
+					if (cur->levelFrom) {
+						free_seapp_context(cur);
+						goto err;
+					}
+					if (!strcasecmp(value, "true"))
+						cur->levelFrom = LEVELFROM_APP;
+					else if (!strcasecmp(value, "false"))
+						cur->levelFrom = LEVELFROM_NONE;
+					else {
+						free_seapp_context(cur);
+						goto err;
+					}
+				} else if (!strcasecmp(name, "levelFrom")) {
+					if (cur->levelFrom) {
+						free_seapp_context(cur);
+						goto err;
+					}
+					if (!strcasecmp(value, "none"))
+						cur->levelFrom = LEVELFROM_NONE;
+					else if (!strcasecmp(value, "app"))
+						cur->levelFrom = LEVELFROM_APP;
+					else if (!strcasecmp(value, "user"))
+						cur->levelFrom = LEVELFROM_USER;
+					else if (!strcasecmp(value, "all"))
+						cur->levelFrom = LEVELFROM_ALL;
+					else {
+						free_seapp_context(cur);
+						goto err;
+					}
+				} else if (!strcasecmp(name, "level")) {
+					if (cur->level) {
+						free_seapp_context(cur);
+						goto err;
+					}
+					cur->level = strdup(value);
+					if (!cur->level) {
+						free_seapp_context(cur);
+						goto oom;
+					}
+				} else if (!strcasecmp(name, "path")) {
+					if (cur->path.str) {
+						free_seapp_context(cur);
+						goto err;
+					}
+					cur->path.str = strdup(value);
+					if (!cur->path.str) {
+						free_seapp_context(cur);
+					goto oom;
+					}
+					cur->path.len = strlen(cur->path.str);
+					if (cur->path.str[cur->path.len-1] == '*')
+						cur->path.is_prefix = 1;
+				} else if (!strcasecmp(name, "isPrivApp")) {
+					cur->isPrivAppSet = true;
+					if (!strcasecmp(value, "true"))
+						cur->isPrivApp = true;
+					else if (!strcasecmp(value, "false"))
+						cur->isPrivApp = false;
+					else {
+						free_seapp_context(cur);
+						goto err;
+					}
+				} else if (!strcasecmp(name, "minTargetSdkVersion")) {
+					cur->minTargetSdkVersion = get_minTargetSdkVersion(value);
+					if (cur->minTargetSdkVersion < 0) {
+						free_seapp_context(cur);
+						goto err;
+					}
+				} else {
+					free_seapp_context(cur);
+					goto err;
+				}
+
+				token = strtok_r(NULL, " \t", &saveptr);
+				if (!token)
+					break;
+			}
+
+			if (cur->name.str &&
+			    (!cur->seinfo || !strcmp(cur->seinfo, "default"))) {
+				selinux_log(SELINUX_ERROR, "%s:  No specific seinfo value specified with name=\"%s\", on line %u:  insecure configuration!\n",
+					    seapp_contexts_files[i], cur->name.str, lineno);
+				free_seapp_context(cur);
+				goto err;
+			}
+
+			seapp_contexts[nspec] = cur;
+			nspec++;
+			lineno++;
+		}
+		fclose(fp);
+		fp = NULL;
+	}
+
+	qsort(seapp_contexts, nspec, sizeof(struct seapp_context *),
+	      seapp_context_cmp);
+
+	if (seapp_contexts_dup)
+		goto err_no_log;
+
+#if DEBUG
+	{
+		int i;
+		for (i = 0; i < nspec; i++) {
+			cur = seapp_contexts[i];
+			selinux_log(SELINUX_INFO, "%s:  isSystemServer=%s  isEphemeralApp=%s isV2App=%s isOwner=%s user=%s seinfo=%s "
+					"name=%s path=%s isPrivApp=%s minTargetSdkVersion=%d -> domain=%s type=%s level=%s levelFrom=%s",
+				__FUNCTION__,
+				cur->isSystemServer ? "true" : "false",
+				cur->isEphemeralAppSet ? (cur->isEphemeralApp ? "true" : "false") : "null",
+				cur->isV2AppSet ? (cur->isV2App ? "true" : "false") : "null",
+				cur->isOwnerSet ? (cur->isOwner ? "true" : "false") : "null",
+				cur->user.str,
+				cur->seinfo, cur->name.str, cur->path.str,
+				cur->isPrivAppSet ? (cur->isPrivApp ? "true" : "false") : "null",
+				cur->minTargetSdkVersion,
+				cur->domain, cur->type, cur->level,
+				levelFromName[cur->levelFrom]);
+		}
+	}
+#endif
+
+	ret = 0;
+
+out:
+	if (fp) {
+		fclose(fp);
+	}
+	return ret;
+
+err:
+	selinux_log(SELINUX_ERROR, "%s:  Invalid entry on line %u\n",
+		    seapp_contexts_files[i], lineno);
+err_no_log:
+	free_seapp_contexts();
+	ret = -1;
+	goto out;
+oom:
+	selinux_log(SELINUX_ERROR,
+		    "%s:  Out of memory\n", __FUNCTION__);
+	free_seapp_contexts();
+	ret = -1;
+	goto out;
+}
+
+
+static void seapp_context_init(void)
+{
+        selinux_android_seapp_context_reload();
+}
+
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+
+/*
+ * Max id that can be mapped to category set uniquely
+ * using the current scheme.
+ */
+#define CAT_MAPPING_MAX_ID (0x1<<16)
+
+enum seapp_kind {
+	SEAPP_TYPE,
+	SEAPP_DOMAIN
+};
+
+#define PRIVILEGED_APP_STR ":privapp"
+#define EPHEMERAL_APP_STR ":ephemeralapp"
+#define V2_APP_STR ":v2"
+#define TARGETSDKVERSION_STR ":targetSdkVersion="
+static int32_t get_app_targetSdkVersion(const char *seinfo)
+{
+	char *substr = strstr(seinfo, TARGETSDKVERSION_STR);
+	long targetSdkVersion;
+	char *endptr;
+	if (substr != NULL) {
+		substr = substr + strlen(TARGETSDKVERSION_STR);
+		if (substr != NULL) {
+			targetSdkVersion = strtol(substr, &endptr, 10);
+			if (('\0' != *endptr && ':' != *endptr)
+					|| (targetSdkVersion < 0) || (targetSdkVersion > INT32_MAX)) {
+				return -1; /* malformed targetSdkVersion value in seinfo */
+			} else {
+				return (int32_t) targetSdkVersion;
+			}
+		}
+	}
+	return 0; /* default to 0 when targetSdkVersion= is not present in seinfo */
+}
+
+static int seinfo_parse(char *dest, const char *src, size_t size)
+{
+	size_t len;
+	char *p;
+
+	if ((p = strchr(src, ':')) != NULL)
+		len = p - src;
+	else
+		len = strlen(src);
+
+	if (len > size - 1)
+		return -1;
+
+	strncpy(dest, src, len);
+	dest[len] = '\0';
+
+	return 0;
+}
+
+static int seapp_context_lookup(enum seapp_kind kind,
+				uid_t uid,
+				bool isSystemServer,
+				const char *seinfo,
+				const char *pkgname,
+				const char *path,
+				context_t ctx)
+{
+	struct passwd *pwd;
+	bool isOwner;
+	const char *username = NULL;
+	struct seapp_context *cur = NULL;
+	int i;
+	size_t n;
+	uid_t userid;
+	uid_t appid;
+	bool isPrivApp = false;
+	bool isEphemeralApp = false;
+	int32_t targetSdkVersion = 0;
+	bool isV2App = false;
+	char parsedseinfo[BUFSIZ];
+
+	__selinux_once(once, seapp_context_init);
+
+	if (seinfo) {
+		if (seinfo_parse(parsedseinfo, seinfo, BUFSIZ))
+			goto err;
+		isPrivApp = strstr(seinfo, PRIVILEGED_APP_STR) ? true : false;
+		isEphemeralApp = strstr(seinfo, EPHEMERAL_APP_STR) ? true : false;
+		isV2App = strstr(seinfo, V2_APP_STR) ? true : false;
+		targetSdkVersion = get_app_targetSdkVersion(seinfo);
+		if (targetSdkVersion < 0) {
+			selinux_log(SELINUX_ERROR,
+					"%s:  Invalid targetSdkVersion passed for app with uid %d, seinfo %s, name %s\n",
+					__FUNCTION__, uid, seinfo, pkgname);
+			goto err;
+		}
+		seinfo = parsedseinfo;
+	}
+
+	userid = uid / AID_USER;
+	isOwner = (userid == 0);
+	appid = uid % AID_USER;
+	if (appid < AID_APP) {
+            /*
+             * This code is Android specific, bionic guarantees that
+             * calls to non-reentrant getpwuid() are thread safe.
+             */
+#ifndef __BIONIC__
+#warning "This code assumes that getpwuid is thread safe, only true with Bionic!"
+#endif
+		pwd = getpwuid(appid);
+		if (!pwd)
+			goto err;
+
+		username = pwd->pw_name;
+
+	} else if (appid < AID_ISOLATED_START) {
+		username = "_app";
+		appid -= AID_APP;
+	} else {
+		username = "_isolated";
+		appid -= AID_ISOLATED_START;
+	}
+
+	if (appid >= CAT_MAPPING_MAX_ID || userid >= CAT_MAPPING_MAX_ID)
+		goto err;
+
+	for (i = 0; i < nspec; i++) {
+		cur = seapp_contexts[i];
+
+		if (cur->isSystemServer != isSystemServer)
+			continue;
+
+		if (cur->isEphemeralAppSet && cur->isEphemeralApp != isEphemeralApp)
+			continue;
+
+		if (cur->isV2AppSet && cur->isV2App != isV2App)
+			continue;
+
+		if (cur->isOwnerSet && cur->isOwner != isOwner)
+			continue;
+
+		if (cur->user.str) {
+			if (cur->user.is_prefix) {
+				if (strncasecmp(username, cur->user.str, cur->user.len-1))
+					continue;
+			} else {
+				if (strcasecmp(username, cur->user.str))
+					continue;
+			}
+		}
+
+		if (cur->seinfo) {
+			if (!seinfo || strcasecmp(seinfo, cur->seinfo))
+				continue;
+		}
+
+		if (cur->name.str) {
+			if(!pkgname)
+				continue;
+
+			if (cur->name.is_prefix) {
+				if (strncasecmp(pkgname, cur->name.str, cur->name.len-1))
+					continue;
+			} else {
+				if (strcasecmp(pkgname, cur->name.str))
+					continue;
+			}
+		}
+
+		if (cur->isPrivAppSet && cur->isPrivApp != isPrivApp)
+			continue;
+
+		if (cur->minTargetSdkVersion > targetSdkVersion)
+			continue;
+
+		if (cur->path.str) {
+			if (!path)
+				continue;
+
+			if (cur->path.is_prefix) {
+				if (strncmp(path, cur->path.str, cur->path.len-1))
+					continue;
+			} else {
+				if (strcmp(path, cur->path.str))
+					continue;
+			}
+		}
+
+		if (kind == SEAPP_TYPE && !cur->type)
+			continue;
+		else if (kind == SEAPP_DOMAIN && !cur->domain)
+			continue;
+
+		if (kind == SEAPP_TYPE) {
+			if (context_type_set(ctx, cur->type))
+				goto oom;
+		} else if (kind == SEAPP_DOMAIN) {
+			if (context_type_set(ctx, cur->domain))
+				goto oom;
+		}
+
+		if (cur->levelFrom != LEVELFROM_NONE) {
+			char level[255];
+			switch (cur->levelFrom) {
+			case LEVELFROM_APP:
+				snprintf(level, sizeof level, "s0:c%u,c%u",
+					 appid & 0xff,
+					 256 + (appid>>8 & 0xff));
+				break;
+			case LEVELFROM_USER:
+				snprintf(level, sizeof level, "s0:c%u,c%u",
+					 512 + (userid & 0xff),
+					 768 + (userid>>8 & 0xff));
+				break;
+			case LEVELFROM_ALL:
+				snprintf(level, sizeof level, "s0:c%u,c%u,c%u,c%u",
+					 appid & 0xff,
+					 256 + (appid>>8 & 0xff),
+					 512 + (userid & 0xff),
+					 768 + (userid>>8 & 0xff));
+				break;
+			default:
+				goto err;
+			}
+			if (context_range_set(ctx, level))
+				goto oom;
+		} else if (cur->level) {
+			if (context_range_set(ctx, cur->level))
+				goto oom;
+		}
+
+		break;
+	}
+
+	if (kind == SEAPP_DOMAIN && i == nspec) {
+		/*
+		 * No match.
+		 * Fail to prevent staying in the zygote's context.
+		 */
+		selinux_log(SELINUX_ERROR,
+			    "%s:  No match for app with uid %d, seinfo %s, name %s\n",
+			    __FUNCTION__, uid, seinfo, pkgname);
+
+		if (security_getenforce() == 1)
+			goto err;
+	}
+
+	return 0;
+err:
+	return -1;
+oom:
+	return -2;
+}
+
+int selinux_android_setfilecon(const char *pkgdir,
+				const char *pkgname,
+				const char *seinfo,
+				uid_t uid)
+{
+	char *orig_ctx_str = NULL;
+	char *ctx_str = NULL;
+	context_t ctx = NULL;
+	int rc = -1;
+
+	if (is_selinux_enabled() <= 0)
+		return 0;
+
+	rc = getfilecon(pkgdir, &ctx_str);
+	if (rc < 0)
+		goto err;
+
+	ctx = context_new(ctx_str);
+	orig_ctx_str = ctx_str;
+	if (!ctx)
+		goto oom;
+
+	rc = seapp_context_lookup(SEAPP_TYPE, uid, 0, seinfo, pkgname, NULL, ctx);
+	if (rc == -1)
+		goto err;
+	else if (rc == -2)
+		goto oom;
+
+	ctx_str = context_str(ctx);
+	if (!ctx_str)
+		goto oom;
+
+	rc = security_check_context(ctx_str);
+	if (rc < 0)
+		goto err;
+
+	if (strcmp(ctx_str, orig_ctx_str)) {
+		rc = setfilecon(pkgdir, ctx_str);
+		if (rc < 0)
+			goto err;
+	}
+
+	rc = 0;
+out:
+	freecon(orig_ctx_str);
+	context_free(ctx);
+	return rc;
+err:
+	selinux_log(SELINUX_ERROR, "%s:  Error setting context for pkgdir %s, uid %d: %s\n",
+		    __FUNCTION__, pkgdir, uid, strerror(errno));
+	rc = -1;
+	goto out;
+oom:
+	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __FUNCTION__);
+	rc = -1;
+	goto out;
+}
+
+int selinux_android_setcon(const char *con)
+{
+	int ret = setcon(con);
+	if (ret)
+		return ret;
+	/*
+	  System properties must be reinitialized after setcon() otherwise the
+	  previous property files will be leaked since mmap()'ed regions are not
+	  closed as a result of setcon().
+	*/
+	return __system_properties_init();
+}
+
+int selinux_android_setcontext(uid_t uid,
+			       bool isSystemServer,
+			       const char *seinfo,
+			       const char *pkgname)
+{
+	char *orig_ctx_str = NULL, *ctx_str;
+	context_t ctx = NULL;
+	int rc = -1;
+
+	if (is_selinux_enabled() <= 0)
+		return 0;
+
+	rc = getcon(&ctx_str);
+	if (rc)
+		goto err;
+
+	ctx = context_new(ctx_str);
+	orig_ctx_str = ctx_str;
+	if (!ctx)
+		goto oom;
+
+	rc = seapp_context_lookup(SEAPP_DOMAIN, uid, isSystemServer, seinfo, pkgname, NULL, ctx);
+	if (rc == -1)
+		goto err;
+	else if (rc == -2)
+		goto oom;
+
+	ctx_str = context_str(ctx);
+	if (!ctx_str)
+		goto oom;
+
+	rc = security_check_context(ctx_str);
+	if (rc < 0)
+		goto err;
+
+	if (strcmp(ctx_str, orig_ctx_str)) {
+		rc = selinux_android_setcon(ctx_str);
+		if (rc < 0)
+			goto err;
+	}
+
+	rc = 0;
+out:
+	freecon(orig_ctx_str);
+	context_free(ctx);
+	avc_netlink_close();
+	return rc;
+err:
+	if (isSystemServer)
+		selinux_log(SELINUX_ERROR,
+				"%s:  Error setting context for system server: %s\n",
+				__FUNCTION__, strerror(errno));
+	else
+		selinux_log(SELINUX_ERROR,
+				"%s:  Error setting context for app with uid %d, seinfo %s: %s\n",
+				__FUNCTION__, uid, seinfo, strerror(errno));
+
+	rc = -1;
+	goto out;
+oom:
+	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __FUNCTION__);
+	rc = -1;
+	goto out;
+}
+
+static struct selabel_handle *fc_sehandle = NULL;
+
+static void file_context_init(void)
+{
+    if (!fc_sehandle)
+        fc_sehandle = selinux_android_file_context_handle();
+}
+
+static pthread_once_t fc_once = PTHREAD_ONCE_INIT;
+
+#define PKGTAB_SIZE 256
+static struct pkg_info *pkgTab[PKGTAB_SIZE];
+
+static unsigned int pkghash(const char *pkgname)
+{
+    unsigned int h = 7;
+    for (; *pkgname; pkgname++) {
+        h = h * 31 + *pkgname;
+    }
+    return h & (PKGTAB_SIZE - 1);
+}
+
+static bool pkg_parse_callback(pkg_info *info, void *userdata) {
+
+    (void) userdata;
+
+    unsigned int hash = pkghash(info->name);
+    if (pkgTab[hash])
+        info->private_data = pkgTab[hash];
+    pkgTab[hash] = info;
+    return true;
+}
+
+static void package_info_init(void)
+{
+
+    bool rc = packagelist_parse(pkg_parse_callback, NULL);
+    if (!rc) {
+        selinux_log(SELINUX_ERROR, "SELinux: Could NOT parse package list\n");
+        return;
+    }
+
+#if DEBUG
+    {
+        unsigned int hash, buckets, entries, chainlen, longestchain;
+        struct pkg_info *info = NULL;
+
+        buckets = entries = longestchain = 0;
+        for (hash = 0; hash < PKGTAB_SIZE; hash++) {
+            if (pkgTab[hash]) {
+                buckets++;
+                chainlen = 0;
+                for (info = pkgTab[hash]; info; info = (pkg_info *)info->private_data) {
+                    chainlen++;
+                    selinux_log(SELINUX_INFO, "%s:  name=%s uid=%u debuggable=%s dataDir=%s seinfo=%s\n",
+                                __FUNCTION__,
+                                info->name, info->uid, info->debuggable ? "true" : "false", info->data_dir, info->seinfo);
+                }
+                entries += chainlen;
+                if (longestchain < chainlen)
+                    longestchain = chainlen;
+            }
+        }
+        selinux_log(SELINUX_INFO, "SELinux:  %d pkg entries and %d/%d buckets used, longest chain %d\n", entries, buckets, PKGTAB_SIZE, longestchain);
+    }
+#endif
+
+}
+
+static pthread_once_t pkg_once = PTHREAD_ONCE_INIT;
+
+struct pkg_info *package_info_lookup(const char *name)
+{
+    struct pkg_info *info;
+    unsigned int hash;
+
+    __selinux_once(pkg_once, package_info_init);
+
+    hash = pkghash(name);
+    for (info = pkgTab[hash]; info; info = (pkg_info *)info->private_data) {
+        if (!strcmp(name, info->name))
+            return info;
+    }
+    return NULL;
+}
+
+/* The contents of these paths are encrypted on FBE devices until user
+ * credentials are presented (filenames inside are mangled), so we need
+ * to delay restorecon of those until vold explicitly requests it. */
+// NOTE: these paths need to be kept in sync with vold
+#define DATA_SYSTEM_CE_PREFIX "/data/system_ce/"
+#define DATA_MISC_CE_PREFIX "/data/misc_ce/"
+
+/* The path prefixes of package data directories. */
+#define DATA_DATA_PATH "/data/data"
+#define DATA_USER_PATH "/data/user"
+#define DATA_USER_DE_PATH "/data/user_de"
+#define EXPAND_USER_PATH "/mnt/expand/\?\?\?\?\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?\?\?\?\?\?\?\?\?/user"
+#define EXPAND_USER_DE_PATH "/mnt/expand/\?\?\?\?\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?\?\?\?\?\?\?\?\?/user_de"
+#define DATA_DATA_PREFIX DATA_DATA_PATH "/"
+#define DATA_USER_PREFIX DATA_USER_PATH "/"
+#define DATA_USER_DE_PREFIX DATA_USER_DE_PATH "/"
+
+static int pkgdir_selabel_lookup(const char *pathname,
+                                 const char *seinfo,
+                                 uid_t uid,
+                                 char **secontextp)
+{
+    char *pkgname = NULL, *end = NULL;
+    struct pkg_info *info = NULL;
+    char *secontext = *secontextp;
+    context_t ctx = NULL;
+    int rc = 0;
+
+    /* Skip directory prefix before package name. */
+    if (!strncmp(pathname, DATA_DATA_PREFIX, sizeof(DATA_DATA_PREFIX)-1)) {
+        pathname += sizeof(DATA_DATA_PREFIX) - 1;
+    } else if (!strncmp(pathname, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1)) {
+        pathname += sizeof(DATA_USER_PREFIX) - 1;
+        while (isdigit(*pathname))
+            pathname++;
+        if (*pathname == '/')
+            pathname++;
+        else
+            return 0;
+    } else if (!strncmp(pathname, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1)) {
+        pathname += sizeof(DATA_USER_DE_PREFIX) - 1;
+        while (isdigit(*pathname))
+            pathname++;
+        if (*pathname == '/')
+            pathname++;
+        else
+            return 0;
+    } else if (!fnmatch(EXPAND_USER_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME)) {
+        pathname += sizeof(EXPAND_USER_PATH);
+        while (isdigit(*pathname))
+            pathname++;
+        if (*pathname == '/')
+            pathname++;
+        else
+            return 0;
+    } else if (!fnmatch(EXPAND_USER_DE_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME)) {
+        pathname += sizeof(EXPAND_USER_DE_PATH);
+        while (isdigit(*pathname))
+            pathname++;
+        if (*pathname == '/')
+            pathname++;
+        else
+            return 0;
+    } else
+        return 0;
+
+    if (!(*pathname))
+        return 0;
+
+    pkgname = strdup(pathname);
+    if (!pkgname)
+        return -1;
+
+    for (end = pkgname; *end && *end != '/'; end++)
+        ;
+    pathname = end;
+    if (*end)
+        pathname++;
+    *end = '\0';
+
+    if (!seinfo) {
+        info = package_info_lookup(pkgname);
+        if (!info) {
+            selinux_log(SELINUX_WARNING, "SELinux:  Could not look up information for package %s, cannot restorecon %s.\n",
+                        pkgname, pathname);
+            free(pkgname);
+            return -1;
+        }
+    }
+
+    ctx = context_new(secontext);
+    if (!ctx)
+        goto err;
+
+    rc = seapp_context_lookup(SEAPP_TYPE, info ? info->uid : uid, 0,
+                              info ? info->seinfo : seinfo, info ? info->name : pkgname, pathname, ctx);
+    if (rc < 0)
+        goto err;
+
+    secontext = context_str(ctx);
+    if (!secontext)
+        goto err;
+
+    if (!strcmp(secontext, *secontextp))
+        goto out;
+
+    rc = security_check_context(secontext);
+    if (rc < 0)
+        goto err;
+
+    freecon(*secontextp);
+    *secontextp = strdup(secontext);
+    if (!(*secontextp))
+        goto err;
+
+    rc = 0;
+
+out:
+    free(pkgname);
+    context_free(ctx);
+    return rc;
+err:
+    selinux_log(SELINUX_ERROR, "%s:  Error looking up context for path %s, pkgname %s, seinfo %s, uid %u: %s\n",
+                __FUNCTION__, pathname, pkgname, info->seinfo, info->uid, strerror(errno));
+    rc = -1;
+    goto out;
+}
+
+#define RESTORECON_LAST "security.restorecon_last"
+
+static int restorecon_sb(const char *pathname, const struct stat *sb,
+                         bool nochange, bool verbose,
+                         const char *seinfo, uid_t uid)
+{
+    char *secontext = NULL;
+    char *oldsecontext = NULL;
+    int rc = 0;
+
+    if (selabel_lookup(fc_sehandle, &secontext, pathname, sb->st_mode) < 0)
+        return 0;  /* no match, but not an error */
+
+    if (lgetfilecon(pathname, &oldsecontext) < 0)
+        goto err;
+
+    /*
+     * For subdirectories of /data/data or /data/user, we ignore selabel_lookup()
+     * and use pkgdir_selabel_lookup() instead. Files within those directories
+     * have different labeling rules, based off of /seapp_contexts, and
+     * installd is responsible for managing these labels instead of init.
+     */
+    if (!strncmp(pathname, DATA_DATA_PREFIX, sizeof(DATA_DATA_PREFIX)-1) ||
+        !strncmp(pathname, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1) ||
+        !strncmp(pathname, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1) ||
+        !fnmatch(EXPAND_USER_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME) ||
+        !fnmatch(EXPAND_USER_DE_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME)) {
+        if (pkgdir_selabel_lookup(pathname, seinfo, uid, &secontext) < 0)
+            goto err;
+    }
+
+    if (strcmp(oldsecontext, secontext) != 0) {
+        if (verbose)
+            selinux_log(SELINUX_INFO,
+                        "SELinux:  Relabeling %s from %s to %s.\n", pathname, oldsecontext, secontext);
+        if (!nochange) {
+            if (lsetfilecon(pathname, secontext) < 0)
+                goto err;
+        }
+    }
+
+    rc = 0;
+
+out:
+    freecon(oldsecontext);
+    freecon(secontext);
+    return rc;
+
+err:
+    selinux_log(SELINUX_ERROR,
+                "SELinux: Could not set context for %s:  %s\n",
+                pathname, strerror(errno));
+    rc = -1;
+    goto out;
+}
+
+#define SYS_PATH "/sys"
+#define SYS_PREFIX SYS_PATH "/"
+
+static int selinux_android_restorecon_common(const char* pathname_orig,
+                                             const char *seinfo,
+                                             uid_t uid,
+                                             unsigned int flags)
+{
+    bool nochange = (flags & SELINUX_ANDROID_RESTORECON_NOCHANGE) ? true : false;
+    bool verbose = (flags & SELINUX_ANDROID_RESTORECON_VERBOSE) ? true : false;
+    bool recurse = (flags & SELINUX_ANDROID_RESTORECON_RECURSE) ? true : false;
+    bool force = (flags & SELINUX_ANDROID_RESTORECON_FORCE) ? true : false;
+    bool datadata = (flags & SELINUX_ANDROID_RESTORECON_DATADATA) ? true : false;
+    bool skipce = (flags & SELINUX_ANDROID_RESTORECON_SKIPCE) ? true : false;
+    bool cross_filesystems = (flags & SELINUX_ANDROID_RESTORECON_CROSS_FILESYSTEMS) ? true : false;
+    bool issys;
+    bool setrestoreconlast = true;
+    struct stat sb;
+    struct statfs sfsb;
+    FTS *fts;
+    FTSENT *ftsent;
+    char *pathname = NULL, *pathdnamer = NULL, *pathdname, *pathbname;
+    char * paths[2] = { NULL , NULL };
+    int ftsflags = FTS_NOCHDIR | FTS_PHYSICAL;
+    int error, sverrno;
+    char xattr_value[FC_DIGEST_SIZE];
+    ssize_t size;
+
+    if (!cross_filesystems) {
+        ftsflags |= FTS_XDEV;
+    }
+
+    if (is_selinux_enabled() <= 0)
+        return 0;
+
+    __selinux_once(fc_once, file_context_init);
+
+    if (!fc_sehandle)
+        return 0;
+
+    /*
+     * Convert passed-in pathname to canonical pathname by resolving realpath of
+     * containing dir, then appending last component name.
+     */
+    pathbname = basename(pathname_orig);
+    if (!strcmp(pathbname, "/") || !strcmp(pathbname, ".") || !strcmp(pathbname, "..")) {
+        pathname = realpath(pathname_orig, NULL);
+        if (!pathname)
+            goto realpatherr;
+    } else {
+        pathdname = dirname(pathname_orig);
+        pathdnamer = realpath(pathdname, NULL);
+        if (!pathdnamer)
+            goto realpatherr;
+        if (!strcmp(pathdnamer, "/"))
+            error = asprintf(&pathname, "/%s", pathbname);
+        else
+            error = asprintf(&pathname, "%s/%s", pathdnamer, pathbname);
+        if (error < 0)
+            goto oom;
+    }
+
+    paths[0] = pathname;
+    issys = (!strcmp(pathname, SYS_PATH)
+            || !strncmp(pathname, SYS_PREFIX, sizeof(SYS_PREFIX)-1)) ? true : false;
+
+    if (!recurse) {
+        if (lstat(pathname, &sb) < 0) {
+            error = -1;
+            goto cleanup;
+        }
+
+        error = restorecon_sb(pathname, &sb, nochange, verbose, seinfo, uid);
+        goto cleanup;
+    }
+
+    /*
+     * Ignore restorecon_last on /data/data or /data/user
+     * since their labeling is based on seapp_contexts and seinfo
+     * assignments rather than file_contexts and is managed by
+     * installd rather than init.
+     */
+    if (!strncmp(pathname, DATA_DATA_PREFIX, sizeof(DATA_DATA_PREFIX)-1) ||
+        !strncmp(pathname, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1) ||
+        !strncmp(pathname, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1) ||
+        !fnmatch(EXPAND_USER_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME) ||
+        !fnmatch(EXPAND_USER_DE_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME))
+        setrestoreconlast = false;
+
+    /* Also ignore on /sys since it is regenerated on each boot regardless. */
+    if (issys)
+        setrestoreconlast = false;
+
+    /* Ignore files on in-memory filesystems */
+    if (statfs(pathname, &sfsb) == 0) {
+        if (sfsb.f_type == RAMFS_MAGIC || sfsb.f_type == TMPFS_MAGIC)
+            setrestoreconlast = false;
+    }
+
+    if (setrestoreconlast) {
+        size = getxattr(pathname, RESTORECON_LAST, xattr_value, sizeof fc_digest);
+        if (!force && size == sizeof fc_digest && memcmp(fc_digest, xattr_value, sizeof fc_digest) == 0) {
+            selinux_log(SELINUX_INFO,
+                        "SELinux: Skipping restorecon_recursive(%s)\n",
+                        pathname);
+            error = 0;
+            goto cleanup;
+        }
+    }
+
+    fts = fts_open(paths, ftsflags, NULL);
+    if (!fts) {
+        error = -1;
+        goto cleanup;
+    }
+
+    error = 0;
+    while ((ftsent = fts_read(fts)) != NULL) {
+        switch (ftsent->fts_info) {
+        case FTS_DC:
+            selinux_log(SELINUX_ERROR,
+                        "SELinux:  Directory cycle on %s.\n", ftsent->fts_path);
+            errno = ELOOP;
+            error = -1;
+            goto out;
+        case FTS_DP:
+            continue;
+        case FTS_DNR:
+            selinux_log(SELINUX_ERROR,
+                        "SELinux:  Could not read %s: %s.\n", ftsent->fts_path, strerror(errno));
+            fts_set(fts, ftsent, FTS_SKIP);
+            continue;
+        case FTS_NS:
+            selinux_log(SELINUX_ERROR,
+                        "SELinux:  Could not stat %s: %s.\n", ftsent->fts_path, strerror(errno));
+            fts_set(fts, ftsent, FTS_SKIP);
+            continue;
+        case FTS_ERR:
+            selinux_log(SELINUX_ERROR,
+                        "SELinux:  Error on %s: %s.\n", ftsent->fts_path, strerror(errno));
+            fts_set(fts, ftsent, FTS_SKIP);
+            continue;
+        case FTS_D:
+            if (issys && !selabel_partial_match(fc_sehandle, ftsent->fts_path)) {
+                fts_set(fts, ftsent, FTS_SKIP);
+                continue;
+            }
+
+            if (skipce &&
+                (!strncmp(ftsent->fts_path, DATA_SYSTEM_CE_PREFIX, sizeof(DATA_SYSTEM_CE_PREFIX)-1) ||
+                 !strncmp(ftsent->fts_path, DATA_MISC_CE_PREFIX, sizeof(DATA_MISC_CE_PREFIX)-1))) {
+                // Don't label anything below this directory.
+                fts_set(fts, ftsent, FTS_SKIP);
+                // but fall through and make sure we label the directory itself
+            }
+
+            if (!datadata &&
+                (!strcmp(ftsent->fts_path, DATA_DATA_PATH) ||
+                 !strncmp(ftsent->fts_path, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1) ||
+                 !strncmp(ftsent->fts_path, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1) ||
+                 !fnmatch(EXPAND_USER_PATH, ftsent->fts_path, FNM_LEADING_DIR|FNM_PATHNAME) ||
+                 !fnmatch(EXPAND_USER_DE_PATH, ftsent->fts_path, FNM_LEADING_DIR|FNM_PATHNAME))) {
+                // Don't label anything below this directory.
+                fts_set(fts, ftsent, FTS_SKIP);
+                // but fall through and make sure we label the directory itself
+            }
+            /* fall through */
+        default:
+            error |= restorecon_sb(ftsent->fts_path, ftsent->fts_statp, nochange, verbose, seinfo, uid);
+            break;
+        }
+    }
+
+    // Labeling successful. Mark the top level directory as completed.
+    if (setrestoreconlast && !nochange && !error)
+        setxattr(pathname, RESTORECON_LAST, fc_digest, sizeof fc_digest, 0);
+
+out:
+    sverrno = errno;
+    (void) fts_close(fts);
+    errno = sverrno;
+cleanup:
+    free(pathdnamer);
+    free(pathname);
+    return error;
+oom:
+    sverrno = errno;
+    selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __FUNCTION__);
+    errno = sverrno;
+    error = -1;
+    goto cleanup;
+realpatherr:
+    sverrno = errno;
+    selinux_log(SELINUX_ERROR, "SELinux: Could not get canonical path for %s restorecon: %s.\n",
+            pathname_orig, strerror(errno));
+    errno = sverrno;
+    error = -1;
+    goto cleanup;
+}
+
+int selinux_android_restorecon(const char *file, unsigned int flags)
+{
+    return selinux_android_restorecon_common(file, NULL, -1, flags);
+}
+
+int selinux_android_restorecon_pkgdir(const char *pkgdir,
+                                      const char *seinfo,
+                                      uid_t uid,
+                                      unsigned int flags)
+{
+    return selinux_android_restorecon_common(pkgdir, seinfo, uid, flags | SELINUX_ANDROID_RESTORECON_DATADATA);
+}
+
+
+void selinux_android_set_sehandle(const struct selabel_handle *hndl)
+{
+      fc_sehandle = (struct selabel_handle *) hndl;
+}
+
+int selinux_android_load_policy()
+{
+	int fd = -1;
+
+	fd = open(sepolicy_file, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
+	if (fd < 0) {
+		selinux_log(SELINUX_ERROR, "SELinux:  Could not open %s:  %s\n",
+				sepolicy_file, strerror(errno));
+		return -1;
+	}
+	int ret = selinux_android_load_policy_from_fd(fd, sepolicy_file);
+	close(fd);
+	return ret;
+}
+
+int selinux_android_load_policy_from_fd(int fd, const char *description)
+{
+	int rc;
+	struct stat sb;
+	void *map = NULL;
+	static int load_successful = 0;
+
+	/*
+	 * Since updating policy at runtime has been abolished
+	 * we just check whether a policy has been loaded before
+	 * and return if this is the case.
+	 * There is no point in reloading policy.
+	 */
+	if (load_successful){
+	  selinux_log(SELINUX_WARNING, "SELinux: Attempted reload of SELinux policy!/n");
+	  return 0;
+	}
+
+	set_selinuxmnt(SELINUXMNT);
+	if (fstat(fd, &sb) < 0) {
+		selinux_log(SELINUX_ERROR, "SELinux:  Could not stat %s:  %s\n",
+				description, strerror(errno));
+		return -1;
+	}
+	map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+	if (map == MAP_FAILED) {
+		selinux_log(SELINUX_ERROR, "SELinux:  Could not map %s:  %s\n",
+				description, strerror(errno));
+		return -1;
+	}
+
+	rc = security_load_policy(map, sb.st_size);
+	if (rc < 0) {
+		selinux_log(SELINUX_ERROR, "SELinux:  Could not load policy:  %s\n",
+				strerror(errno));
+		munmap(map, sb.st_size);
+		return -1;
+	}
+
+	munmap(map, sb.st_size);
+	selinux_log(SELINUX_INFO, "SELinux: Loaded policy from %s\n", description);
+	load_successful = 1;
+	return 0;
+}
+
diff --git a/libselinux/src/android/android_vendor.c b/libselinux/src/android/android_vendor.c
new file mode 100644
index 0000000..45af4c6
--- /dev/null
+++ b/libselinux/src/android/android_vendor.c
@@ -0,0 +1,14 @@
+#include "android_common.h"
+
+int selinux_android_restorecon(const char *file __attribute__((unused)),
+                               unsigned int flags __attribute__((unused)))
+{
+    selinux_log(SELINUX_ERROR, "%s: not implemented for libselinux_vendor\n", __FUNCTION__);
+    return -1;
+}
+
+struct selabel_handle* selinux_android_prop_context_handle(void)
+{
+    selinux_log(SELINUX_ERROR, "%s: not implemented for libselinux_vendor\n", __FUNCTION__);
+    return NULL;
+}
diff --git a/libselinux/src/label.c b/libselinux/src/label.c
index 48f4d2d..c051e9f 100644
--- a/libselinux/src/label.c
+++ b/libselinux/src/label.c
@@ -17,6 +17,12 @@
 
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
 
+#ifdef NO_FILE_BACKEND
+#define CONFIG_FILE_BACKEND(fnptr) NULL
+#else
+#define CONFIG_FILE_BACKEND(fnptr) &fnptr
+#endif
+
 #ifdef NO_MEDIA_BACKEND
 #define CONFIG_MEDIA_BACKEND(fnptr) NULL
 #else
@@ -46,7 +52,7 @@
 				unsigned nopts);
 
 static selabel_initfunc initfuncs[] = {
-	&selabel_file_init,
+	CONFIG_FILE_BACKEND(selabel_file_init),
 	CONFIG_MEDIA_BACKEND(selabel_media_init),
 	CONFIG_X_BACKEND(selabel_x_init),
 	CONFIG_DB_BACKEND(selabel_db_init),
@@ -143,7 +149,11 @@
 			    struct selabel_lookup_rec *lr,
 			    int translating)
 {
-	if (compat_validate(rec, lr, rec->spec_file, 0))
+	char *path = NULL;
+
+	if (rec->spec_files)
+		path = rec->spec_files[0];
+	if (compat_validate(rec, lr, path, 0))
 		return -1;
 
 	if (translating && !lr->ctx_trans &&
@@ -226,11 +236,9 @@
 	rec->digest = selabel_is_digest_set(opts, nopts, rec->digest);
 
 	if ((*initfuncs[backend])(rec, opts, nopts)) {
-		free(rec->spec_file);
-		free(rec);
+		selabel_close(rec);
 		rec = NULL;
 	}
-
 out:
 	return rec;
 }
@@ -337,10 +345,17 @@
 
 void selabel_close(struct selabel_handle *rec)
 {
+	size_t i;
+
+	if (rec->spec_files) {
+		for (i = 0; i < rec->spec_files_len; i++)
+			free(rec->spec_files[i]);
+		free(rec->spec_files);
+	}
 	if (rec->digest)
 		selabel_digest_fini(rec->digest);
-	rec->func_close(rec);
-	free(rec->spec_file);
+	if (rec->func_close)
+		rec->func_close(rec);
 	free(rec);
 }
 
diff --git a/libselinux/src/label_backends_android.c b/libselinux/src/label_backends_android.c
index cb8aae2..f85ec6f 100644
--- a/libselinux/src/label_backends_android.c
+++ b/libselinux/src/label_backends_android.c
@@ -47,7 +47,7 @@
 /*
  * Warn about duplicate specifications.
  */
-static int nodups_specs(struct saved_data *data, const char *path)
+static int nodups_specs(struct saved_data *data)
 {
 	int rc = 0;
 	unsigned int ii, jj;
@@ -64,15 +64,15 @@
 						    curr_spec->lr.ctx_raw)) {
 					selinux_log
 						(SELINUX_ERROR,
-						 "%s: Multiple different specifications for %s  (%s and %s).\n",
-						 path, curr_spec->property_key,
+						 "Multiple different specifications for %s  (%s and %s).\n",
+						 curr_spec->property_key,
 						 spec_arr[jj].lr.ctx_raw,
 						 curr_spec->lr.ctx_raw);
 				} else {
 					selinux_log
 						(SELINUX_ERROR,
-						 "%s: Multiple same specifications for %s.\n",
-						 path, curr_spec->property_key);
+						 "Multiple same specifications for %s.\n",
+						 curr_spec->property_key);
 				}
 			}
 		}
@@ -130,33 +130,23 @@
 				return -1;
 			}
 		}
+
+		data->nspec = ++nspec;
 	}
 
-	data->nspec = ++nspec;
 	return 0;
 }
 
-static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
-		unsigned n)
+static int process_file(struct selabel_handle *rec, const char *path)
 {
 	struct saved_data *data = (struct saved_data *)rec->data;
-	const char *path = NULL;
-	FILE *fp;
 	char line_buf[BUFSIZ];
 	unsigned int lineno, maxnspec, pass;
-	int status = -1;
 	struct stat sb;
-
-	/* Process arguments */
-	while (n--)
-		switch (opts[n].type) {
-		case SELABEL_OPT_PATH:
-			path = opts[n].value;
-			break;
-		}
-
-	if (!path)
-		return -1;
+	FILE *fp;
+	int status = -1;
+	unsigned int nspec;
+	spec_t *spec_arr;
 
 	/* Open the specification file. */
 	if ((fp = fopen(path, "re")) == NULL)
@@ -164,60 +154,116 @@
 
 	if (fstat(fileno(fp), &sb) < 0)
 		goto finish;
+
 	errno = EINVAL;
+
 	if (!S_ISREG(sb.st_mode))
 		goto finish;
 
 	/*
-	 * Two passes of the specification file. First is to get the size.
-	 * After the first pass, the spec array is malloced to the appropriate
-	 * size. Second pass is to populate the spec array and check for
-	 * dups.
+	 * Two passes per specification file. First is to get the size.
+	 * After the first pass, the spec array is malloced / realloced to
+	 * the appropriate size. Second pass is to populate the spec array.
 	 */
 	maxnspec = UINT_MAX / sizeof(spec_t);
 	for (pass = 0; pass < 2; pass++) {
-		data->nspec = 0;
+		nspec = 0;
 		lineno = 0;
 
-		while (fgets(line_buf, sizeof(line_buf) - 1, fp)
-		       && data->nspec < maxnspec) {
-			if (process_line(rec, path, line_buf, pass, ++lineno)
-									  != 0)
+		while (fgets(line_buf, sizeof(line_buf) - 1, fp) &&
+			nspec < maxnspec) {
+			if (process_line(rec, path, line_buf, pass, ++lineno))
 				goto finish;
-		}
-
-		if (pass == 1) {
-			status = nodups_specs(data, path);
-
-			if (status)
-				goto finish;
+			nspec++;
 		}
 
 		if (pass == 0) {
-			if (data->nspec == 0) {
+			if (nspec == 0) {
 				status = 0;
 				goto finish;
 			}
 
-			if (NULL == (data->spec_arr =
-				     calloc(data->nspec, sizeof(spec_t))))
+			/* grow spec array if required */
+			spec_arr = realloc(data->spec_arr,
+					(data->nspec + nspec) * sizeof(spec_t));
+			if (spec_arr == NULL)
 				goto finish;
 
-			maxnspec = data->nspec;
+			memset(&spec_arr[data->nspec], 0, nspec * sizeof(spec_t));
+			data->spec_arr = spec_arr;
+			maxnspec = nspec;
 			rewind(fp);
 		}
 	}
 
-	qsort(data->spec_arr, data->nspec, sizeof(struct spec), cmp);
-
 	status = digest_add_specfile(rec->digest, fp, NULL, sb.st_size, path);
+
+finish:
+	fclose(fp);
+	return status;
+}
+
+static void closef(struct selabel_handle *rec);
+
+static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
+		unsigned n)
+{
+	struct saved_data *data = (struct saved_data *)rec->data;
+	char **paths = NULL;
+	size_t num_paths = 0;
+	int status = -1;
+	size_t i;
+
+	/* Process arguments */
+	i = n;
+	while (i--) {
+		switch (opts[i].type) {
+		case SELABEL_OPT_PATH:
+			num_paths++;
+			break;
+		}
+	}
+
+	if (!num_paths)
+		return -1;
+
+	paths = calloc(num_paths, sizeof(*paths));
+	if (!paths)
+		return -1;
+
+	rec->spec_files = paths;
+	rec->spec_files_len = num_paths;
+
+	i = n;
+	while (i--) {
+		switch(opts[i].type) {
+		case SELABEL_OPT_PATH:
+			*paths = strdup(opts[i].value);
+			if (*paths == NULL)
+				goto finish;
+			paths++;
+		}
+	}
+
+	for (i = 0; i < num_paths; i++) {
+		status = process_file(rec, rec->spec_files[i]);
+		if (status)
+			goto finish;
+	}
+
+	/* warn about duplicates after all files have been processed. */
+	status = nodups_specs(data);
 	if (status)
 		goto finish;
 
+	qsort(data->spec_arr, data->nspec, sizeof(struct spec), cmp);
+
 	digest_gen_hash(rec->digest);
 
 finish:
-	fclose(fp);
+	if (status)
+		closef(rec);
+
 	return status;
 }
 
@@ -230,15 +276,16 @@
 	struct spec *spec;
 	unsigned int i;
 
-	for (i = 0; i < data->nspec; i++) {
-		spec = &data->spec_arr[i];
-		free(spec->property_key);
-		free(spec->lr.ctx_raw);
-		free(spec->lr.ctx_trans);
-	}
+	if (data->spec_arr) {
+		for (i = 0; i < data->nspec; i++) {
+			spec = &data->spec_arr[i];
+			free(spec->property_key);
+			free(spec->lr.ctx_raw);
+			free(spec->lr.ctx_trans);
+		}
 
-	if (data->spec_arr)
 		free(data->spec_arr);
+	}
 
 	free(data);
 }
diff --git a/libselinux/src/label_file.c b/libselinux/src/label_file.c
index 560d8c3..b3b36bc 100644
--- a/libselinux/src/label_file.c
+++ b/libselinux/src/label_file.c
@@ -709,28 +709,61 @@
 		unsigned n)
 {
 	struct saved_data *data = (struct saved_data *)rec->data;
-	const char *path = NULL;
+	size_t num_paths = 0;
+	char **path = NULL;
 	const char *prefix = NULL;
-	int status = -1, baseonly = 0;
+	int status = -1;
+	size_t i;
+	bool baseonly = false;
+	bool path_provided;
 
 	/* Process arguments */
-	while (n--)
-		switch(opts[n].type) {
+	i = n;
+	while (i--)
+		switch(opts[i].type) {
 		case SELABEL_OPT_PATH:
-			path = opts[n].value;
+			num_paths++;
 			break;
 		case SELABEL_OPT_SUBSET:
-			prefix = opts[n].value;
+			prefix = opts[i].value;
 			break;
 		case SELABEL_OPT_BASEONLY:
-			baseonly = !!opts[n].value;
+			baseonly = !!opts[i].value;
 			break;
 		}
 
+	if (!num_paths) {
+		num_paths = 1;
+		path_provided = false;
+	} else {
+		path_provided = true;
+	}
+
+	path = calloc(num_paths, sizeof(*path));
+	if (path == NULL) {
+		goto finish;
+	}
+	rec->spec_files = path;
+	rec->spec_files_len = num_paths;
+
+	if (path_provided) {
+		for (i = 0; i < n; i++) {
+			switch(opts[i].type) {
+			case SELABEL_OPT_PATH:
+				*path = strdup(opts[i].value);
+				if (*path == NULL)
+					goto finish;
+				path++;
+				break;
+			default:
+				break;
+			}
+		}
+	}
 #if !defined(BUILD_HOST) && !defined(ANDROID)
 	char subs_file[PATH_MAX + 1];
 	/* Process local and distribution substitution files */
-	if (!path) {
+	if (!path_provided) {
 		status = selabel_subs_init(
 			selinux_file_context_subs_dist_path(),
 			rec->digest, &data->dist_subs);
@@ -740,43 +773,52 @@
 			rec->digest, &data->subs);
 		if (status)
 			goto finish;
-		path = selinux_file_context_path();
+		rec->spec_files[0] = strdup(selinux_file_context_path());
+		if (rec->spec_files[0] == NULL)
+			goto finish;
 	} else {
-		snprintf(subs_file, sizeof(subs_file), "%s.subs_dist", path);
-		status = selabel_subs_init(subs_file, rec->digest,
+		for (i = 0; i < num_paths; i++) {
+			snprintf(subs_file, sizeof(subs_file), "%s.subs_dist", rec->spec_files[i]);
+			status = selabel_subs_init(subs_file, rec->digest,
 					   &data->dist_subs);
-		if (status)
-			goto finish;
-		snprintf(subs_file, sizeof(subs_file), "%s.subs", path);
-		status = selabel_subs_init(subs_file, rec->digest,
+			if (status)
+				goto finish;
+			snprintf(subs_file, sizeof(subs_file), "%s.subs", rec->spec_files[i]);
+			status = selabel_subs_init(subs_file, rec->digest,
 					   &data->subs);
-		if (status)
-			goto finish;
+			if (status)
+				goto finish;
+		}
 	}
-
+#else
+	if (!path_provided) {
+		selinux_log(SELINUX_ERROR, "No path given to file labeling backend\n");
+		goto finish;
+	}
 #endif
-	rec->spec_file = strdup(path);
 
 	/*
-	 * The do detailed validation of the input and fill the spec array
+	 * Do detailed validation of the input and fill the spec array
 	 */
-	status = process_file(path, NULL, rec, prefix, rec->digest);
-	if (status)
-		goto finish;
-
-	if (rec->validating) {
-		status = nodups_specs(data, path);
+	for (i = 0; i < num_paths; i++) {
+		status = process_file(rec->spec_files[i], NULL, rec, prefix, rec->digest);
 		if (status)
 			goto finish;
+
+		if (rec->validating) {
+			status = nodups_specs(data, rec->spec_files[i]);
+			if (status)
+				goto finish;
+		}
 	}
 
 	if (!baseonly) {
-		status = process_file(path, "homedirs", rec, prefix,
+		status = process_file(rec->spec_files[0], "homedirs", rec, prefix,
 							    rec->digest);
 		if (status && errno != ENOENT)
 			goto finish;
 
-		status = process_file(path, "local", rec, prefix,
+		status = process_file(rec->spec_files[0], "local", rec, prefix,
 							    rec->digest);
 		if (status && errno != ENOENT)
 			goto finish;
@@ -804,6 +846,12 @@
 	struct stem *stem;
 	unsigned int i;
 
+	if (!data)
+		return;
+
+	/* make sure successive ->func_close() calls are harmless */
+	rec->data = NULL;
+
 	selabel_subs_fini(data->subs);
 	selabel_subs_fini(data->dist_subs);
 
diff --git a/libselinux/src/label_internal.h b/libselinux/src/label_internal.h
index c55efb7..6810018 100644
--- a/libselinux/src/label_internal.h
+++ b/libselinux/src/label_internal.h
@@ -98,10 +98,12 @@
 	void *data;
 
 	/*
-	 * The main spec file used. Note for file contexts the local and/or
+	 * The main spec file(s) used. Note for file contexts the local and/or
 	 * homedirs could also have been used to resolve a context.
 	 */
-	char *spec_file;
+	size_t spec_files_len;
+	char **spec_files;
+
 
 	/* ptr to SHA1 hash information if SELABEL_OPT_DIGEST set */
 	struct selabel_digest *digest;
diff --git a/libsepol/Android.bp b/libsepol/Android.bp
new file mode 100644
index 0000000..49398b4
--- /dev/null
+++ b/libsepol/Android.bp
@@ -0,0 +1,106 @@
+common_CFLAGS = [
+    "-D_GNU_SOURCE",
+    "-Wall",
+    "-W",
+    "-Wundef",
+    "-Wshadow",
+    "-Wmissing-noreturn",
+    "-Wmissing-format-attribute",
+]
+
+cc_library {
+    name: "libsepol",
+    host_supported: true,
+    cflags: common_CFLAGS,
+    srcs: [
+        "src/assertion.c",
+        "src/avrule_block.c",
+        "src/avtab.c",
+        "src/boolean_record.c",
+        "src/booleans.c",
+        "src/conditional.c",
+        "src/constraint.c",
+        "src/context.c",
+        "src/context_record.c",
+        "src/debug.c",
+        "src/ebitmap.c",
+        "src/expand.c",
+        "src/genbools.c",
+        "src/genusers.c",
+        "src/handle.c",
+        "src/hashtab.c",
+        "src/hierarchy.c",
+        "src/iface_record.c",
+        "src/interfaces.c",
+        "src/kernel_to_cil.c",
+        "src/kernel_to_common.c",
+        "src/kernel_to_conf.c",
+        "src/link.c",
+        "src/mls.c",
+        "src/module.c",
+        "src/module_to_cil.c",
+        "src/node_record.c",
+        "src/nodes.c",
+        "src/polcaps.c",
+        "src/policydb.c",
+        "src/policydb_convert.c",
+        "src/policydb_public.c",
+        "src/port_record.c",
+        "src/ports.c",
+        "src/roles.c",
+        "src/services.c",
+        "src/sidtab.c",
+        "src/symtab.c",
+        "src/user_record.c",
+        "src/users.c",
+        "src/util.c",
+        "src/write.c",
+        "cil/src/android.c",
+        "cil/src/cil_binary.c",
+        "cil/src/cil_build_ast.c",
+        "cil/src/cil.c",
+        "cil/src/cil_copy_ast.c",
+        "cil/src/cil_find.c",
+        "cil/src/cil_fqn.c",
+        "cil/src/cil_lexer.l",
+        "cil/src/cil_list.c",
+        "cil/src/cil_log.c",
+        "cil/src/cil_mem.c",
+        "cil/src/cil_parser.c",
+        "cil/src/cil_policy.c",
+        "cil/src/cil_post.c",
+        "cil/src/cil_reset_ast.c",
+        "cil/src/cil_resolve_ast.c",
+        "cil/src/cil_stack.c",
+        "cil/src/cil_strpool.c",
+        "cil/src/cil_symtab.c",
+        "cil/src/cil_tree.c",
+        "cil/src/cil_verify.c",
+        "cil/src/cil_write_ast.c",
+    ],
+    local_include_dirs: [
+        "cil/src",
+        "src",
+    ],
+    export_include_dirs: [
+        "cil/include",
+        "include",
+    ],
+    stl: "none",
+    // The host version of libsepol is loaded by the system python, which does
+    // not have the sanitizer runtimes.
+    target: {
+        host: {
+            sanitize: {
+                never: true,
+            },
+        },
+    },
+}
+
+cc_binary_host {
+    name: "chkcon",
+    srcs: ["utils/chkcon.c"],
+    shared_libs: ["libsepol"],
+    cflags: common_CFLAGS,
+}
diff --git a/libsepol/cil/include/cil/android.h b/libsepol/cil/include/cil/android.h
new file mode 100644
index 0000000..082d7fd
--- /dev/null
+++ b/libsepol/cil/include/cil/android.h
@@ -0,0 +1,32 @@
+#ifndef _SEPOL_ANDROID_H_
+#define _SEPOL_ANDROID_H_
+#include <cil/cil.h>
+
+#define PLAT_VERS "curr"
+#define PLAT_ID "p"
+#define NON_PLAT_ID "n"
+
+/*
+ * cil_android_attrib_mapping - extract attributizable elements of the policy in
+ * srcdb and create the mapping file necessary to link the platform and
+ * non-platform policy files after non-platform policy attributization.
+ *   mdb - uninitialized cil_db reference to the resulting policy. Caller
+ *         responsibility to destroy.
+ *   srcdb - initialized and parsed cil_db reference to source public policy.
+ *   num - the version string to append types when converted to attributes.
+ * returns SEPOL_OK if successful, otherwise passes on the encountered error.
+ */
+int cil_android_attrib_mapping(struct cil_db **mdb, struct cil_db *srcdb, const char *num);
+
+/*
+ * cil_android_attributize - extract attributizable elements of the policy in
+ * srcdb and convert all usage of those elements in tgtdb to versioned attributes.
+ *   tgtdb - initialized and parsed cil_db reference to modify.
+ *   srcdb - initialized and parsed cil_db reference to source public policy
+ *           from which to extract attributizable elements.
+ *   num - the version string to append types when converted to attributes.
+ * returns SEPOL_OK if successful, otherwise passes on the encountered error.
+ */
+int cil_android_attributize(struct cil_db *tgtdb, struct cil_db *srcdb, const char *num);
+
+#endif /* _SEPOL_ANDROID_H_ */
diff --git a/libsepol/cil/include/cil/cil_write_ast.h b/libsepol/cil/include/cil/cil_write_ast.h
new file mode 100644
index 0000000..77c3087
--- /dev/null
+++ b/libsepol/cil/include/cil/cil_write_ast.h
@@ -0,0 +1,7 @@
+#ifndef CIL_WRITE_H_
+#define CIL_WRITE_H_
+
+#include <cil/cil.h>
+
+int cil_write_ast(struct cil_db *db, const char* path);
+#endif /* CIL_WRITE_H_ */
diff --git a/libsepol/cil/src/android.c b/libsepol/cil/src/android.c
new file mode 100644
index 0000000..7fade52
--- /dev/null
+++ b/libsepol/cil/src/android.c
@@ -0,0 +1,933 @@
+#include <cil/android.h>
+#include <sepol/policydb/hashtab.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cil_build_ast.h"
+#include "cil_internal.h"
+#include "cil_strpool.h"
+#include "cil_symtab.h"
+#include "cil_tree.h"
+
+#define VER_MAP_SZ (1 << 12)
+
+/* added to hashmap - currently unused as hashmap is used as a set */
+struct version_datum {
+	struct cil_db *db;
+	struct cil_tree_node *ast_node;
+	char *orig_name;
+};
+
+struct version_args {
+	struct cil_db *db;
+	hashtab_t vers_map;
+	const char *num;
+};
+
+enum plat_flavor {
+	PLAT_NONE = 0,
+	PLAT_TYPE,
+	PLAT_ATTRIB
+};
+
+static unsigned int ver_map_hash_val(hashtab_t h, const_hashtab_key_t key)
+{
+	/* from cil_stpool.c */
+	char *p, *keyp;
+	size_t size;
+	unsigned int val;
+
+	val = 0;
+	keyp = (char*)key;
+	size = strlen(keyp);
+	for (p = keyp; ((size_t) (p - keyp)) < size; p++)
+		val =
+			(val << 4 | (val >> (8 * sizeof(unsigned int) - 4))) ^ (*p);
+	return val & (h->size - 1);
+}
+
+
+static int ver_map_key_cmp(hashtab_t h __attribute__ ((unused)),
+			   const_hashtab_key_t key1, const_hashtab_key_t key2)
+{
+	/* hashtab_key_t is just a const char* underneath */
+	return strcmp(key1, key2);
+}
+
+/*
+ * version_datum  pointers all refer to memory owned elsewhere, so just free the
+ * datum itself.
+ */
+static int ver_map_entry_destroy(__attribute__ ((unused))hashtab_key_t k,
+				 hashtab_datum_t d, __attribute__ ((unused))void *args)
+{
+	free(d);
+	return 0;
+}
+
+static void ver_map_destroy(hashtab_t h)
+{
+	hashtab_map(h, ver_map_entry_destroy, NULL);
+	hashtab_destroy(h);
+}
+
+static int __extract_attributees_helper(struct cil_tree_node *node, uint32_t *finished, void *extra_args)
+{
+	int rc = SEPOL_ERR;
+	struct version_args *args = (struct version_args *) extra_args;
+	char *key;
+	struct version_datum *datum;
+
+	if (node == NULL || finished == NULL || extra_args == NULL) {
+		goto exit;
+	}
+
+	switch (node->flavor) {
+	case CIL_ROLE:
+		cil_log(CIL_ERR, "%s unsupported statement in attributee policy (line %d)\n",
+			CIL_KEY_ROLE, node->line);
+		rc = SEPOL_ERR;
+		break;
+	case CIL_TYPE:
+	case CIL_TYPEATTRIBUTE:
+		datum = cil_malloc(sizeof(*datum));
+		datum->db = args->db;
+		datum->ast_node = node;
+		datum->orig_name = DATUM(node->data)->name;
+		key = datum->orig_name;
+		if (!strncmp(key, "base_typeattr_", 14)) {
+			/* checkpolicy creates base attributes which are just typeattributesets,
+			   of the existing types and attributes.  These may be differnt in
+			   every checkpolicy output, ignore them here, they'll be dealt with
+			   as a special case when attributizing. */
+			free(datum);
+		} else {
+			rc = hashtab_insert(args->vers_map, (hashtab_key_t) key, (hashtab_datum_t) datum);
+			if (rc != SEPOL_OK) {
+				goto exit;
+			}
+		}
+		break;
+	case CIL_TYPEALIAS:
+		cil_log(CIL_ERR, "%s unsupported statement in attributee policy (line %d)\n",
+			CIL_KEY_TYPEALIAS, node->line);
+		goto exit;
+		break;
+	case CIL_TYPEPERMISSIVE:
+		cil_log(CIL_ERR, "%s unsupported statement in attributee policy (line %d)\n",
+			CIL_KEY_TYPEPERMISSIVE, node->line);
+		goto exit;
+		break;
+	case CIL_NAMETYPETRANSITION:
+	case CIL_TYPE_RULE:
+		cil_log(CIL_ERR, "%s unsupported statement in attributee policy (line %d)\n",
+			CIL_KEY_TYPETRANSITION, node->line);
+		goto exit;
+		break;
+	default:
+		break;
+	}
+	return SEPOL_OK;
+exit:
+	return rc;
+}
+
+/*
+ * For the given db, with an already-built AST, fill the vers_map hash table
+ * with every encountered type and attribute.  This could eventually be expanded
+ * to include other language constructs, such as users and roles, in which case
+ * multiple hash tables would be needed.  These tables can then be used by
+ * attributize() to change all references to these types.
+ */
+int cil_extract_attributees(struct cil_db *db, hashtab_t vers_map)
+{
+	/* walk ast. */
+	int rc = SEPOL_ERR;
+	struct version_args extra_args;
+	extra_args.db = db;
+	extra_args.vers_map = vers_map;
+	extra_args.num = NULL;
+	rc = cil_tree_walk(db->ast->root, __extract_attributees_helper, NULL, NULL, &extra_args);
+	if (rc != SEPOL_OK) {
+		goto exit;
+	}
+
+	return SEPOL_OK;
+exit:
+	return rc;
+}
+
+static enum plat_flavor __cil_get_plat_flavor(hashtab_t vers_map, hashtab_key_t key)
+{
+	enum plat_flavor rc;
+	struct version_datum *vers_datum;
+
+	vers_datum = (struct version_datum *)hashtab_search(vers_map, key);
+	if (vers_datum == NULL) {
+		return PLAT_NONE;
+	}
+	switch (vers_datum->ast_node->flavor) {
+	case CIL_TYPE:
+		rc = PLAT_TYPE;
+		break;
+	case CIL_TYPEATTRIBUTE:
+		rc = PLAT_ATTRIB;
+		break;
+	default:
+		rc = PLAT_NONE;
+		break;
+	}
+	return rc;
+}
+
+/*
+ * Takes the old name and version string and creates a new strpool entry by
+ * combining them.
+ */
+static char *__cil_attrib_get_versname(char *old, const char *vers)
+{
+	size_t len = 0;
+	char *tmp_new = NULL;
+	char *final;
+
+	len += strlen(old) + strlen(vers) + 2;
+	tmp_new = cil_malloc(len);
+	snprintf(tmp_new, len, "%s_%s", old, vers);
+	final = cil_strpool_add(tmp_new);
+	free(tmp_new);
+	return final;
+}
+
+/*
+ * Change type to attribute - create new versioned name based on old, create
+ * typeattribute node and replace existing type node.
+ */
+static int __cil_attrib_convert_type(struct cil_tree_node *node, struct version_args *args)
+{
+	int rc = SEPOL_ERR;
+	struct cil_type *type = (struct cil_type *)node->data;
+	struct cil_typeattribute *typeattr = NULL;
+	char *new_key;
+
+	cil_typeattribute_init(&typeattr);
+
+	new_key = __cil_attrib_get_versname(type->datum.name, args->num);
+
+	cil_symtab_datum_remove_node(&type->datum, node);
+	cil_destroy_type(type);
+
+	rc = cil_gen_node(args->db, node, (struct cil_symtab_datum *) typeattr,
+			  new_key, CIL_SYM_TYPES, CIL_TYPEATTRIBUTE);
+	if (rc != SEPOL_OK) {
+		goto exit;
+	}
+
+	return SEPOL_OK;
+exit:
+	return rc;
+}
+
+/*
+ * Update datum - create new key, remove entry under old key,
+ * update entry, and insert under new key
+ */
+static int __cil_attrib_swap_symtab_key(struct cil_tree_node *node, char *old_key,
+					const char *num)
+{
+	int rc = SEPOL_ERR;
+	char *new_key;
+	symtab_t *symtab;
+	struct cil_symtab_datum *datum = (struct cil_symtab_datum *) node->data;
+
+	new_key = __cil_attrib_get_versname(old_key, num);
+
+	symtab = datum->symtab;
+
+	/* TODO: remove, but what happens to other nodes on this datum ?*/
+	cil_list_remove(datum->nodes, CIL_NODE, node, 0);
+	cil_symtab_remove_datum(datum);
+
+	rc = cil_symtab_insert(symtab, new_key, datum, node);
+
+	if (rc != SEPOL_OK) {
+		goto exit;
+	}
+
+	return SEPOL_OK;
+exit:
+	return rc;
+}
+
+/*
+ * expressions may contains strings which are not in the type-attribute
+ * namespace, so this is not a general cil_expr attributizer.
+ * TODO: add support for other types of expressions which may contain types.
+ */
+static int cil_attrib_type_expr(struct cil_list *expr_str, struct version_args *args)
+{
+	int rc = SEPOL_ERR;
+	struct cil_list_item *curr = NULL;
+	char *new;
+	hashtab_key_t key;
+
+	/* iterate through cil_list, replacing types */
+	cil_list_for_each(curr, expr_str) {
+		switch(curr->flavor) {
+		case CIL_LIST:
+			rc = cil_attrib_type_expr((struct cil_list *)curr->data, args);
+			if (rc != SEPOL_OK)
+				goto exit;
+			break;
+		case CIL_STRING:
+			key = (hashtab_key_t) curr->data;
+			enum plat_flavor pf = __cil_get_plat_flavor(args->vers_map, key);
+			if (!strncmp(curr->data, "base_typeattr_", 14) || pf == PLAT_TYPE) {
+				new = __cil_attrib_get_versname((char *) curr->data, args->num);
+				curr->data = (void *) new;
+			}
+			break;
+		case CIL_DATUM:
+			cil_log(CIL_ERR, "AST already resolved. Not yet supported.\n");
+			rc = SEPOL_ERR;
+			goto exit;
+			break;
+		default:
+			break;
+		}
+	}
+
+	return SEPOL_OK;
+exit:
+	return rc;
+}
+
+static int cil_attrib_check_context(struct cil_context *ctxt, struct version_args *args)
+{
+	int rc = SEPOL_ERR;
+	hashtab_key_t key;
+
+	if (ctxt->type != NULL) {
+		cil_log(CIL_ERR, "AST already resolved. Not yet supported.\n");
+		goto exit;
+	}
+
+	key = (hashtab_key_t) ctxt->type_str;
+	if (__cil_get_plat_flavor(args->vers_map, key) != PLAT_NONE) {
+        /* TODO: reinstate check, but leave out for now
+		cil_log(CIL_ERR, "AST contains context with platform public type: %s\n",
+			ctxt->type_str);
+		rc = SEPOL_ERR;
+		goto exit; */
+	}
+
+	return SEPOL_OK;
+exit:
+	return rc;
+}
+
+static int cil_attrib_sidcontext(struct cil_tree_node *node, struct version_args *args)
+{
+	int rc = SEPOL_ERR;
+	struct cil_sidcontext *sidcon = (struct cil_sidcontext *)node->data;
+
+	if (sidcon->context_str == NULL) {
+		/* sidcon contains an anon context, which needs to have type checked */
+		rc = cil_attrib_check_context(sidcon->context, args);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+	}
+
+	return SEPOL_OK;
+exit:
+	return rc;
+}
+
+static int cil_attrib_context(struct cil_tree_node *node, struct version_args *args)
+{
+	struct cil_context *ctxt = (struct cil_context *)node->data;
+
+	return cil_attrib_check_context(ctxt, args);
+}
+
+static int cil_attrib_roletype(struct cil_tree_node *node,
+			       __attribute__((unused)) struct version_args *args)
+{
+	int rc = SEPOL_ERR;
+	char *key;
+	struct cil_roletype *roletype = (struct cil_roletype *)node->data;
+
+	if (roletype->role) {
+		cil_log(CIL_ERR, "AST already resolved.  !!! Not yet supported.\n");
+		goto exit;
+	}
+	key = roletype->type_str;
+	if (__cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) == PLAT_TYPE) {
+		roletype->type_str = __cil_attrib_get_versname(key, args->num);
+	}
+
+	return SEPOL_OK;
+exit:
+	return rc;
+}
+
+static int cil_attrib_type(struct cil_tree_node *node, struct version_args *args)
+{
+	int rc = SEPOL_ERR;
+	struct cil_type *type = (struct cil_type *)node->data;
+	char *key = type->datum.name;
+
+	if (type->value) {
+		cil_log(CIL_ERR, "AST already resolved.  !!! Not yet supported.\n");
+		goto exit;
+	}
+	if (__cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) == PLAT_TYPE) {
+		rc = __cil_attrib_convert_type(node, args);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+	}
+
+	return SEPOL_OK;
+exit:
+	return rc;
+}
+
+static int cil_attrib_typepermissive(struct cil_tree_node *node,
+				     struct version_args *args __attribute__ ((unused)))
+{
+	struct cil_typepermissive *typeperm = (struct cil_typepermissive *)node->data;
+
+	if (typeperm->type != NULL) {
+		cil_log(CIL_ERR, "AST already resolved.  ### Not yet supported.\n");
+		return SEPOL_ERR;
+	}
+
+	return SEPOL_OK;
+}
+
+static int cil_attrib_typeattribute(struct cil_tree_node *node, struct version_args *args)
+{
+	int rc = SEPOL_ERR;
+	struct cil_typeattribute *typeattr = (struct cil_typeattribute *)node->data;
+	char *key = typeattr->datum.name;
+
+	if (typeattr->types) {
+		cil_log(CIL_ERR, "AST already resolved. Not yet supported (line %d).\n",
+			node->line);
+		goto exit;
+	}
+	if (!strncmp(key, "base_typeattr_", 14)) {
+		rc = __cil_attrib_swap_symtab_key(node, key, args->num);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+	} else if (__cil_get_plat_flavor(args->vers_map, key) == PLAT_ATTRIB) {
+		// platform attribute declaration to be provided by platform policy
+		cil_symtab_datum_remove_node(&typeattr->datum, node);
+		cil_destroy_typeattribute(typeattr);
+		node->flavor = CIL_NONE; // traversal relies on this node sticking around, empty it.
+	}
+
+	return SEPOL_OK;
+exit:
+	return rc;
+}
+
+static int cil_attrib_typeattributeset(struct cil_tree_node *node, struct version_args *args)
+{
+	int rc = SEPOL_ERR;
+	char *key;
+	struct cil_typeattributeset *typeattrset = (struct cil_typeattributeset *) node->data;
+
+	if (typeattrset->datum_expr != NULL) {
+		cil_log(CIL_ERR, "AST already resolved. Not yet supported (line %d).\n",
+			node->line);
+		goto exit;
+	}
+
+	key = typeattrset->attr_str;
+	/* first check to see if the attribute to which this set belongs is versioned */
+	if (!strncmp(key, "base_typeattr_", 14)) {
+		typeattrset->attr_str = __cil_attrib_get_versname(key, args->num);
+	}
+
+	rc = cil_attrib_type_expr(typeattrset->str_expr, args);
+	if (rc != SEPOL_OK) {
+		goto exit;
+	}
+
+	return SEPOL_OK;
+exit:
+	return rc;
+}
+
+static int cil_attrib_typealiasactual(struct cil_tree_node *node, struct version_args *args)
+{
+	int rc = SEPOL_ERR;
+	char *key;
+	struct cil_aliasactual *aliasact = (struct cil_aliasactual *)node->data;
+
+	key = aliasact->actual_str;
+	if (__cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) != PLAT_NONE) {
+		cil_log(CIL_ERR, "%s with platform public type not allowed (line %d)\n",
+		    CIL_KEY_TYPEALIASACTUAL, node->line);
+		goto exit;
+	}
+
+	return SEPOL_OK;
+exit:
+	return rc;
+}
+
+static int cil_attrib_nametypetransition(struct cil_tree_node *node, struct version_args *args)
+{
+	int rc = SEPOL_ERR;
+	char *key;
+	struct cil_nametypetransition *namettrans = (struct cil_nametypetransition *)node->data;
+
+	if (namettrans->src != NULL) {
+		cil_log(CIL_ERR, "AST already resolved. Not yet supported (line %d).\n",
+			node->line);
+		goto exit;
+	}
+	key = namettrans->src_str;
+	if (__cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) == PLAT_TYPE) {
+		namettrans->src_str = __cil_attrib_get_versname(key, args->num);
+	}
+
+	key = namettrans->tgt_str;
+	if (__cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) == PLAT_TYPE) {
+		namettrans->tgt_str = __cil_attrib_get_versname(key, args->num);
+	}
+
+	return SEPOL_OK;
+exit:
+	return rc;
+}
+
+/*
+ * This is exactly the same as cil_attrib_nametypetransition, but the struct
+ * layouts differ, so we can't reuse it.
+ */
+static int cil_attrib_type_rule(struct cil_tree_node *node, struct version_args *args)
+{
+	int rc = SEPOL_ERR;
+	char *key;
+	struct cil_type_rule *type_rule = (struct cil_type_rule *)node->data;
+
+	if (type_rule->src != NULL) {
+		cil_log(CIL_ERR, "AST already resolved. Not yet supported (line %d).\n",
+			node->line);
+		goto exit;
+	}
+	key = type_rule->src_str;
+	if (__cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) == PLAT_TYPE) {
+		type_rule->src_str = __cil_attrib_get_versname(key, args->num);
+	}
+
+	key = type_rule->tgt_str;
+	if (__cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) == PLAT_TYPE) {
+		type_rule->tgt_str = __cil_attrib_get_versname(key, args->num);
+	}
+
+	return SEPOL_OK;
+exit:
+	return rc;
+}
+
+static int cil_attrib_avrule(struct cil_tree_node *node, struct version_args *args)
+{
+	int rc = SEPOL_ERR;
+	char *key;
+	struct cil_avrule *avrule = (struct cil_avrule *)node->data;
+
+	if (avrule->src != NULL) {
+		cil_log(CIL_ERR, "AST already resolved. Not yet supported (line %d).\n",
+			node->line);
+		goto exit;
+	}
+
+	key = avrule->src_str;
+	if (!strncmp(key, "base_typeattr_", 14) ||
+	    __cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) == PLAT_TYPE) {
+		avrule->src_str = __cil_attrib_get_versname(key, args->num);
+	}
+
+	key = avrule->tgt_str;
+	if (!strncmp(key, "base_typeattr_", 14) ||
+	    __cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) == PLAT_TYPE) {
+		avrule->tgt_str = __cil_attrib_get_versname(key, args->num);
+	}
+
+	return SEPOL_OK;
+exit:
+	return rc;
+}
+
+static int cil_attrib_genfscon(struct cil_tree_node *node, struct version_args *args)
+{
+	int rc = SEPOL_ERR;
+
+	struct cil_genfscon *genfscon = (struct cil_genfscon *)node->data;
+
+	if (genfscon->context_str == NULL) {
+		/* genfscon contains an anon context, which needs to have type checked */
+		rc = cil_attrib_check_context(genfscon->context, args);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+	}
+
+	return SEPOL_OK;
+exit:
+	return rc;
+}
+
+static int cil_attrib_fsuse(struct cil_tree_node *node, struct version_args *args)
+{
+	int rc = SEPOL_ERR;
+	struct cil_fsuse *fsuse = (struct cil_fsuse *)node->data;
+
+	if (fsuse->context_str == NULL) {
+		/* fsuse contains an anon context, which needs to have type checked */
+		rc = cil_attrib_check_context(fsuse->context, args);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+	}
+
+	return SEPOL_OK;
+exit:
+	return rc;
+}
+
+static int __attributize_helper(struct cil_tree_node *node, uint32_t *finished, void *extra_args)
+{
+	int rc = SEPOL_ERR;
+	struct version_args *args = (struct version_args *) extra_args;
+
+	if (node == NULL || finished == NULL || extra_args == NULL) {
+		goto exit;
+	}
+
+	switch (node->flavor) {
+	case CIL_SIDCONTEXT:
+		/* contains type, but shouldn't involve an attributized type, maybe add
+		   a check on type and error if it conflicts */
+		rc = cil_attrib_sidcontext(node, args);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+		break;
+	case CIL_ROLE:
+		cil_log(CIL_ERR, "%s declaration illegal non-platform policy (line %d)\n",
+			CIL_KEY_ROLE, node->line);
+		rc = SEPOL_ERR;
+		break;
+	case CIL_ROLETYPE:
+		/* Yes, this is needed if we support roletype in non-platform policy.
+		   type_id can be type, typealias or typeattr */
+		rc = cil_attrib_roletype(node, args);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+		break;
+	case CIL_ROLEATTRIBUTE:
+		/* don't think this is needed, only used for cil_gen_req, and we aren't
+		   yet supporting roles in non-platform policy. */
+		break;
+	case CIL_TYPE:
+		/* conver to attribute if in policy */
+		rc = cil_attrib_type(node, args);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+		break;
+	case CIL_TYPEPERMISSIVE:
+		rc = cil_attrib_typepermissive(node, args);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+		break;
+	case CIL_TYPEATTRIBUTE:
+		rc = cil_attrib_typeattribute(node, args);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+		break;
+	case CIL_TYPEATTRIBUTESET:
+		rc = cil_attrib_typeattributeset(node, args);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+		break;
+	case CIL_TYPEALIASACTUAL:
+		/* this will break on an attributized type - identify it and throw error */
+		rc = cil_attrib_typealiasactual(node, args);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+		break;
+	case CIL_NAMETYPETRANSITION:
+		/* not allowed in plat-policy. Types present, throw error if attributee */
+		rc = cil_attrib_nametypetransition(node, args);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+		break;
+	case CIL_TYPE_RULE:
+		/* not allowed in plat-policy. Types present, throw error if attributee */
+		rc = cil_attrib_type_rule(node, args);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+		break;
+	case CIL_AVRULE:
+	case CIL_AVRULEX:
+		rc = cil_attrib_avrule(node, args);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+		break;
+	case CIL_CONTEXT:
+		/* not currently found in AOSP policy, but if found would need to be
+		   checked to not be attributee */
+		rc = cil_attrib_context(node, args);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+		break;
+	case CIL_GENFSCON:
+		/* not allowed in plat-policy, but types present, throw error if attributee */
+		rc = cil_attrib_genfscon(node, args);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+		break;
+	case CIL_FILECON:
+	case CIL_NODECON:
+	case CIL_PORTCON:
+	case CIL_PIRQCON:
+	case CIL_IOMEMCON:
+	case CIL_IOPORTCON:
+	case CIL_PCIDEVICECON:
+	case CIL_DEVICETREECON:
+	case CIL_VALIDATETRANS:
+	case CIL_MLSVALIDATETRANS:
+	case CIL_CALL:
+	case CIL_MACRO:
+	case CIL_OPTIONAL:
+		/* Not currently found in AOSP and not yet properly handled.  Return err until support added. */
+		cil_log(CIL_ERR, "unsupported policy statement (line %d)\n", node->line);
+		rc = SEPOL_ERR;
+		goto exit;
+	case CIL_FSUSE:
+		/* not allowed in plat-policy, but types present, throw error if attributee */
+		cil_attrib_fsuse(node, args);
+		if (rc != SEPOL_OK) {
+			goto exit;
+		}
+		break;
+	case CIL_CONSTRAIN:
+	case CIL_MLSCONSTRAIN:
+		/* there is type info here, but not sure if we'll allow non-platform code
+		   to have this, or whether or not it's in platform policy.  Currently
+		   assuming that mlsconstrain is private-platform only, and that normal
+		   constrain is verboten. */
+		cil_log(CIL_ERR, "unsupported policy statement (line %d)\n", node->line);
+		rc = SEPOL_ERR;
+		goto exit;
+	default:
+		break;
+	}
+
+	return SEPOL_OK;
+exit:
+	return rc;
+}
+
+/*
+ * walk ast, replacing previously identified types and attributes with the
+ * attributized version. Also replace previous references to the attributees
+ * with the versioned type.
+ */
+static int cil_attributize(struct cil_db *db, hashtab_t vers_map, const char *num)
+{
+	int rc = SEPOL_ERR;
+	struct version_args extra_args;
+	extra_args.db = db;
+	extra_args.vers_map = vers_map;
+	extra_args.num = num;
+
+	rc = cil_tree_walk(db->ast->root, __attributize_helper, NULL, NULL, &extra_args);
+	if (rc != SEPOL_OK) {
+		goto exit;
+	}
+
+	return SEPOL_OK;
+exit:
+	return rc;
+}
+
+/*
+ * Create typeattributeset mappings from the attributes generated from the
+ * original types/attributes to the original values.  This mapping will provide
+ * the basis for the platform policy's mapping to this public version.
+ *
+ * Add these new typeattributeset nodes to the given cil_db.
+ */
+static int cil_build_mappings_tree(hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	struct cil_typeattributeset *attrset = NULL;
+	struct cil_typeattribute *typeattr = NULL;
+	struct cil_expandtypeattribute *expandattr = NULL;
+	struct cil_tree_node *ast_node = NULL;
+	struct version_args *verargs = (struct version_args *)args;
+	struct cil_tree_node *ast_parent = verargs->db->ast->root;
+	char *orig_type = (char *) k;
+	struct version_datum *vers_datum = (struct version_datum *) d;
+	char *new_key = __cil_attrib_get_versname(orig_type, verargs->num);
+
+	if (vers_datum->ast_node->flavor == CIL_TYPEATTRIBUTE) {
+		// platform attributes are not versioned
+		return SEPOL_OK;
+	}
+	/* create typeattributeset datum */
+	cil_typeattributeset_init(&attrset);
+	cil_list_init(&attrset->str_expr, CIL_TYPE);
+	attrset->attr_str = new_key;
+	cil_list_append(attrset->str_expr, CIL_STRING, orig_type);
+
+	/* create containing tree node */
+	cil_tree_node_init(&ast_node);
+	ast_node->data = attrset;
+	ast_node->flavor = CIL_TYPEATTRIBUTESET;
+
+	/* add to tree */
+	ast_node->parent = ast_parent;
+	if (ast_parent->cl_head == NULL)
+		ast_parent->cl_head = ast_node;
+	else
+		ast_parent->cl_tail->next = ast_node;
+	ast_parent->cl_tail = ast_node;
+
+	/* create expandtypeattribute datum */
+	cil_expandtypeattribute_init(&expandattr);
+	cil_list_init(&expandattr->attr_strs, CIL_TYPE);
+	cil_list_append(expandattr->attr_strs, CIL_STRING, new_key);
+	expandattr->expand = CIL_TRUE;
+
+	/* create containing tree node */
+	cil_tree_node_init(&ast_node);
+	ast_node->data = expandattr;
+	ast_node->flavor = CIL_EXPANDTYPEATTRIBUTE;
+	/* add to tree */
+	ast_node->parent = ast_parent;
+	ast_parent->cl_tail->next = ast_node;
+	ast_parent->cl_tail = ast_node;
+
+	/* re)declare typeattribute. */
+	cil_typeattribute_init(&typeattr);
+	typeattr->datum.name = new_key;
+	cil_tree_node_init(&ast_node);
+	ast_node->data = typeattr;
+	ast_node->flavor = CIL_TYPEATTRIBUTE;
+	ast_node->parent = ast_parent;
+	ast_parent->cl_tail->next = ast_node;
+	ast_parent->cl_tail = ast_node;
+
+	return SEPOL_OK;
+}
+
+/*
+ * Initializes the given db and uses the version mapping generated by
+ * cil_extract_attributees() to fill it with the glue policy required to
+ * connect the attributized policy created by cil_attributize() to the policy
+ * declaring the concrete types.
+ */
+static int cil_attrib_mapping(struct cil_db **db, hashtab_t vers_map, const char *num)
+{
+	int rc = SEPOL_ERR;
+	struct version_args extra_args;
+
+	cil_db_init(db);
+
+	/* foreach entry in vers_map, create typeattributeset node and attach to tree */
+	extra_args.db = *db;
+	extra_args.vers_map = NULL;
+	extra_args.num = num;
+	rc = hashtab_map(vers_map, cil_build_mappings_tree, &extra_args);
+	if (rc != SEPOL_OK) {
+		goto exit;
+	}
+
+	return SEPOL_OK;
+exit:
+	return rc;
+}
+
+int cil_android_attrib_mapping(struct cil_db **mdb, struct cil_db *srcdb, const char *num)
+{
+	int rc = SEPOL_ERR;
+	hashtab_t ver_map_tab = NULL;
+
+	ver_map_tab = hashtab_create(ver_map_hash_val, ver_map_key_cmp, VER_MAP_SZ);
+	if (!ver_map_tab) {
+		cil_log(CIL_ERR, "Unable to create version mapping table.\n");
+		goto exit;
+	}
+	rc = cil_build_ast(srcdb, srcdb->parse->root, srcdb->ast->root);
+	if (rc != SEPOL_OK) {
+		cil_log(CIL_ERR, "Unable to build source db AST.\n");
+		goto exit;
+	}
+	rc = cil_extract_attributees(srcdb, ver_map_tab);
+	if (rc != SEPOL_OK) {
+		cil_log(CIL_ERR, "Unable to extract attributizable elements from source db.\n");
+		goto exit;
+	}
+	rc = cil_attrib_mapping(mdb, ver_map_tab, num);
+	if (rc != SEPOL_OK) {
+		cil_log(CIL_ERR, "Unable to create mapping db from source db.\n");
+		goto exit;
+	}
+exit:
+	ver_map_destroy(ver_map_tab);
+	return rc;
+}
+
+int cil_android_attributize(struct cil_db *tgtdb, struct cil_db *srcdb, const char *num)
+{
+	int rc = SEPOL_ERR;
+	hashtab_t ver_map_tab = NULL;
+
+	ver_map_tab = hashtab_create(ver_map_hash_val, ver_map_key_cmp, VER_MAP_SZ);
+	if (!ver_map_tab) {
+		cil_log(CIL_ERR, "Unable to create version mapping table.\n");
+		goto exit;
+	}
+	rc = cil_build_ast(srcdb, srcdb->parse->root, srcdb->ast->root);
+	if (rc != SEPOL_OK) {
+		cil_log(CIL_ERR, "Unable to build source db AST.\n");
+		goto exit;
+	}
+	rc = cil_extract_attributees(srcdb, ver_map_tab);
+	if (rc != SEPOL_OK) {
+		cil_log(CIL_ERR, "Unable to extract attributizable elements from source db.\n");
+		goto exit;
+	}
+	rc = cil_build_ast(tgtdb, tgtdb->parse->root, tgtdb->ast->root);
+	if (rc != SEPOL_OK) {
+		cil_log(CIL_ERR, "Unable to build target db AST.\n");
+		goto exit;
+	}
+	rc = cil_attributize(tgtdb, ver_map_tab, num);
+	if (rc != SEPOL_OK) {
+		cil_log(CIL_ERR, "Unable to attributize target db.\n");
+		goto exit;
+	}
+exit:
+	ver_map_destroy(ver_map_tab);
+	return rc;
+}
diff --git a/libsepol/cil/src/cil_write_ast.c b/libsepol/cil/src/cil_write_ast.c
new file mode 100644
index 0000000..3b252db
--- /dev/null
+++ b/libsepol/cil/src/cil_write_ast.c
@@ -0,0 +1,1513 @@
+#include <stdlib.h>
+
+#include "cil_flavor.h"
+#include "cil_internal.h"
+#include "cil_log.h"
+#include "cil_tree.h"
+
+struct cil_args_write {
+	FILE *cil_out;
+	struct cil_db *db;
+};
+
+static int cil_unfill_expr(struct cil_list *expr_str, char **out_str, int paren);
+static int cil_unfill_classperms_list(struct cil_list *classperms, char **out_str, int paren);
+static int __cil_write_first_child_helper(struct cil_tree_node *node, void *extra_args);
+static int __cil_write_node_helper(struct cil_tree_node *node, uint32_t *finished, void *extra_args);
+static int __cil_write_last_child_helper(struct cil_tree_node *node, void *extra_args);
+
+static int __cil_strlist_concat(struct cil_list *str_list, char **out_str, int paren) {
+	size_t len = paren ? 3 : 1;
+	size_t num_elems = 0;
+	char *p = NULL;
+	struct cil_list_item *curr;
+
+	/* get buffer size */
+	cil_list_for_each(curr, str_list) {
+		len += strlen((char *)curr->data);
+		num_elems++;
+	}
+	if (num_elems != 0) {
+		/* add spaces between elements */
+		len += num_elems - 1;
+	}
+	*out_str = cil_malloc(len);
+	p = *out_str;
+	if (paren)
+		*p++ = '(';
+	cil_list_for_each(curr, str_list) {
+		size_t src_len = strlen((char *)curr->data);
+		memcpy(p, curr->data, src_len);
+		p += src_len;
+		if (curr->next != NULL)
+			*p++ = ' ';
+	}
+	if (paren)
+		*p++ = ')';
+	*p++ = '\0';
+	return SEPOL_OK;
+}
+
+static int __cil_unfill_expr_helper(struct cil_list_item *curr,
+			     struct cil_list_item **next, char **out_str, int paren) {
+	int rc = SEPOL_ERR;
+	char *str = NULL;
+	char *operand1 = NULL;
+	char *operand2 = NULL;
+
+	switch(curr->flavor) {
+	case CIL_LIST:
+		rc = cil_unfill_expr((struct cil_list *)curr->data, &str, paren);
+		if (rc != SEPOL_OK)
+			goto exit;
+		*out_str = str;
+		*next = curr->next;
+		break;
+	case CIL_STRING:
+		str = strdup((char *)curr->data);
+		if (!str) {
+			cil_log(CIL_ERR, "OOM. Unable to copy string.\n");
+            rc = SEPOL_ERR;
+			goto exit;
+		}
+		*out_str = str;
+		*next = curr->next;
+		break;
+	case CIL_DATUM:
+		str = strdup(((struct cil_symtab_datum *)curr->data)->name);
+		if (!str) {
+			cil_log(CIL_ERR, "OOM. Unable to copy string.\n");
+            rc = SEPOL_ERR;
+			goto exit;
+		}
+		*out_str = str;
+		*next = curr->next;
+		break;
+	case CIL_OP: {
+		char *op_str = NULL;
+		size_t len = 0;
+		enum cil_flavor op_flavor = (enum cil_flavor)curr->data;
+		switch (op_flavor) {
+		case CIL_AND:
+			op_str = CIL_KEY_AND;
+			break;
+		case CIL_OR:
+			op_str = CIL_KEY_OR;
+			break;
+		case CIL_NOT:
+			op_str = CIL_KEY_NOT;
+			break;
+		case CIL_ALL:
+			op_str = CIL_KEY_ALL;
+			break;
+		case CIL_EQ:
+			op_str = CIL_KEY_EQ;
+			break;
+		case CIL_NEQ:
+			op_str = CIL_KEY_NEQ;
+			break;
+		case CIL_RANGE:
+			op_str = CIL_KEY_RANGE;
+			break;
+		case CIL_XOR:
+			op_str = CIL_KEY_XOR;
+			break;
+		case CIL_CONS_DOM:
+			op_str = CIL_KEY_CONS_DOM;
+			break;
+		case CIL_CONS_DOMBY:
+			op_str = CIL_KEY_CONS_DOMBY;
+			break;
+		case CIL_CONS_INCOMP:
+			op_str = CIL_KEY_CONS_INCOMP;
+			break;
+		default:
+			cil_log(CIL_ERR, "Unknown operator in expression: %d\n", op_flavor);
+			goto exit;
+			break;
+		}
+		/* all operands take two args except for 'all' and 'not', which take
+		 * one and two, respectively */
+		len = strlen(op_str) + 3;
+		if (op_flavor == CIL_ALL) {
+			*out_str = cil_malloc(len);
+			sprintf(*out_str, "(%s)", op_str);
+			*next = curr->next;
+		} else if (op_flavor == CIL_NOT) {
+			rc = __cil_unfill_expr_helper(curr->next, next, &operand1, paren);
+			if (rc != SEPOL_OK)
+				goto exit;
+			len += strlen(operand1) + 1;
+			*out_str = cil_malloc(len);
+			sprintf(*out_str, "(%s %s)", op_str, operand1);
+			// *next already set by recursive call
+		} else {
+			rc = __cil_unfill_expr_helper(curr->next, next, &operand1, paren);
+			if (rc != SEPOL_OK)
+				goto exit;
+			len += strlen(operand1) + 1;
+			// *next contains operand2, but keep track of next after that
+			rc = __cil_unfill_expr_helper(*next, next, &operand2, paren);
+			if (rc != SEPOL_OK)
+				goto exit;
+			len += strlen(operand2) + 1;
+			*out_str = cil_malloc(len);
+			sprintf(*out_str, "(%s %s %s)", op_str, operand1, operand2);
+			// *next already set by recursive call
+		}
+	}
+		break;
+	case CIL_CONS_OPERAND: {
+		enum cil_flavor operand_flavor = (enum cil_flavor)curr->data;
+		char *operand_str = NULL;
+		switch (operand_flavor) {
+		case CIL_CONS_U1:
+			operand_str = CIL_KEY_CONS_U1;
+			break;
+		case CIL_CONS_U2:
+			operand_str = CIL_KEY_CONS_U2;
+			break;
+		case CIL_CONS_U3:
+			operand_str = CIL_KEY_CONS_U3;
+			break;
+		case CIL_CONS_T1:
+			operand_str = CIL_KEY_CONS_T1;
+			break;
+		case CIL_CONS_T2:
+			operand_str = CIL_KEY_CONS_T2;
+			break;
+		case CIL_CONS_T3:
+			operand_str = CIL_KEY_CONS_T3;
+			break;
+		case CIL_CONS_R1:
+			operand_str = CIL_KEY_CONS_R1;
+			break;
+		case CIL_CONS_R2:
+			operand_str = CIL_KEY_CONS_R2;
+			break;
+		case CIL_CONS_R3:
+			operand_str = CIL_KEY_CONS_R3;
+			break;
+		case CIL_CONS_L1:
+			operand_str = CIL_KEY_CONS_L1;
+			break;
+		case CIL_CONS_L2:
+			operand_str = CIL_KEY_CONS_L2;
+			break;
+		case CIL_CONS_H1:
+			operand_str = CIL_KEY_CONS_H1;
+			break;
+		case CIL_CONS_H2:
+			operand_str = CIL_KEY_CONS_H2;
+			break;
+		default:
+			cil_log(CIL_ERR, "Unknown operand in expression\n");
+			goto exit;
+			break;
+		}
+		str = strdup(operand_str);
+		if (!str) {
+			cil_log(CIL_ERR, "OOM. Unable to copy string.\n");
+            rc = SEPOL_ERR;
+			goto exit;
+		}
+		*out_str = str;
+		*next = curr->next;
+	}
+		break;
+	default:
+		cil_log(CIL_ERR, "Unknown flavor in expression\n");
+		goto exit;
+		break;
+	}
+	rc = SEPOL_OK;
+exit:
+	free(operand1);
+	free(operand2);
+	return rc;
+}
+
+static int cil_unfill_expr(struct cil_list *expr_str, char **out_str, int paren) {
+	int rc = SEPOL_ERR;
+
+	/* reuse cil_list to keep track of strings */
+	struct cil_list *str_list = NULL;
+	struct cil_list_item *curr = NULL;
+
+	cil_list_init(&str_list, CIL_NONE);
+
+	/* iterate through cil_list, grabbing elements as needed */
+	curr = expr_str->head;
+	while(curr != NULL) {
+		char *str = NULL;
+		struct cil_list_item *next = NULL;
+
+		rc = __cil_unfill_expr_helper(curr, &next, &str, paren);
+        if (rc != SEPOL_OK)
+            goto exit;
+		cil_list_append(str_list, CIL_STRING, (void *) str);
+		str = NULL;
+		curr = next;
+	}
+	rc = __cil_strlist_concat(str_list, out_str, paren);
+	if (rc != SEPOL_OK)
+		goto exit;
+	rc = SEPOL_OK;
+exit:
+	cil_list_for_each(curr, str_list) {
+		free(curr->data);
+	}
+	cil_list_destroy(&str_list, 0);
+	return rc;
+}
+
+static int cil_unfill_cats(struct cil_cats *cats, char **out_str) {
+	return cil_unfill_expr(cats->str_expr, out_str, 0);
+}
+
+static int cil_unfill_level(struct cil_level *lvl, char **out_str) {
+	int rc = SEPOL_ERR;
+	size_t len = 0;
+	char *sens, *cats = NULL;
+	sens = lvl->sens_str;
+	len = strlen(sens) + 3; // '()\0'
+	if (lvl->cats != NULL) {
+		rc = cil_unfill_cats(lvl->cats, &cats);
+		if (rc != SEPOL_OK)
+			goto exit;
+		len += strlen(cats) + 1;
+	}
+	*out_str = cil_malloc(len);
+	if (cats == NULL) {
+		if (sprintf(*out_str, "(%s)", sens) < 0) {
+			cil_log(CIL_ERR, "Error unpacking and writing level\n");
+			rc = SEPOL_ERR;
+			goto exit;
+		}
+	} else {
+		if (sprintf(*out_str, "(%s %s)", sens, cats) < 0) {
+			cil_log(CIL_ERR, "Error unpacking and writing level\n");
+			rc = SEPOL_ERR;
+			goto exit;
+		}
+	}
+	rc = SEPOL_OK;
+exit:
+	free(cats);
+	return rc;
+}
+
+static int cil_unfill_levelrange(struct cil_levelrange *lvlrnge, char **out_str) {
+	int rc = SEPOL_ERR;
+	size_t len = 0;
+	char *low = NULL, *high = NULL;
+	if (lvlrnge->low_str != NULL) {
+		low = strdup(lvlrnge->low_str);
+		if (low == NULL) {
+			cil_log(CIL_ERR, "OOM. Unable to copy level string.\n");
+            rc = SEPOL_ERR;
+			goto exit;
+		}
+	} else {
+		rc = cil_unfill_level(lvlrnge->low, &low);
+		if (rc != SEPOL_OK)
+			goto exit;
+	}
+	if (lvlrnge->high_str != NULL) {
+		high = strdup(lvlrnge->high_str);
+		if (high == NULL) {
+			cil_log(CIL_ERR, "OOM. Unable to copy level string.\n");
+            rc = SEPOL_ERR;
+			goto exit;
+		}
+	} else {
+		rc = cil_unfill_level(lvlrnge->high, &high);
+		if (rc != SEPOL_OK)
+			goto exit;
+	}
+	len = strlen(low) + strlen(high) + 4;
+	*out_str = cil_malloc(len);
+	if (sprintf(*out_str, "(%s %s)", low, high) < 0) {
+		cil_log(CIL_ERR, "Error unpacking and writing levelrange\n");
+		rc = SEPOL_ERR;
+		goto exit;
+	}
+	rc = SEPOL_OK;
+exit:
+	free(low);
+	free(high);
+	return rc;
+}
+
+static int cil_unfill_context(struct cil_context *context, char **out_str) {
+	int rc = SEPOL_ERR;
+	size_t len = 0;
+	char *user_str, *role_str, *type_str;
+	char *range_str = NULL;
+
+	user_str = context->user_str;
+	role_str = context->role_str;
+	type_str = context->type_str;
+	if (context->range_str != NULL) {
+		range_str = strdup(context->range_str);
+		if (range_str == NULL) {
+			cil_log(CIL_ERR, "OOM. Unable to copy range string.\n");
+            rc = SEPOL_ERR;
+			goto exit;
+		}
+	} else {
+		rc = cil_unfill_levelrange(context->range, &range_str);
+		if (rc != SEPOL_OK)
+			goto exit;
+	}
+	len = strlen(user_str) + strlen(role_str) + strlen(type_str)
+		+ strlen(range_str) + 6;
+	*out_str = cil_malloc(len);
+	if (sprintf(*out_str, "(%s %s %s %s)", user_str, role_str, type_str, range_str) < 0) {
+		cil_log(CIL_ERR, "Error unpacking and writing context\n");
+		rc = SEPOL_ERR;
+		goto exit;
+	}
+	rc = SEPOL_OK;
+exit:
+	free(range_str);
+	return rc;
+}
+
+static int cil_unfill_permx(struct cil_permissionx *permx, char **out_str) {
+	size_t len = 3;
+	int rc = SEPOL_ERR;
+	char *kind, *obj;
+	char *expr = NULL;
+
+	switch (permx->kind) {
+	case CIL_PERMX_KIND_IOCTL:
+		kind = CIL_KEY_IOCTL;
+		break;
+	default:
+		cil_log(CIL_ERR, "Unknown permissionx kind: %d\n", permx->kind);
+		rc = SEPOL_ERR;
+		goto exit;
+		break;
+	}
+	obj = permx->obj_str;
+	rc = cil_unfill_expr(permx->expr_str, &expr, 1);
+	if (rc != SEPOL_OK)
+		goto exit;
+	len += strlen(kind) + strlen(obj) + strlen(expr) + 2;
+	*out_str = cil_malloc(len);
+	if (sprintf(*out_str, "(%s %s %s)", kind, obj, expr) < 0) {
+		cil_log(CIL_ERR, "Error writing xperm\n");
+		rc = SEPOL_ERR;
+		goto exit;
+	}
+	rc = SEPOL_OK;
+exit:
+	free(expr);
+	return rc;
+}
+
+#define cil_write_unsupported(flavor) _cil_write_unsupported(flavor, __LINE__)
+static int _cil_write_unsupported(const char *flavor, int line) {
+	cil_log(CIL_ERR,
+			"flavor \"%s\" is not supported, look in file \"%s\""
+			" on line %d to add support.\n", flavor, __FILE__, line);
+	return SEPOL_ENOTSUP;
+}
+
+static int cil_write_policycap(struct cil_tree_node *node, FILE *cil_out) {
+	struct cil_policycap *polcap = (struct cil_policycap *)node->data;
+	fprintf(cil_out, "(%s %s)\n", CIL_KEY_POLICYCAP, polcap->datum.name);
+	return SEPOL_OK;
+}
+
+static int cil_write_perm(struct cil_tree_node *node, FILE *cil_out) {
+	struct cil_perm *perm = (struct cil_perm *)node->data;
+	fprintf(cil_out, "%s", perm->datum.name);
+	if (node->next != NULL)
+		fprintf(cil_out, " ");
+	return SEPOL_OK;
+}
+
+
+static int cil_write_class(struct cil_tree_node *node, uint32_t *finished,
+		     struct cil_args_write *extra_args) {
+	int rc = SEPOL_ERR;
+	FILE *cil_out = extra_args->cil_out;
+	struct cil_symtab_datum *datum = (struct cil_symtab_datum *)node->data;
+	char *class_type = (node->flavor == CIL_CLASS) ? CIL_KEY_CLASS : CIL_KEY_COMMON;
+
+	/* print preamble */
+	fprintf(cil_out, "(%s %s ", class_type, datum->name);
+
+	if (node->cl_head == NULL) {
+		/* no associated perms in this part of tree */
+		fprintf(cil_out, "()");
+	} else {
+
+		/* visit subtree (perms) */
+		rc = cil_tree_walk(node, __cil_write_node_helper,
+				   __cil_write_first_child_helper,
+				   __cil_write_last_child_helper,
+				   extra_args);
+		if (rc != SEPOL_OK)
+			goto exit;
+	}
+
+	/* postamble (trailing paren) */
+	fprintf(cil_out, ")\n");
+	*finished = CIL_TREE_SKIP_HEAD;
+	rc = SEPOL_OK;
+exit:
+	return rc;
+}
+
+static int cil_write_classorder(struct cil_tree_node *node, FILE *cil_out) {
+	int rc = SEPOL_ERR;
+	char *ord_str = NULL;
+	struct cil_classorder *classord = (struct cil_classorder *)node->data;
+
+	/* cil_unfill_expr() has logic to stringify a cil_list, reuse that. */
+	rc = cil_unfill_expr(classord->class_list_str, &ord_str, 1);
+	if (rc != SEPOL_OK)
+		goto exit;
+	fprintf(cil_out, "(%s %s)\n", CIL_KEY_CLASSORDER, ord_str);
+	rc = SEPOL_OK;
+exit:
+	free(ord_str);
+	return rc;
+}
+
+static int cil_write_classcommon(struct cil_tree_node *node, FILE *cil_out) {
+	struct cil_classcommon *classcommon = (struct cil_classcommon *)node->data;
+	fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_CLASSCOMMON, classcommon->class_str,
+		classcommon->common_str);
+	return SEPOL_OK;
+}
+
+static int cil_write_sid(struct cil_tree_node *node, FILE *cil_out) {
+	struct cil_sid *sid = (struct cil_sid *)node->data;
+	fprintf(cil_out, "(%s %s)\n", CIL_KEY_SID, sid->datum.name);
+	return SEPOL_OK;
+}
+
+static int cil_write_sidcontext(struct cil_tree_node *node, FILE *cil_out) {
+	int rc = SEPOL_ERR;
+	char *sid;
+	char *ctx_str = NULL;
+	struct cil_sidcontext *sidcon = (struct cil_sidcontext *)node->data;
+
+	sid = sidcon->sid_str;
+	if (sidcon->context_str != NULL) {
+		ctx_str = strdup(sidcon->context_str);
+		if (ctx_str == NULL) {
+			cil_log(CIL_ERR, "OOM. Unable to copy context string.\n");
+            rc = SEPOL_ERR;
+			goto exit;
+		}
+	} else {
+		rc = cil_unfill_context(sidcon->context, &ctx_str);
+		if (rc != SEPOL_OK)
+			goto exit;
+	}
+	fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_SIDCONTEXT, sid, ctx_str);
+	rc = SEPOL_OK;
+exit:
+	free(ctx_str);
+	return rc;
+}
+
+static int cil_write_sidorder(struct cil_tree_node *node, FILE *cil_out) {
+	int rc = SEPOL_ERR;
+	char *ord_str = NULL;
+	struct cil_sidorder *sidord = (struct cil_sidorder *)node->data;
+
+	/* cil_unfill_expr() has logic to stringify a cil_list, reuse that. */
+	rc = cil_unfill_expr(sidord->sid_list_str, &ord_str, 1);
+	if (rc != SEPOL_OK)
+		goto exit;
+	fprintf(cil_out, "(%s %s)\n", CIL_KEY_SIDORDER, ord_str);
+	rc = SEPOL_OK;
+exit:
+	free(ord_str);
+	return rc;
+}
+
+static int cil_write_user(struct cil_tree_node *node, FILE *cil_out) {
+	struct cil_user *user = (struct cil_user *)node->data;
+	fprintf(cil_out, "(%s %s)\n", CIL_KEY_USER, user->datum.name);
+	return SEPOL_OK;
+}
+
+static int cil_write_userrole(struct cil_tree_node *node, FILE *cil_out) {
+	struct cil_userrole *userrole = (struct cil_userrole *)node->data;
+	fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_USERROLE, userrole->user_str,
+		userrole->role_str);
+	return SEPOL_OK;
+}
+
+static int cil_write_userlevel(struct cil_tree_node *node, FILE *cil_out) {
+	struct cil_userlevel *usrlvl = (struct cil_userlevel *)node->data;
+	int rc = SEPOL_ERR;
+	char *usr;
+	char *lvl = NULL;
+
+	usr = usrlvl->user_str;
+	if (usrlvl->level_str != NULL) {
+		lvl = strdup(usrlvl->level_str);
+		if (lvl == NULL) {
+			cil_log(CIL_ERR, "OOM. Unable to copy level string.\n");
+            rc = SEPOL_ERR;
+			goto exit;
+		}
+	} else {
+		rc = cil_unfill_level(usrlvl->level, &lvl);
+		if (rc != SEPOL_OK)
+			goto exit;
+	}
+	fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_USERLEVEL, usr, lvl);
+	rc = SEPOL_OK;
+exit:
+	free(lvl);
+	return rc;
+}
+
+static int cil_write_userrange(struct cil_tree_node *node, FILE *cil_out) {
+	struct cil_userrange *usrrng = (struct cil_userrange *)node->data;
+	int rc = SEPOL_ERR;
+	char *usr;
+	char *range = NULL;
+
+	usr = usrrng->user_str;
+	if (usrrng->range_str != NULL) {
+		range = strdup(usrrng->range_str);
+		if (range == NULL) {
+			cil_log(CIL_ERR, "OOM. Unable to copy levelrange string.\n");
+            rc = SEPOL_ERR;
+			goto exit;
+		}
+	} else {
+		rc = cil_unfill_levelrange(usrrng->range, &range);
+		if (rc != SEPOL_OK)
+			goto exit;
+	}
+	fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_USERRANGE, usr, range);
+	rc = SEPOL_OK;
+exit:
+	free(range);
+	return rc;
+}
+
+static int cil_write_role(struct cil_tree_node *node, FILE *cil_out) {
+	struct cil_role *role = (struct cil_role *)node->data;
+	fprintf(cil_out, "(%s %s)\n", CIL_KEY_ROLE, role->datum.name);
+	return SEPOL_OK;
+}
+
+static int cil_write_roletype(struct cil_tree_node *node, FILE *cil_out) {
+	struct cil_roletype *roletype = (struct cil_roletype *)node->data;
+	fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_ROLETYPE, roletype->role_str, roletype->type_str);
+	return SEPOL_OK;
+}
+
+static int cil_write_roleattribute(struct cil_tree_node *node, FILE *cil_out) {
+	struct cil_roleattribute *roleattr = (struct cil_roleattribute *)node->data;
+	fprintf(cil_out, "(%s %s)\n", CIL_KEY_ROLEATTRIBUTE, roleattr->datum.name);
+	return SEPOL_OK;
+}
+
+static int cil_write_type(struct cil_tree_node *node, FILE *cil_out) {
+	struct cil_type *type = (struct cil_type *)node->data;
+	fprintf(cil_out, "(%s %s)\n", CIL_KEY_TYPE, type->datum.name);
+	return SEPOL_OK;
+}
+
+static int cil_write_typepermissive(struct cil_tree_node *node, FILE *cil_out) {
+	struct cil_typepermissive *type = (struct cil_typepermissive *)node->data;
+	fprintf(cil_out, "(%s %s)\n", CIL_KEY_TYPEPERMISSIVE, type->type_str);
+	return SEPOL_OK;
+}
+
+static int cil_write_typeattribute(struct cil_tree_node *node, FILE *cil_out) {
+	struct cil_typeattribute *typeattr = (struct cil_typeattribute *)node->data;
+	fprintf(cil_out, "(%s %s)\n", CIL_KEY_TYPEATTRIBUTE, typeattr->datum.name);
+	return SEPOL_OK;
+}
+
+static int cil_write_typeattributeset(struct cil_tree_node *node, FILE *cil_out) {
+	int rc = SEPOL_ERR;
+	char *typeattr;
+	char *set_str = NULL;
+	struct cil_typeattributeset *typeattrset = (struct cil_typeattributeset *)node->data;
+
+	typeattr = typeattrset->attr_str;
+	rc = cil_unfill_expr(typeattrset->str_expr, &set_str, 1);
+	if (rc != SEPOL_OK)
+		goto exit;
+
+	fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_TYPEATTRIBUTESET, typeattr, set_str);
+	rc = SEPOL_OK;
+exit:
+	free(set_str);
+	return rc;
+}
+
+static int cil_write_expandtypeattribute(struct cil_tree_node *node, FILE *cil_out)
+{
+	int rc = SEPOL_ERR;
+	char *attr_strs = NULL;
+	struct cil_expandtypeattribute *expandattr = (struct cil_expandtypeattribute *)node->data;
+
+	rc = cil_unfill_expr(expandattr->attr_strs, &attr_strs, 1);
+	if (rc != SEPOL_OK)
+		goto exit;
+
+	fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_EXPANDTYPEATTRIBUTE, attr_strs,
+		expandattr->expand ? CIL_KEY_CONDTRUE : CIL_KEY_CONDFALSE);
+	rc = SEPOL_OK;
+exit:
+	free(attr_strs);
+	return rc;
+}
+
+static int cil_write_alias(struct cil_tree_node *node, FILE *cil_out) {
+	int rc = SEPOL_ERR;
+	char *type;
+	struct cil_alias *alias = (struct cil_alias *)node->data;
+
+	switch (node->flavor) {
+	case CIL_TYPEALIAS:
+		type = CIL_KEY_TYPEALIAS;
+		break;
+	case CIL_SENSALIAS:
+		type = CIL_KEY_SENSALIAS;
+		break;
+	case CIL_CATALIAS:
+		type = CIL_KEY_CATALIAS;
+		break;
+	default:
+		cil_log(CIL_ERR, "Unknown alias type: %d\n", node->flavor);
+		rc = SEPOL_ERR;
+		goto exit;
+		break;
+	}
+	fprintf(cil_out, "(%s %s)\n", type, alias->datum.name);
+	rc = SEPOL_OK;
+exit:
+	return rc;
+}
+
+static int cil_write_aliasactual(struct cil_tree_node *node, FILE *cil_out) {
+	int rc = SEPOL_ERR;
+	char *type, *alias, *actual;
+	struct cil_aliasactual *aliasact = (struct cil_aliasactual *)node->data;
+
+	switch (node->flavor) {
+	case CIL_TYPEALIASACTUAL:
+		type = CIL_KEY_TYPEALIASACTUAL;
+		break;
+	case CIL_SENSALIASACTUAL:
+		type = CIL_KEY_SENSALIASACTUAL;
+		break;
+	case CIL_CATALIASACTUAL:
+		type = CIL_KEY_CATALIASACTUAL;
+		break;
+	default:
+		cil_log(CIL_ERR, "Unknown alias type: %d\n", node->flavor);
+		rc = SEPOL_ERR;
+		goto exit;
+		break;
+	}
+	alias = aliasact->alias_str;
+	actual = aliasact->actual_str;
+	fprintf(cil_out, "(%s %s %s)\n", type, alias, actual);
+	rc = SEPOL_OK;
+exit:
+	return rc;
+}
+
+static int cil_write_nametypetransition(struct cil_tree_node *node, FILE *cil_out) {
+	char *src, *tgt, *obj, *res, *name;
+	struct cil_nametypetransition *ntrans = (struct cil_nametypetransition *)node->data;
+
+	src = ntrans->src_str;
+	tgt = ntrans->tgt_str;
+	obj = ntrans->obj_str;
+	res = ntrans->result_str;
+	name = ntrans->name_str;
+	fprintf(cil_out, "(%s %s %s %s \"%s\" %s)\n", CIL_KEY_TYPETRANSITION,
+		src, tgt, obj, name, res);
+	return SEPOL_OK;
+}
+
+static int cil_write_avrule_x(struct cil_avrule *avrule, FILE *cil_out) {
+	int rc = SEPOL_ERR;
+	char *rulekind, *src, *tgt;
+	char *xperms = NULL;
+
+	switch (avrule->rule_kind) {
+	case CIL_AVRULE_ALLOWED:
+		rulekind = CIL_KEY_ALLOWX;
+		break;
+	case CIL_AVRULE_AUDITALLOW:
+		rulekind = CIL_KEY_AUDITALLOWX;
+		break;
+	case CIL_AVRULE_DONTAUDIT:
+		rulekind = CIL_KEY_DONTAUDITX;
+		break;
+	case CIL_AVRULE_NEVERALLOW:
+		rulekind = CIL_KEY_NEVERALLOWX;
+		break;
+	default:
+		cil_log(CIL_ERR, "Unknown AVRULE type: %d\n", avrule->rule_kind);
+		rc = SEPOL_ERR;
+		goto exit;
+		break;
+	}
+	src = avrule->src_str;
+	tgt = avrule->tgt_str;
+
+	if (avrule->perms.x.permx_str != NULL) {
+		xperms = strdup(avrule->perms.x.permx_str);
+		if (xperms == NULL) {
+			cil_log(CIL_ERR, "OOM. Unable to copy xperms string.\n");
+			rc = SEPOL_ERR;
+			goto exit;
+		}
+	} else {
+		rc = cil_unfill_permx(avrule->perms.x.permx, &xperms);
+		if (rc != SEPOL_OK)
+			goto exit;
+	}
+	fprintf(cil_out, "(%s %s %s %s)\n", rulekind, src, tgt, xperms);
+	rc = SEPOL_OK;
+exit:
+	free(xperms);
+	return rc;
+}
+
+static int cil_write_avrule_orig(struct cil_avrule *avrule, FILE *cil_out) {
+	int rc = SEPOL_ERR;
+	char *rulekind, *src, *tgt;
+	char *classperms = NULL;
+
+	switch (avrule->rule_kind) {
+	case CIL_AVRULE_ALLOWED:
+		rulekind = CIL_KEY_ALLOW;
+		break;
+	case CIL_AVRULE_AUDITALLOW:
+		rulekind = CIL_KEY_AUDITALLOW;
+		break;
+	case CIL_AVRULE_DONTAUDIT:
+		rulekind = CIL_KEY_DONTAUDIT;
+		break;
+	case CIL_AVRULE_NEVERALLOW:
+		rulekind = CIL_KEY_NEVERALLOW;
+		break;
+	default:
+		cil_log(CIL_ERR, "Unknown AVRULE type: %d\n", avrule->rule_kind);
+		rc = SEPOL_ERR;
+		goto exit;
+		break;
+	}
+	src = avrule->src_str;
+	tgt = avrule->tgt_str;
+
+	rc = cil_unfill_classperms_list(avrule->perms.classperms, &classperms, 0);
+	if (rc != SEPOL_OK)
+		goto exit;
+	fprintf(cil_out, "(%s %s %s %s)\n", rulekind, src, tgt, classperms);
+	rc = SEPOL_OK;
+exit:
+	free(classperms);
+	return rc;
+}
+
+static int cil_write_avrule(struct cil_tree_node *node, FILE *cil_out) {
+	int rc = SEPOL_ERR;
+	struct cil_avrule *avrule = (struct cil_avrule *)node->data;
+
+	if (avrule->is_extended)
+		rc = cil_write_avrule_x(avrule, cil_out);
+	else
+		rc = cil_write_avrule_orig(avrule, cil_out);
+	return rc;
+}
+
+static int cil_write_type_rule(struct cil_tree_node *node, FILE *cil_out) {
+	int rc = SEPOL_ERR;
+	char *type, *src, *tgt, *obj, *res;
+	struct cil_type_rule *typerule = (struct cil_type_rule *)node->data;
+
+	switch (typerule->rule_kind) {
+	case CIL_TYPE_TRANSITION:
+		type = CIL_KEY_TYPETRANSITION;
+		break;
+	case CIL_TYPE_MEMBER:
+		type = CIL_KEY_TYPEMEMBER;
+		break;
+	case CIL_TYPE_CHANGE:
+		type = CIL_KEY_TYPECHANGE;
+		break;
+	default:
+		cil_log(CIL_ERR, "Unknown TYPERULE type: %d\n", typerule->rule_kind);
+		rc = SEPOL_ERR;
+		goto exit;
+		break;
+	}
+	src = typerule->src_str;
+	tgt = typerule->tgt_str;
+	obj = typerule->obj_str;
+	res = typerule->result_str;
+	fprintf(cil_out, "(%s %s %s %s %s)\n", type, src, tgt, obj, res);
+	rc = SEPOL_OK;
+exit:
+	return rc;
+}
+
+static int cil_write_sens(struct cil_tree_node *node, FILE *cil_out) {
+	struct cil_sens *sens = (struct cil_sens *)node->data;
+	fprintf(cil_out, "(%s %s)\n", CIL_KEY_SENSITIVITY, sens->datum.name);
+	return SEPOL_OK;
+}
+
+static int cil_write_cat(struct cil_tree_node *node, FILE *cil_out) {
+	struct cil_cat *cat = (struct cil_cat *)node->data;
+	fprintf(cil_out, "(%s %s)\n", CIL_KEY_CATEGORY, cat->datum.name);
+	return SEPOL_OK;
+}
+
+static int cil_write_senscat(struct cil_tree_node *node, FILE *cil_out) {
+	int rc = SEPOL_ERR;
+	char *sens;
+	char *cats = NULL;
+	struct cil_senscat *senscat = (struct cil_senscat *)node->data;
+
+	sens = senscat->sens_str;
+	rc = cil_unfill_cats(senscat->cats, &cats);
+	if (rc != SEPOL_OK)
+		goto exit;
+	/* TODO: deal with extra/missing parens */
+	fprintf(cil_out, "(%s %s (%s))\n", CIL_KEY_SENSCAT, sens, cats);
+	rc = SEPOL_OK;
+exit:
+	free(cats);
+	return rc;
+}
+
+static int cil_write_catorder(struct cil_tree_node *node, FILE *cil_out) {
+	int rc = SEPOL_ERR;
+	char *ord_str = NULL;
+	struct cil_catorder *catord = (struct cil_catorder *)node->data;
+
+	/* cil_unfill_expr() has logic to stringify a cil_list, reuse that. */
+	rc = cil_unfill_expr(catord->cat_list_str, &ord_str, 1);
+	if (rc != SEPOL_OK)
+		goto exit;
+	fprintf(cil_out, "(%s %s)\n", CIL_KEY_CATORDER, ord_str);
+	rc = SEPOL_OK;
+exit:
+	free(ord_str);
+	return rc;
+}
+
+static int cil_write_sensorder(struct cil_tree_node *node, FILE *cil_out) {
+	int rc = SEPOL_ERR;
+	char *ord_str = NULL;
+	struct cil_sensorder *sensord = (struct cil_sensorder *)node->data;
+
+	/* cil_unfill_expr() has logic to stringify a cil_list, reuse that. */
+	rc = cil_unfill_expr(sensord->sens_list_str, &ord_str, 1);
+	if (rc != SEPOL_OK)
+		goto exit;
+	fprintf(cil_out, "(%s %s)\n", CIL_KEY_SENSITIVITYORDER, ord_str);
+	rc = SEPOL_OK;
+exit:
+	free(ord_str);
+	return rc;
+}
+
+static int cil_write_genfscon(struct cil_tree_node *node, FILE *cil_out) {
+	int rc = SEPOL_ERR;
+	char *ctx_str = NULL;
+
+	struct cil_genfscon *genfscon = (struct cil_genfscon *)node->data;
+	if (genfscon->context_str != NULL) {
+		ctx_str = strdup(genfscon->context_str);
+		if (ctx_str == NULL) {
+			cil_log(CIL_ERR, "OOM. Unable to copy context string.\n");
+            rc = SEPOL_ERR;
+			goto exit;
+		}
+	} else {
+		rc = cil_unfill_context(genfscon->context, &ctx_str);
+		if (rc != SEPOL_OK)
+			goto exit;
+	}
+	fprintf(cil_out, "(%s %s %s %s)\n", CIL_KEY_GENFSCON, genfscon->fs_str,
+            genfscon->path_str, ctx_str);
+	rc = SEPOL_OK;
+exit:
+	free(ctx_str);
+	return rc;
+}
+
+static int cil_unfill_classperms(struct cil_list_item *curr, char **out_str) {
+	int rc = SEPOL_ERR;
+	size_t len = 3;
+	char *class_str;
+	char *perms_str = NULL;
+	struct cil_classperms *cp = (struct cil_classperms *)curr->data;
+
+	class_str = cp->class_str;
+	len += strlen(class_str) + 1;
+
+	/* fill_perms just calls gen_expr */
+	rc = cil_unfill_expr(cp->perm_strs, &perms_str, 1);
+	if (rc != SEPOL_OK)
+		goto exit;
+	len += strlen(perms_str);
+	*out_str = cil_malloc(len);
+	sprintf(*out_str, "(%s %s)", class_str, perms_str);
+	rc = SEPOL_OK;
+exit:
+	free(perms_str);
+	return rc;
+}
+
+static int cil_unfill_classperms_list(struct cil_list *classperms, char **out_str, int paren) {
+	int rc = SEPOL_ERR;
+	struct cil_list_item *curr;
+	char *str = NULL;
+
+	/* reuse cil_list to keep track of strings */
+	struct cil_list *str_list = NULL;
+	cil_list_init(&str_list, CIL_NONE);
+	cil_list_for_each(curr, classperms) {
+		switch (curr->flavor) {
+		case CIL_CLASSPERMS_SET:
+			str = strdup(((struct cil_classperms_set *)curr->data)->set_str);
+			if (str == NULL) {
+				cil_log(CIL_ERR, "OOM. Unable to copy classpermset.\n");
+                rc = SEPOL_ERR;
+				goto exit;
+			}
+			break;
+		case CIL_CLASSPERMS:
+			rc = cil_unfill_classperms(curr, &str);
+			if (rc != SEPOL_OK)
+				goto exit;
+			break;
+		default:
+			cil_log(CIL_ERR, "Unrecognized classperms flavor\n.");
+			goto exit;
+		}
+		cil_list_append(str_list, CIL_STRING, (void *) str);
+		str = NULL;
+	}
+	rc = __cil_strlist_concat(str_list, out_str, paren);
+	if (rc != SEPOL_OK)
+		goto exit;
+	rc = SEPOL_OK;
+exit:
+	cil_list_for_each(curr, str_list) {
+		free(curr->data);
+	}
+	cil_list_destroy(&str_list, 0);
+	return rc;
+}
+
+static int cil_write_fsuse(struct cil_tree_node *node, FILE *cil_out) {
+	int rc = SEPOL_ERR;
+	struct cil_fsuse *fsuse = (struct cil_fsuse *)node->data;
+	char *type, *fsname;
+	char *ctx_str = NULL;
+
+	switch(fsuse->type) {
+	case CIL_FSUSE_XATTR:
+		type = CIL_KEY_XATTR;
+		break;
+	case CIL_FSUSE_TASK:
+		type = CIL_KEY_TASK;
+		break;
+	case CIL_FSUSE_TRANS:
+		type = CIL_KEY_TRANS;
+		break;
+	default:
+		cil_log(CIL_ERR, "Unrecognized fsuse type\n");
+		rc = SEPOL_ERR;
+		goto exit;
+		break;
+	}
+
+	fsname = fsuse->fs_str;
+	if (fsuse->context_str != NULL) {
+		ctx_str = strdup(fsuse->context_str);
+		if (ctx_str == NULL) {
+			cil_log(CIL_ERR, "OOM. Unable to copy context string.\n");
+			rc = SEPOL_ERR;
+			goto exit;
+		}
+	} else {
+		rc = cil_unfill_context(fsuse->context, &ctx_str);
+		if (rc != SEPOL_OK)
+			goto exit;
+	}
+	fprintf(cil_out, "(%s %s %s %s)\n", CIL_KEY_FSUSE, type, fsname, ctx_str);
+exit:
+	free(ctx_str);
+	return rc;
+}
+
+static int cil_write_constrain(struct cil_tree_node *node, FILE *cil_out) {
+	int rc = SEPOL_ERR;
+	struct cil_constrain *cons = (struct cil_constrain *)node->data;
+	char *flav;
+	char *classperms = NULL;
+	char *expr = NULL;
+
+	flav = (node->flavor == CIL_CONSTRAIN) ? CIL_KEY_CONSTRAIN : CIL_KEY_MLSCONSTRAIN;
+
+	rc = cil_unfill_classperms_list(cons->classperms, &classperms, 0);
+	if (rc != SEPOL_OK)
+		goto exit;
+
+	rc = cil_unfill_expr(cons->str_expr, &expr, 0);
+	if (rc != SEPOL_OK)
+		goto exit;
+
+	fprintf(cil_out, "(%s %s %s)\n", flav, classperms, expr);
+exit:
+	free(classperms);
+	free(expr);
+	return rc;
+}
+
+static int cil_write_handleunknown(struct cil_tree_node *node, FILE *cil_out) {
+	int rc = SEPOL_OK;
+	struct cil_handleunknown *handunknown = (struct cil_handleunknown *)node->data;
+	char *val = NULL;
+	switch (handunknown->handle_unknown) {
+	case SEPOL_ALLOW_UNKNOWN:
+		val = CIL_KEY_HANDLEUNKNOWN_ALLOW;
+		break;
+	case SEPOL_DENY_UNKNOWN:
+		val = CIL_KEY_HANDLEUNKNOWN_DENY;
+		break;
+	case SEPOL_REJECT_UNKNOWN:
+		val = CIL_KEY_HANDLEUNKNOWN_REJECT;
+		break;
+	default:
+		cil_log(CIL_ERR, "Unknown handleunknown value: %d.\n",
+			handunknown->handle_unknown);
+		rc = SEPOL_ERR;
+		goto exit;
+		break;
+	}
+	fprintf(cil_out, "(%s %s)\n", CIL_KEY_HANDLEUNKNOWN, val);
+exit:
+	return rc;
+}
+
+static int cil_write_mls(struct cil_tree_node *node, FILE *cil_out) {
+	int rc = SEPOL_OK;
+	struct cil_mls *mls = (struct cil_mls *)node->data;
+	char *val = NULL;
+	switch (mls->value) {
+	case CIL_TRUE:
+		val = CIL_KEY_CONDTRUE;
+		break;
+	case CIL_FALSE:
+		val = CIL_KEY_CONDFALSE;
+		break;
+	default:
+		cil_log(CIL_ERR, "Unknown mls value: %d.\n", mls->value);
+		rc = SEPOL_ERR;
+		goto exit;
+		break;
+	}
+	fprintf(cil_out, "(%s %s)\n", CIL_KEY_MLS, val);
+exit:
+	return rc;
+}
+
+static int __cil_write_first_child_helper(struct cil_tree_node *node, void *extra_args)
+{
+	int rc = SEPOL_ERR;
+	struct cil_args_write *args = (struct cil_args_write *) extra_args;
+	FILE *cil_out = NULL;
+
+	if (node == NULL || extra_args == NULL) {
+		goto exit;
+	}
+
+	cil_out = args->cil_out;
+
+	if (node->parent && node->parent->flavor != CIL_ROOT && node->parent->flavor != CIL_SRC_INFO)
+		fprintf(cil_out,"(");
+	rc = SEPOL_OK;
+exit:
+	return rc;
+}
+
+static int __cil_write_node_helper(struct cil_tree_node *node, uint32_t *finished, void *extra_args)
+{
+	int rc = SEPOL_OK;
+	struct cil_db *db = NULL;
+	struct cil_args_write *args = NULL;
+	FILE *cil_out = NULL;
+
+	if (node == NULL || extra_args == NULL) {
+		goto exit;
+	}
+
+	args = extra_args;
+	db = args->db;
+	cil_out = args->cil_out;
+
+	switch (node->flavor) {
+	case CIL_BLOCK:
+		rc = cil_write_unsupported("CIL_BLOCK");
+		break;
+	case CIL_BLOCKABSTRACT:
+		rc = cil_write_unsupported("CIL_BLOCKABSTRACT");
+		break;
+	case CIL_BLOCKINHERIT:
+		rc = cil_write_unsupported("CIL_BLOCKINHERIT");
+		break;
+	case CIL_IN:
+		rc = cil_write_unsupported("CIL_IN");
+		break;
+	case CIL_POLICYCAP:
+		cil_write_policycap(node, cil_out);
+		break;
+	case CIL_PERM:
+		rc = cil_write_perm(node, cil_out);
+		break;
+	case CIL_MAP_PERM:
+		rc = cil_write_unsupported("CIL_MAP_PERM");
+		break;
+	case CIL_CLASSMAPPING:
+		rc = cil_write_unsupported("CIL_CLASSMAPPING");
+		break;
+	case CIL_CLASS:
+		rc = cil_write_class(node, finished, extra_args);
+		break;
+	case CIL_COMMON:
+		rc = cil_write_class(node, finished, extra_args);
+		break;
+	case CIL_MAP_CLASS:
+		rc = cil_write_unsupported("CIL_MAP_CLASS");
+		break;
+	case CIL_CLASSORDER:
+		rc = cil_write_classorder(node, cil_out);
+		break;
+	case CIL_CLASSPERMISSION:
+		rc = cil_write_unsupported("CIL_CLASSPERMISSION");
+		break;
+	case CIL_CLASSPERMISSIONSET:
+		rc = cil_write_unsupported("CIL_CLASSPERMISSIONSET");
+		break;
+	case CIL_CLASSCOMMON:
+		rc = cil_write_classcommon(node, cil_out);
+		break;
+	case CIL_SID:
+		rc = cil_write_sid(node, cil_out);
+		break;
+	case CIL_SIDCONTEXT:
+		rc = cil_write_sidcontext(node, cil_out);
+		break;
+	case CIL_SIDORDER:
+		rc = cil_write_sidorder(node, cil_out);
+		break;
+	case CIL_USER:
+		rc = cil_write_user(node, cil_out);
+		break;
+	case CIL_USERATTRIBUTE:
+		rc = cil_write_unsupported("CIL_USERATTRIBUTE");
+		break;
+	case CIL_USERATTRIBUTESET:
+		rc = cil_write_unsupported("CIL_USERATTRIBUTESET");
+		break;
+	case CIL_USERROLE:
+		rc = cil_write_userrole(node, cil_out);
+		break;
+	case CIL_USERLEVEL:
+		rc = cil_write_userlevel(node, cil_out);
+		break;
+	case CIL_USERRANGE:
+		rc = cil_write_userrange(node, cil_out);
+		break;
+	case CIL_USERBOUNDS:
+		rc = cil_write_unsupported("CIL_USERBOUNDS");
+		break;
+	case CIL_USERPREFIX:
+		rc = cil_write_unsupported("CIL_USERPREFIX");
+		break;
+	case CIL_ROLE:
+		rc = cil_write_role(node, cil_out);
+		break;
+	case CIL_ROLETYPE:
+		rc = cil_write_roletype(node, cil_out);
+		break;
+	case CIL_ROLEBOUNDS:
+		rc = cil_write_unsupported("CIL_ROLEBOUNDS");
+		break;
+	case CIL_ROLEATTRIBUTE:
+		cil_write_roleattribute(node, cil_out);
+		break;
+	case CIL_ROLEATTRIBUTESET:
+		rc = cil_write_unsupported("CIL_ROLEATTRIBUTESET");
+		break;
+	case CIL_ROLEALLOW:
+		rc = cil_write_unsupported("CIL_ROLEALLOW");
+		break;
+	case CIL_TYPE:
+		rc = cil_write_type(node, cil_out);
+		break;
+	case CIL_TYPEBOUNDS:
+		rc = cil_write_unsupported("CIL_TYPEBOUNDS");
+		break;
+	case CIL_TYPEPERMISSIVE:
+		rc = cil_write_typepermissive(node, cil_out);
+		break;
+	case CIL_TYPEATTRIBUTE:
+		rc = cil_write_typeattribute(node, cil_out);
+		break;
+	case CIL_TYPEATTRIBUTESET:
+		rc = cil_write_typeattributeset(node, cil_out);
+		break;
+    case CIL_EXPANDTYPEATTRIBUTE:
+        rc = cil_write_expandtypeattribute(node, cil_out);
+        break;
+	case CIL_TYPEALIAS:
+		rc = cil_write_alias(node, cil_out);
+		break;
+	case CIL_TYPEALIASACTUAL:
+		rc = cil_write_aliasactual(node, cil_out);
+		break;
+	case CIL_ROLETRANSITION:
+		rc = cil_write_unsupported("CIL_ROLETRANSITION");
+		break;
+	case CIL_NAMETYPETRANSITION:
+		rc = cil_write_nametypetransition(node, cil_out);
+		break;
+	case CIL_RANGETRANSITION:
+		rc = cil_write_unsupported("CIL_RANGETRANSITION");
+		break;
+	case CIL_TUNABLE:
+		rc = cil_write_unsupported("CIL_TUNABLE");
+		break;
+	case CIL_BOOL:
+		rc = cil_write_unsupported("CIL_BOOL");
+		break;
+	case CIL_AVRULE:
+	case CIL_AVRULEX:
+		rc = cil_write_avrule(node, cil_out);
+		break;
+	case CIL_PERMISSIONX:
+		rc = cil_write_unsupported("CIL_PERMISSIONX");
+		break;
+	case CIL_TYPE_RULE:
+		cil_write_type_rule(node, cil_out);
+		break;
+	case CIL_SENS:
+		rc = cil_write_sens(node, cil_out);
+		break;
+	case CIL_SENSALIAS:
+		rc = cil_write_alias(node, cil_out);
+		break;
+	case CIL_SENSALIASACTUAL:
+		rc = cil_write_aliasactual(node, cil_out);
+		break;
+	case CIL_CAT:
+		rc = cil_write_cat(node, cil_out);
+		break;
+	case CIL_CATALIAS:
+		rc = cil_write_alias(node, cil_out);
+		break;
+	case CIL_CATALIASACTUAL:
+		rc = cil_write_aliasactual(node, cil_out);
+		break;
+	case CIL_CATSET:
+		rc = cil_write_unsupported("CIL_CATSET");
+		break;
+	case CIL_SENSCAT:
+		rc = cil_write_senscat(node, cil_out);
+		break;
+	case CIL_CATORDER:
+		rc = cil_write_catorder(node, cil_out);
+		break;
+	case CIL_SENSITIVITYORDER:
+		rc = cil_write_sensorder(node, cil_out);
+		break;
+	case CIL_LEVEL:
+		rc = cil_write_unsupported("CIL_LEVEL");
+		break;
+	case CIL_LEVELRANGE:
+		rc = cil_write_unsupported("CIL_LEVELRANGE");
+		break;
+	case CIL_CONTEXT:
+		rc = cil_write_unsupported("CIL_CONTEXT");
+		break;
+	case CIL_NETIFCON:
+		rc = cil_write_unsupported("CIL_NETIFCON");
+		break;
+	case CIL_GENFSCON:
+		 rc = cil_write_genfscon(node, cil_out);
+		break;
+	case CIL_FILECON:
+		rc = cil_write_unsupported("CIL_FILECON");
+		break;
+	case CIL_NODECON:
+		rc = cil_write_unsupported("CIL_NODECON");
+		break;
+	case CIL_PORTCON:
+		rc = cil_write_unsupported("CIL_PORTCON");
+		break;
+	case CIL_PIRQCON:
+		rc = cil_write_unsupported("CIL_PIRQCON");
+		break;
+	case CIL_IOMEMCON:
+		rc = cil_write_unsupported("CIL_IOMEMCON");
+		break;
+	case CIL_IOPORTCON:
+		rc = cil_write_unsupported("CIL_IOPORTCON");
+		break;
+	case CIL_PCIDEVICECON:
+		rc = cil_write_unsupported("CIL_PCIDEVICECON");
+		break;
+	case CIL_DEVICETREECON:
+		rc = cil_write_unsupported("CIL_DEVICETREECON");
+		break;
+	case CIL_FSUSE:
+		rc = cil_write_fsuse(node, cil_out);
+		break;
+	case CIL_CONSTRAIN:
+		rc = cil_write_unsupported("CIL_CONSTRAIN");
+		break;
+	case CIL_MLSCONSTRAIN:
+		rc = cil_write_constrain(node, cil_out);
+		break;
+	case CIL_VALIDATETRANS:
+		rc = cil_write_unsupported("CIL_VALIDATETRANS");
+		break;
+	case CIL_MLSVALIDATETRANS:
+		rc = cil_write_unsupported("CIL_MLSVALIDATETRANS");
+		break;
+	case CIL_CALL:
+		rc = cil_write_unsupported("CIL_CALL");
+		break;
+	case CIL_MACRO:
+		rc = cil_write_unsupported("CIL_MACRO");
+		break;
+	case CIL_NODE:
+		rc = cil_write_unsupported("CIL_NODE");
+		break;
+	case CIL_OPTIONAL:
+		rc = cil_write_unsupported("CIL_OPTIONAL");
+		break;
+	case CIL_IPADDR:
+		rc = cil_write_unsupported("CIL_IPADDR");
+		break;
+	case CIL_CONDBLOCK:
+		rc = cil_write_unsupported("CIL_CONDBLOCK");
+		break;
+	case CIL_BOOLEANIF:
+		rc = cil_write_unsupported("CIL_BOOLEANIF");
+		break;
+	case CIL_TUNABLEIF:
+		rc = cil_write_unsupported("CIL_TUNABLEIF");
+		break;
+	case CIL_DEFAULTUSER:
+		rc = cil_write_unsupported("CIL_DEFAULTUSER");
+		break;
+	case CIL_DEFAULTROLE:
+		rc = cil_write_unsupported("CIL_DEFAULTROLE");
+		break;
+	case CIL_DEFAULTTYPE:
+		rc = cil_write_unsupported("CIL_DEFAULTTYPE");
+		break;
+	case CIL_DEFAULTRANGE:
+		rc = cil_write_unsupported("CIL_DEFAULTRANGE");
+		break;
+	case CIL_SELINUXUSER:
+		rc = cil_write_unsupported("CIL_SELINUXUSER");
+		break;
+	case CIL_SELINUXUSERDEFAULT:
+		rc = cil_write_unsupported("CIL_SELINUXUSERDEFAULT");
+		break;
+	case CIL_HANDLEUNKNOWN:
+		rc = cil_write_handleunknown(node, cil_out);
+		break;
+	case CIL_MLS:
+		rc = cil_write_mls(node, cil_out);
+		break;
+	case CIL_SRC_INFO:
+		break;
+	case CIL_NONE:
+		// TODO: add proper removal support
+		*finished = CIL_TREE_SKIP_HEAD;
+		break;
+	default:
+		cil_log(CIL_ERR, "Unknown AST flavor: %d.\n", node->flavor);
+		rc = SEPOL_ERR;
+		goto exit;
+		break;
+	}
+exit:
+	return rc;
+}
+
+static int __cil_write_last_child_helper(struct cil_tree_node *node, void *extra_args)
+{
+	int rc = SEPOL_ERR;
+	struct cil_db *db = NULL;
+	struct cil_args_write *args = NULL;
+	FILE *cil_out = NULL;
+
+	if (node == NULL || extra_args == NULL) {
+		goto exit;
+	}
+
+	args = extra_args;
+	db = args->db;
+	cil_out = args->cil_out;
+
+	if (node->parent && node->parent->flavor != CIL_ROOT && node->parent->flavor != CIL_SRC_INFO) {
+		fprintf(cil_out,")");
+	}
+	rc = SEPOL_OK;
+exit:
+	return rc;
+}
+
+/* main exported function */
+int cil_write_ast(struct cil_db *db, const char* path) {
+	int rc = SEPOL_ERR;
+	struct cil_args_write extra_args;
+	FILE *cil_out = NULL;
+
+	cil_out = fopen(path, "we");
+	if (cil_out == NULL) {
+		cil_log(CIL_ERR, "Failure opening output file for writing AST\n");
+		rc = SEPOL_ERR;
+		goto exit;
+	}
+
+	extra_args.cil_out = cil_out;
+	extra_args.db = db;
+	rc = cil_tree_walk(db->ast->root, __cil_write_node_helper,
+			   __cil_write_first_child_helper,
+			   __cil_write_last_child_helper,
+			   &extra_args);
+	if (rc != SEPOL_OK) {
+		cil_log(CIL_INFO, "cil_tree_walk failed, rc: %d\n", rc);
+		goto exit;
+	}
+
+exit:
+	fclose(cil_out);
+	cil_out = NULL;
+	return rc;
+}
diff --git a/prebuilts/bin/audit2allow b/prebuilts/bin/audit2allow
new file mode 100755
index 0000000..0d96ca6
--- /dev/null
+++ b/prebuilts/bin/audit2allow
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+if [ -z "${ANDROID_HOST_OUT}" ]; then
+	echo 'ANDROID_HOST_OUT not set. Have you run lunch?'
+	exit 1
+elif [ -z "${ANDROID_BUILD_TOP}" ]; then
+	echo 'ANDROID_BUILD_TOP not set. Have you run lunch?'
+	exit 1
+elif [ ! -f "$ANDROID_HOST_OUT/lib64/libselinux.so" ]; then
+	echo "Cannot find $ANDROID_HOST_OUT/lib64/libselinux.so"
+	echo "Need to build project"
+	exit 1
+fi
+
+unamestr=`uname`
+if [ "$unamestr" = "Linux" -o "$unamestr" = "linux" ]; then
+	export LD_PRELOAD=$ANDROID_HOST_OUT/lib64/libselinux.so
+	export PYTHONPATH=$ANDROID_BUILD_TOP/prebuilts/python/linux-x86/2.7.5/lib/python2.7/site-packages
+	python $ANDROID_BUILD_TOP/external/selinux/python/audit2allow/audit2allow "$@"
+else
+	echo "audit2allow is only supported on linux"
+	exit 1
+fi
diff --git a/prebuilts/bin/audit2why b/prebuilts/bin/audit2why
new file mode 100755
index 0000000..38dd602
--- /dev/null
+++ b/prebuilts/bin/audit2why
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+if [ -z "${ANDROID_HOST_OUT}" ]; then
+	echo 'ANDROID_HOST_OUT not set. Have you run lunch?'
+	exit 1
+elif [ -z "${ANDROID_BUILD_TOP}" ]; then
+	echo 'ANDROID_BUILD_TOP not set. Have you run lunch?'
+	exit 1
+elif [ ! -f "$ANDROID_HOST_OUT/lib64/libselinux.so" ]; then
+	echo "Cannot find $ANDROID_HOST_OUT/lib64/libselinux.so"
+	echo "Need to build project"
+	exit 1
+fi
+
+unamestr=`uname`
+if [ "$unamestr" = "Linux" -o "$unamestr" = "linux" ]; then
+	export LD_PRELOAD=$ANDROID_HOST_OUT/lib64/libselinux.so
+	export PYTHONPATH=$ANDROID_BUILD_TOP/prebuilts/python/linux-x86/2.7.5/lib/python2.7/site-packages
+	exec python $ANDROID_BUILD_TOP/external/selinux/python/audit2allow/audit2why "$@"
+else
+	echo "audit2why is only supported on linux"
+	exit 1
+fi
diff --git a/prebuilts/bin/sediff b/prebuilts/bin/sediff
new file mode 100755
index 0000000..d907226
--- /dev/null
+++ b/prebuilts/bin/sediff
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+if [ -z "${ANDROID_BUILD_TOP}" ]; then
+        echo 'ANDROID_BUILD_TOP not set. Have you run lunch?'
+        exit 1
+fi
+
+unamestr=`uname`
+if [ "$unamestr" = "Linux" -o "$unamestr" = "linux" ]; then
+	export PYTHONPATH=$ANDROID_BUILD_TOP/prebuilts/python/linux-x86/2.7.5/lib/python2.7/site-packages
+	python $ANDROID_BUILD_TOP/external/selinux/prebuilts/bin/sediff.py "$@"
+else
+	echo "sediff is only supported on linux"
+	exit 1
+fi
diff --git a/prebuilts/bin/sediff.py b/prebuilts/bin/sediff.py
new file mode 100755
index 0000000..acb6719
--- /dev/null
+++ b/prebuilts/bin/sediff.py
@@ -0,0 +1,1366 @@
+#!/usr/bin/env python
+# Copyright 2015-2016, Tresys Technology, LLC
+#
+# This file is part of SETools.
+#
+# SETools is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# SETools is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with SETools.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+from __future__ import print_function
+import setools
+import argparse
+import sys
+import logging
+from itertools import chain
+
+parser = argparse.ArgumentParser(
+    description="SELinux policy semantic difference tool.",
+    epilog="If no differences are selected, all differences will be printed.")
+parser.add_argument("POLICY1", help="Path to the first SELinux policy to diff.", nargs=1)
+parser.add_argument("POLICY2", help="Path to the second SELinux policy to diff.", nargs=1)
+parser.add_argument("--version", action="version", version=setools.__version__)
+parser.add_argument("--stats", action="store_true", help="Display only statistics.")
+parser.add_argument("-v", "--verbose", action="store_true",
+                    help="Print extra informational messages")
+parser.add_argument("--debug", action="store_true", dest="debug", help="Enable debugging.")
+
+comp = parser.add_argument_group("component differences")
+comp.add_argument("--common", action="store_true", help="Print common differences")
+comp.add_argument("-c", "--class", action="store_true", help="Print class differences",
+                  dest="class_")
+comp.add_argument("-t", "--type", action="store_true", help="Print type differences",
+                  dest="type_")
+comp.add_argument("-a", "--attribute", action="store_true", help="Print type attribute differences")
+comp.add_argument("-r", "--role", action="store_true", help="Print role differences")
+comp.add_argument("-u", "--user", action="store_true", help="Print user differences")
+comp.add_argument("-b", "--bool", action="store_true", help="Print Boolean differences",
+                  dest="bool_")
+comp.add_argument("--sensitivity", action="store_true", help="Print MLS sensitivity differences")
+comp.add_argument("--category", action="store_true", help="Print MLS category differences")
+comp.add_argument("--level", action="store_true", help="Print MLS level definition differences")
+
+terule = parser.add_argument_group("type enforcement rule differences")
+terule.add_argument("-A", action="store_true", help="Print allow and allowxperm rule differences")
+terule.add_argument("--allow", action="store_true", help="Print allow rule differences")
+terule.add_argument("--neverallow", action="store_true", help="Print neverallow rule differences")
+terule.add_argument("--auditallow", action="store_true", help="Print auditallow rule differences")
+terule.add_argument("--dontaudit", action="store_true", help="Print dontaudit rule differences")
+terule.add_argument("--allowxperm", action="store_true", help="Print allowxperm rule differences")
+terule.add_argument("--neverallowxperm", action="store_true",
+                    help="Print neverallowxperm rule differences")
+terule.add_argument("--auditallowxperm", action="store_true",
+                    help="Print auditallowxperm rule differences")
+terule.add_argument("--dontauditxperm", action="store_true",
+                    help="Print dontauditxperm rule differences")
+terule.add_argument("-T", "--type_trans", action="store_true",
+                    help="Print type_transition rule differences")
+terule.add_argument("--type_change", action="store_true", help="Print type_change rule differences")
+terule.add_argument("--type_member", action="store_true",
+                    help="Print type_member rule differences")
+
+rbacrule = parser.add_argument_group("RBAC rule differences")
+rbacrule.add_argument("--role_allow", action="store_true", help="Print role allow rule differences")
+rbacrule.add_argument("--role_trans", action="store_true",
+                      help="Print role_transition rule differences")
+
+mlsrule = parser.add_argument_group("MLS rule differences")
+mlsrule.add_argument("--range_trans", action="store_true",
+                     help="Print range_transition rule differences")
+
+constrain = parser.add_argument_group("Constraint differences")
+constrain.add_argument("--constrain", action="store_true", help="Print constrain differences")
+constrain.add_argument("--mlsconstrain", action="store_true", help="Print mlsconstrain differences")
+constrain.add_argument("--validatetrans", action="store_true",
+                       help="Print validatetrans differences")
+constrain.add_argument("--mlsvalidatetrans", action="store_true",
+                       help="Print mlsvalidatetrans differences")
+
+labeling = parser.add_argument_group("labeling statement differences")
+labeling.add_argument("--initialsid", action="store_true", help="Print initial SID differences")
+labeling.add_argument("--fs_use", action="store_true", help="Print fs_use_* differences")
+labeling.add_argument("--genfscon", action="store_true", help="Print genfscon differences")
+labeling.add_argument("--netifcon", action="store_true", help="Print netifcon differences")
+labeling.add_argument("--nodecon", action="store_true", help="Print nodecon differences")
+labeling.add_argument("--portcon", action="store_true", help="Print portcon differences")
+
+other = parser.add_argument_group("other differences")
+other.add_argument("--default", action="store_true", help="Print default_* differences")
+other.add_argument("--property", action="store_true",
+                   help="Print policy property differences (handle_unknown, version, MLS)")
+other.add_argument("--polcap", action="store_true", help="Print policy capability differences")
+other.add_argument("--typebounds", action="store_true", help="Print typebounds differences")
+
+args = parser.parse_args()
+
+if args.A:
+    args.allow = True
+    args.allowxperm = True
+
+all_differences = not any((args.class_, args.common, args.type_, args.attribute, args.role,
+                           args.user, args.bool_, args.sensitivity, args.category, args.level,
+                           args.allow, args.neverallow, args.auditallow, args.dontaudit,
+                           args.type_trans, args.type_change, args.type_member, args.role_allow,
+                           args.role_trans, args.range_trans, args.initialsid, args.genfscon,
+                           args.netifcon, args.nodecon, args.portcon, args.fs_use, args.polcap,
+                           args.property, args.default, args.constrain, args.mlsconstrain,
+                           args.validatetrans, args.mlsvalidatetrans, args.typebounds,
+                           args.allowxperm, args.neverallowxperm, args.auditallowxperm,
+                           args.dontauditxperm))
+
+if args.debug:
+    logging.basicConfig(level=logging.DEBUG,
+                        format='%(asctime)s|%(levelname)s|%(name)s|%(message)s')
+elif args.verbose:
+    logging.basicConfig(level=logging.INFO, format='%(message)s')
+else:
+    logging.basicConfig(level=logging.WARNING, format='%(message)s')
+
+try:
+    p1 = setools.SELinuxPolicy(args.POLICY1[0])
+    p2 = setools.SELinuxPolicy(args.POLICY2[0])
+    diff = setools.PolicyDifference(p1, p2)
+
+    if all_differences or args.property:
+        print("Policy Properties ({0} Modified)".format(len(diff.modified_properties)))
+
+        if diff.modified_properties and not args.stats:
+            for name, added, removed in sorted(diff.modified_properties, key=lambda x: x.property):
+                print("      * {0} +{1} -{2}".format(name, added, removed))
+
+        print()
+
+    if all_differences or args.common:
+        if diff.added_commons or diff.removed_commons or diff.modified_commons or args.common:
+            print("Commons ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_commons), len(diff.removed_commons), len(diff.modified_commons)))
+            if diff.added_commons and not args.stats:
+                print("   Added Commons: {0}".format(len(diff.added_commons)))
+                for c in sorted(diff.added_commons):
+                    print("      + {0}".format(c))
+            if diff.removed_commons and not args.stats:
+                print("   Removed Commons: {0}".format(len(diff.removed_commons)))
+                for c in sorted(diff.removed_commons):
+                    print("      - {0}".format(c))
+            if diff.modified_commons and not args.stats:
+                print("   Modified Commons: {0}".format(len(diff.modified_commons)))
+                for name, mod in sorted(diff.modified_commons.items()):
+                    change = []
+                    if mod.added_perms:
+                        change.append("{0} Added permissions".format(len(mod.added_perms)))
+                    if mod.removed_perms:
+                        change.append("{0} Removed permissions".format(len(mod.removed_perms)))
+
+                    print("      * {0} ({1})".format(name, ", ".join(change)))
+                    for p in sorted(mod.added_perms):
+                        print("          + {0}".format(p))
+                    for p in sorted(mod.removed_perms):
+                        print("          - {0}".format(p))
+            print()
+
+    if all_differences or args.class_:
+        if diff.added_classes or diff.removed_classes or diff.modified_classes or args.class_:
+            print("Classes ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_classes), len(diff.removed_classes), len(diff.modified_classes)))
+            if diff.added_classes and not args.stats:
+                print("   Added Classes: {0}".format(len(diff.added_classes)))
+                for c in sorted(diff.added_classes):
+                    print("      + {0}".format(c))
+            if diff.removed_classes and not args.stats:
+                print("   Removed Classes: {0}".format(len(diff.removed_classes)))
+                for c in sorted(diff.removed_classes):
+                    print("      - {0}".format(c))
+            if diff.modified_classes and not args.stats:
+                print("   Modified Classes: {0}".format(len(diff.modified_classes)))
+                for name, mod in sorted(diff.modified_classes.items()):
+                    change = []
+                    if mod.added_perms:
+                        change.append("{0} Added permissions".format(len(mod.added_perms)))
+                    if mod.removed_perms:
+                        change.append("{0} Removed permissions".format(len(mod.removed_perms)))
+
+                    print("      * {0} ({1})".format(name, ", ".join(change)))
+                    for p in sorted(mod.added_perms):
+                        print("          + {0}".format(p))
+                    for p in sorted(mod.removed_perms):
+                        print("          - {0}".format(p))
+            print()
+
+    if all_differences or args.bool_:
+        if diff.added_booleans or diff.removed_booleans or \
+                diff.modified_booleans or args.bool_:
+            print("Booleans ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_booleans), len(diff.removed_booleans),
+                len(diff.modified_booleans)))
+            if diff.added_booleans and not args.stats:
+                print("   Added Booleans: {0}".format(len(diff.added_booleans)))
+                for a in sorted(diff.added_booleans):
+                    print("      + {0}".format(a))
+            if diff.removed_booleans and not args.stats:
+                print("   Removed Booleans: {0}".format(len(diff.removed_booleans)))
+                for a in sorted(diff.removed_booleans):
+                    print("      - {0}".format(a))
+            if diff.modified_booleans and not args.stats:
+                print("   Modified Booleans: {0}".format(len(diff.modified_booleans)))
+                for name, mod in sorted(diff.modified_booleans.items()):
+                    print("      * {0} (Modified default state)".format(name))
+                    print("          + {0}".format(mod.added_state))
+                    print("          - {0}".format(mod.removed_state))
+
+            print()
+
+    if all_differences or args.role:
+        if diff.added_roles or diff.removed_roles or diff.modified_roles or args.role:
+            print("Roles ({0} Added, {1} Removed, {2} Modified)".format(len(diff.added_roles),
+                                                                        len(diff.removed_roles),
+                                                                        len(diff.modified_roles)))
+            if diff.added_roles and not args.stats:
+                print("   Added Roles: {0}".format(len(diff.added_roles)))
+                for r in sorted(diff.added_roles):
+                    print("      + {0}".format(r))
+            if diff.removed_roles and not args.stats:
+                print("   Removed Roles: {0}".format(len(diff.removed_roles)))
+                for r in sorted(diff.removed_roles):
+                    print("      - {0}".format(r))
+            if diff.modified_roles and not args.stats:
+                print("   Modified Roles: {0}".format(len(diff.modified_roles)))
+                for name, mod in sorted(diff.modified_roles.items()):
+                    change = []
+                    if mod.added_types:
+                        change.append("{0} Added types".format(len(mod.added_types)))
+                    if mod.removed_types:
+                        change.append("{0} Removed types".format(len(mod.removed_types)))
+
+                    print("      * {0} ({1})".format(name, ", ".join(change)))
+                    for t in sorted(mod.added_types):
+                        print("          + {0}".format(t))
+                    for t in sorted(mod.removed_types):
+                        print("          - {0}".format(t))
+            print()
+
+    if all_differences or args.type_:
+        if diff.added_types or diff.removed_types or diff.modified_types or args.type_:
+            print("Types ({0} Added, {1} Removed, {2} Modified)".format(len(diff.added_types),
+                                                                        len(diff.removed_types),
+                                                                        len(diff.modified_types)))
+            if diff.added_types and not args.stats:
+                print("   Added Types: {0}".format(len(diff.added_types)))
+                for r in sorted(diff.added_types):
+                    print("      + {0}".format(r))
+            if diff.removed_types and not args.stats:
+                print("   Removed Types: {0}".format(len(diff.removed_types)))
+                for r in sorted(diff.removed_types):
+                    print("      - {0}".format(r))
+            if diff.modified_types and not args.stats:
+                print("   Modified Types: {0}".format(len(diff.modified_types)))
+                for name, mod in sorted(diff.modified_types.items()):
+                    change = []
+                    if mod.added_attributes:
+                        change.append("{0} Added attributes".format(len(mod.added_attributes)))
+                    if mod.removed_attributes:
+                        change.append("{0} Removed attributes".format(len(mod.removed_attributes)))
+                    if mod.added_aliases:
+                        change.append("{0} Added aliases".format(len(mod.added_aliases)))
+                    if mod.removed_aliases:
+                        change.append("{0} Removed aliases".format(len(mod.removed_aliases)))
+                    if mod.modified_permissive:
+                        if mod.permissive:
+                            change.append("Removed permissive")
+                        else:
+                            change.append("Added permissive")
+
+                    print("      * {0} ({1})".format(name, ", ".join(change)))
+                    if mod.added_attributes or mod.removed_attributes:
+                        print("          Attributes:")
+                    for t in sorted(mod.added_attributes):
+                        print("          + {0}".format(t))
+                    for t in sorted(mod.removed_attributes):
+                        print("          - {0}".format(t))
+
+                    if mod.added_aliases or mod.removed_aliases:
+                        print("          Aliases:")
+                    for t in sorted(mod.added_aliases):
+                        print("          + {0}".format(t))
+                    for t in sorted(mod.removed_aliases):
+                        print("          - {0}".format(t))
+
+            print()
+
+    if all_differences or args.attribute:
+        if diff.added_type_attributes or diff.removed_type_attributes or \
+                diff.modified_type_attributes or args.attribute:
+            print("Type Attributes ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_type_attributes), len(diff.removed_type_attributes),
+                len(diff.modified_type_attributes)))
+            if diff.added_type_attributes and not args.stats:
+                print("   Added Type Attributes: {0}".format(len(diff.added_type_attributes)))
+                for a in sorted(diff.added_type_attributes):
+                    print("      + {0}".format(a))
+            if diff.removed_type_attributes and not args.stats:
+                print("   Removed Type Attributes: {0}".format(len(diff.removed_type_attributes)))
+                for a in sorted(diff.removed_type_attributes):
+                    print("      - {0}".format(a))
+            if diff.modified_type_attributes and not args.stats:
+                print("   Modified Type Attributes: {0}".format(len(diff.modified_type_attributes)))
+                for name, mod in sorted(diff.modified_type_attributes.items()):
+                    change = []
+                    if mod.added_types:
+                        change.append("{0} Added types".format(len(mod.added_types)))
+                    if mod.removed_types:
+                        change.append("{0} Removed types".format(len(mod.removed_types)))
+
+                    print("      * {0} ({1})".format(name, ", ".join(change)))
+                    for t in sorted(mod.added_types):
+                        print("          + {0}".format(t))
+                    for t in sorted(mod.removed_types):
+                        print("          - {0}".format(t))
+            print()
+
+    if all_differences or args.user:
+        if diff.added_users or diff.removed_users or diff.modified_users or args.user:
+            print("Users ({0} Added, {1} Removed, {2} Modified)".format(len(diff.added_users),
+                                                                        len(diff.removed_users),
+                                                                        len(diff.modified_users)))
+            if diff.added_users and not args.stats:
+                print("   Added Users: {0}".format(len(diff.added_users)))
+                for u in sorted(diff.added_users):
+                    print("      + {0}".format(u))
+            if diff.removed_users and not args.stats:
+                print("   Removed Users: {0}".format(len(diff.removed_users)))
+                for u in sorted(diff.removed_users):
+                    print("      - {0}".format(u))
+            if diff.modified_users and not args.stats:
+                print("   Modified Users: {0}".format(len(diff.modified_users)))
+                for name, mod in sorted(diff.modified_users.items()):
+                    change = []
+                    if mod.added_roles:
+                        change.append("{0} Added roles".format(len(mod.added_roles)))
+                    if mod.removed_roles:
+                        change.append("{0} Removed roles".format(len(mod.removed_roles)))
+                    if mod.removed_level:
+                        change.append("Modified default level")
+                    if mod.removed_range:
+                        change.append("Modified range")
+
+                    print("      * {0} ({1})".format(name, ", ".join(change)))
+                    if mod.added_roles or mod.removed_roles:
+                        print("          Roles:")
+                    for t in sorted(mod.added_roles):
+                        print("          + {0}".format(t))
+                    for t in sorted(mod.removed_roles):
+                        print("          - {0}".format(t))
+
+                    if mod.removed_level:
+                        print("          Default level:")
+                        print("          + {0}".format(mod.added_level))
+                        print("          - {0}".format(mod.removed_level))
+
+                    if mod.removed_range:
+                        print("          Range:")
+                        print("          + {0}".format(mod.added_range))
+                        print("          - {0}".format(mod.removed_range))
+            print()
+
+    if all_differences or args.category:
+        if diff.added_categories or diff.removed_categories or diff.modified_categories \
+                or args.category:
+            print("Categories ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_categories), len(diff.removed_categories),
+                len(diff.modified_categories)))
+            if diff.added_categories and not args.stats:
+                print("   Added Categories: {0}".format(len(diff.added_categories)))
+                for c in sorted(diff.added_categories):
+                    print("      + {0}".format(c))
+            if diff.removed_categories and not args.stats:
+                print("   Removed Categories: {0}".format(len(diff.removed_categories)))
+                for c in sorted(diff.removed_categories):
+                    print("      - {0}".format(c))
+            if diff.modified_categories and not args.stats:
+                print("   Modified Categories: {0}".format(len(diff.modified_categories)))
+                for name, mod in sorted(diff.modified_categories.items()):
+                    change = []
+                    if mod.added_aliases:
+                        change.append("{0} Added Aliases".format(len(mod.added_aliases)))
+                    if mod.removed_aliases:
+                        change.append("{0} Removed Aliases".format(len(mod.removed_aliases)))
+
+                    print("      * {0} ({1})".format(name, ", ".join(change)))
+                    print("          Aliases:")
+                    for a in sorted(mod.added_aliases):
+                        print("          + {0}".format(a))
+                    for a in sorted(mod.removed_aliases):
+                        print("          - {0}".format(a))
+
+            print()
+
+    if all_differences or args.sensitivity:
+        if diff.added_sensitivities or diff.removed_sensitivities or diff.modified_sensitivities \
+                or args.sensitivity:
+            print("Sensitivities ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_sensitivities), len(diff.removed_sensitivities),
+                len(diff.modified_sensitivities)))
+            if diff.added_sensitivities and not args.stats:
+                print("   Added Sensitivites: {0}".format(len(diff.added_sensitivities)))
+                for s in sorted(diff.added_sensitivities):
+                    print("      + {0}".format(s))
+            if diff.removed_sensitivities and not args.stats:
+                print("   Removed Sensitivities: {0}".format(len(diff.removed_sensitivities)))
+                for s in sorted(diff.removed_sensitivities):
+                    print("      - {0}".format(s))
+            if diff.modified_sensitivities and not args.stats:
+                print("   Modified Sensitivities: {0}".format(len(diff.modified_sensitivities)))
+                for name, mod in sorted(diff.modified_sensitivities.items()):
+                    change = []
+                    if mod.added_aliases:
+                        change.append("{0} Added Aliases".format(len(mod.added_aliases)))
+                    if mod.removed_aliases:
+                        change.append("{0} Removed Aliases".format(len(mod.removed_aliases)))
+
+                    print("      * {0} ({1})".format(name, ", ".join(change)))
+                    print("          Aliases:")
+                    for a in sorted(mod.added_aliases):
+                        print("          + {0}".format(a))
+                    for a in sorted(mod.removed_aliases):
+                        print("          - {0}".format(a))
+
+            print()
+
+    if all_differences or args.level:
+        if diff.added_levels or diff.removed_levels or \
+                diff.modified_levels or args.level:
+            print("Levels ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_levels), len(diff.removed_levels),
+                len(diff.modified_levels)))
+            if diff.added_levels and not args.stats:
+                print("   Added Levels: {0}".format(len(diff.added_levels)))
+                for l in sorted(diff.added_levels):
+                    print("      + {0}".format(l))
+            if diff.removed_levels and not args.stats:
+                print("   Removed Levels: {0}".format(len(diff.removed_levels)))
+                for l in sorted(diff.removed_levels):
+                    print("      - {0}".format(l))
+            if diff.modified_levels and not args.stats:
+                print("   Modified Levels: {0}".format(len(diff.modified_levels)))
+                for level, added_categories, removed_categories, _ in sorted(diff.modified_levels,
+                                                                             key=lambda x: x.level):
+                    change = []
+                    if added_categories:
+                        change.append("{0} Added Categories".format(len(added_categories)))
+                    if removed_categories:
+                        change.append("{0} Removed Categories".format(len(removed_categories)))
+
+                    print("      * {0} ({1})".format(level.sensitivity, ", ".join(change)))
+                    for c in sorted(added_categories):
+                        print("          + {0}".format(c))
+                    for c in sorted(removed_categories):
+                        print("          - {0}".format(c))
+            print()
+
+    if all_differences or args.allow:
+        if diff.added_allows or diff.removed_allows or diff.modified_allows or args.allow:
+            print("Allow Rules ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_allows), len(diff.removed_allows), len(diff.modified_allows)))
+
+            if diff.added_allows and not args.stats:
+                print("   Added Allow Rules: {0}".format(len(diff.added_allows)))
+                for r in sorted(diff.added_allows):
+                    print("      + {0}".format(r))
+
+            if diff.removed_allows and not args.stats:
+                print("   Removed Allow Rules: {0}".format(len(diff.removed_allows)))
+                for r in sorted(diff.removed_allows):
+                    print("      - {0}".format(r))
+
+            if diff.modified_allows and not args.stats:
+                print("   Modified Allow Rules: {0}".format(len(diff.modified_allows)))
+
+                for rule, added_perms, removed_perms, matched_perms in sorted(diff.modified_allows,
+                                                                              key=lambda x: x.rule):
+                    perms = " ".join(chain((p for p in matched_perms),
+                                           ("+" + p for p in added_perms),
+                                           ("-" + p for p in removed_perms)))
+                    rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} {{ {1} }};".format(
+                        rule, perms)
+
+                    try:
+                        rule_string += " [ {0} ]".format(rule.conditional)
+                    except AttributeError:
+                        pass
+                    print("      * {0}".format(rule_string))
+
+            print()
+
+    if all_differences or args.allowxperm:
+        if diff.added_allowxperms or diff.removed_allowxperms or diff.modified_allowxperms \
+                or args.allowxperm:
+
+            print("Allowxperm Rules ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_allowxperms), len(diff.removed_allowxperms),
+                len(diff.modified_allowxperms)))
+
+            if diff.added_allowxperms and not args.stats:
+                print("   Added Allowxperm Rules: {0}".format(len(diff.added_allowxperms)))
+                for r in sorted(diff.added_allowxperms):
+                    print("      + {0}".format(r))
+
+            if diff.removed_allowxperms and not args.stats:
+                print("   Removed Allowxperm Rules: {0}".format(len(diff.removed_allowxperms)))
+                for r in sorted(diff.removed_allowxperms):
+                    print("      - {0}".format(r))
+
+            if diff.modified_allowxperms and not args.stats:
+                print("   Modified Allowxperm Rules: {0}".format(len(diff.modified_allowxperms)))
+
+                for rule, added_perms, removed_perms, matched_perms in sorted(
+                        diff.modified_allowxperms, key=lambda x: x.rule):
+
+                    # Process the string representation of the sets
+                    # so hex representation and ranges are preserved.
+                    # Check if the perm sets have contents, otherwise
+                    # split on empty string will be an empty string.
+                    # Add brackets to added and removed permissions
+                    # in case there is a range of permissions.
+                    perms = []
+                    if matched_perms:
+                        for p in str(matched_perms).split(" "):
+                            perms.append(p)
+                    if added_perms:
+                        for p in str(added_perms).split(" "):
+                            if '-' in p:
+                                perms.append("+[{0}]".format(p))
+                            else:
+                                perms.append("+{0}".format(p))
+                    if removed_perms:
+                        for p in str(removed_perms).split(" "):
+                            if '-' in p:
+                                perms.append("-[{0}]".format(p))
+                            else:
+                                perms.append("-{0}".format(p))
+
+                    rule_string = \
+                        "{0.ruletype} {0.source} {0.target}:{0.tclass} {0.xperm_type} {{ {1} }};". \
+                        format(rule, perms)
+
+                    print("      * {0.ruletype} {0.source} {0.target}:{0.tclass} {0.xperm_type} "
+                          "{{ {1} }};".format(rule, " ".join(perms)))
+
+            print()
+
+    if all_differences or args.neverallow:
+        if diff.added_neverallows or diff.removed_neverallows or diff.modified_neverallows or \
+                args.neverallow:
+            print("Neverallow Rules ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_neverallows), len(diff.removed_neverallows),
+                len(diff.modified_neverallows)))
+
+            if diff.added_neverallows and not args.stats:
+                print("   Added Neverallow Rules: {0}".format(len(diff.added_neverallows)))
+                for r in sorted(diff.added_neverallows):
+                    print("      + {0}".format(r))
+
+            if diff.removed_neverallows and not args.stats:
+                print("   Removed Neverallow Rules: {0}".format(len(diff.removed_neverallows)))
+                for r in sorted(diff.removed_neverallows):
+                    print("      - {0}".format(r))
+
+            if diff.modified_neverallows and not args.stats:
+                print("   Modified Neverallow Rules: {0}".format(len(diff.modified_neverallows)))
+
+                for rule, added_perms, removed_perms, matched_perms in sorted(
+                        diff.modified_neverallows, key=lambda x: x.rule):
+                    perms = " ".join(chain((p for p in matched_perms),
+                                           ("+" + p for p in added_perms),
+                                           ("-" + p for p in removed_perms)))
+                    rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} {{ {1} }};".format(
+                        rule, perms)
+
+                    try:
+                        rule_string += " [ {0} ]".format(rule.conditional)
+                    except AttributeError:
+                        pass
+                    print("      * {0}".format(rule_string))
+
+            print()
+
+    if all_differences or args.neverallowxperm:
+        if diff.added_neverallowxperms or diff.removed_neverallowxperms or \
+                diff.modified_neverallowxperms or args.neverallowxperm:
+
+            print("Neverallowxperm Rules ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_neverallowxperms), len(diff.removed_neverallowxperms),
+                len(diff.modified_neverallowxperms)))
+
+            if diff.added_neverallowxperms and not args.stats:
+                print("   Added Neverallowxperm Rules: {0}".format(
+                      len(diff.added_neverallowxperms)))
+                for r in sorted(diff.added_neverallowxperms):
+                    print("      + {0}".format(r))
+
+            if diff.removed_neverallowxperms and not args.stats:
+                print("   Removed Neverallowxperm Rules: {0}".format(
+                      len(diff.removed_neverallowxperms)))
+                for r in sorted(diff.removed_neverallowxperms):
+                    print("      - {0}".format(r))
+
+            if diff.modified_neverallowxperms and not args.stats:
+                print("   Modified Neverallowxperm Rules: {0}".format(
+                      len(diff.modified_neverallowxperms)))
+
+                for rule, added_perms, removed_perms, matched_perms in sorted(
+                        diff.modified_neverallowxperms, key=lambda x: x.rule):
+
+                    # Process the string representation of the sets
+                    # so hex representation and ranges are preserved.
+                    # Check if the perm sets have contents, otherwise
+                    # split on empty string will be an empty string.
+                    # Add brackets to added and removed permissions
+                    # in case there is a range of permissions.
+                    perms = []
+                    if matched_perms:
+                        for p in str(matched_perms).split(" "):
+                            perms.append(p)
+                    if added_perms:
+                        for p in str(added_perms).split(" "):
+                            if '-' in p:
+                                perms.append("+[{0}]".format(p))
+                            else:
+                                perms.append("+{0}".format(p))
+                    if removed_perms:
+                        for p in str(removed_perms).split(" "):
+                            if '-' in p:
+                                perms.append("-[{0}]".format(p))
+                            else:
+                                perms.append("-{0}".format(p))
+
+                    rule_string = \
+                        "{0.ruletype} {0.source} {0.target}:{0.tclass} {0.xperm_type} {{ {1} }};". \
+                        format(rule, perms)
+
+                    print("      * {0.ruletype} {0.source} {0.target}:{0.tclass} {0.xperm_type} "
+                          "{{ {1} }};".format(rule, " ".join(perms)))
+
+            print()
+
+    if all_differences or args.auditallow:
+        if diff.added_auditallows or diff.removed_auditallows or diff.modified_auditallows or \
+                args.auditallow:
+            print("Auditallow Rules ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_auditallows), len(diff.removed_auditallows),
+                len(diff.modified_auditallows)))
+
+            if diff.added_auditallows and not args.stats:
+                print("   Added Auditallow Rules: {0}".format(len(diff.added_auditallows)))
+                for r in sorted(diff.added_auditallows):
+                    print("      + {0}".format(r))
+
+            if diff.removed_auditallows and not args.stats:
+                print("   Removed Auditallow Rules: {0}".format(len(diff.removed_auditallows)))
+                for r in sorted(diff.removed_auditallows):
+                    print("      - {0}".format(r))
+
+            if diff.modified_auditallows and not args.stats:
+                print("   Modified Auditallow Rules: {0}".format(len(diff.modified_auditallows)))
+
+                for rule, added_perms, removed_perms, matched_perms in sorted(
+                        diff.modified_auditallows, key=lambda x: x.rule):
+                    perms = " ".join(chain((p for p in matched_perms),
+                                           ("+" + p for p in added_perms),
+                                           ("-" + p for p in removed_perms)))
+                    rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} {{ {1} }};".format(
+                        rule, perms)
+
+                    try:
+                        rule_string += " [ {0} ]".format(rule.conditional)
+                    except AttributeError:
+                        pass
+                    print("      * {0}".format(rule_string))
+
+            print()
+
+    if all_differences or args.auditallowxperm:
+        if diff.added_auditallowxperms or diff.removed_auditallowxperms or \
+                diff.modified_auditallowxperms or args.auditallowxperm:
+
+            print("Auditallowxperm Rules ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_auditallowxperms), len(diff.removed_auditallowxperms),
+                len(diff.modified_auditallowxperms)))
+
+            if diff.added_auditallowxperms and not args.stats:
+                print("   Added Auditallowxperm Rules: {0}".format(
+                      len(diff.added_auditallowxperms)))
+                for r in sorted(diff.added_auditallowxperms):
+                    print("      + {0}".format(r))
+
+            if diff.removed_auditallowxperms and not args.stats:
+                print("   Removed Auditallowxperm Rules: {0}".format(
+                      len(diff.removed_auditallowxperms)))
+                for r in sorted(diff.removed_auditallowxperms):
+                    print("      - {0}".format(r))
+
+            if diff.modified_auditallowxperms and not args.stats:
+                print("   Modified Auditallowxperm Rules: {0}".format(
+                      len(diff.modified_auditallowxperms)))
+
+                for rule, added_perms, removed_perms, matched_perms in sorted(
+                        diff.modified_auditallowxperms, key=lambda x: x.rule):
+
+                    # Process the string representation of the sets
+                    # so hex representation and ranges are preserved.
+                    # Check if the perm sets have contents, otherwise
+                    # split on empty string will be an empty string.
+                    # Add brackets to added and removed permissions
+                    # in case there is a range of permissions.
+                    perms = []
+                    if matched_perms:
+                        for p in str(matched_perms).split(" "):
+                            perms.append(p)
+                    if added_perms:
+                        for p in str(added_perms).split(" "):
+                            if '-' in p:
+                                perms.append("+[{0}]".format(p))
+                            else:
+                                perms.append("+{0}".format(p))
+                    if removed_perms:
+                        for p in str(removed_perms).split(" "):
+                            if '-' in p:
+                                perms.append("-[{0}]".format(p))
+                            else:
+                                perms.append("-{0}".format(p))
+
+                    rule_string = \
+                        "{0.ruletype} {0.source} {0.target}:{0.tclass} {0.xperm_type} {{ {1} }};". \
+                        format(rule, perms)
+
+                    print("      * {0.ruletype} {0.source} {0.target}:{0.tclass} {0.xperm_type} "
+                          "{{ {1} }};".format(rule, " ".join(perms)))
+
+            print()
+
+    if all_differences or args.dontaudit:
+        if diff.added_dontaudits or diff.removed_dontaudits or diff.modified_dontaudits or \
+                args.dontaudit:
+            print("Dontaudit Rules ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_dontaudits), len(diff.removed_dontaudits),
+                len(diff.modified_dontaudits)))
+
+            if diff.added_dontaudits and not args.stats:
+                print("   Added Dontaudit Rules: {0}".format(len(diff.added_dontaudits)))
+                for r in sorted(diff.added_dontaudits):
+                    print("      + {0}".format(r))
+
+            if diff.removed_dontaudits and not args.stats:
+                print("   Removed Dontaudit Rules: {0}".format(len(diff.removed_dontaudits)))
+                for r in sorted(diff.removed_dontaudits):
+                    print("      - {0}".format(r))
+
+            if diff.modified_dontaudits and not args.stats:
+                print("   Modified Dontaudit Rules: {0}".format(len(diff.modified_dontaudits)))
+
+                for rule, added_perms, removed_perms, matched_perms in sorted(
+                        diff.modified_dontaudits, key=lambda x: x.rule):
+                    perms = " ".join(chain((p for p in matched_perms),
+                                           ("+" + p for p in added_perms),
+                                           ("-" + p for p in removed_perms)))
+                    rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} {{ {1} }};".format(
+                        rule, perms)
+
+                    try:
+                        rule_string += " [ {0} ]".format(rule.conditional)
+                    except AttributeError:
+                        pass
+                    print("      * {0}".format(rule_string))
+
+            print()
+
+    if all_differences or args.dontauditxperm:
+        if diff.added_dontauditxperms or diff.removed_dontauditxperms or \
+                diff.modified_dontauditxperms or args.dontauditxperm:
+
+            print("Dontauditxperm Rules ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_dontauditxperms), len(diff.removed_dontauditxperms),
+                len(diff.modified_dontauditxperms)))
+
+            if diff.added_dontauditxperms and not args.stats:
+                print("   Added Dontauditxperm Rules: {0}".format(
+                      len(diff.added_dontauditxperms)))
+                for r in sorted(diff.added_dontauditxperms):
+                    print("      + {0}".format(r))
+
+            if diff.removed_dontauditxperms and not args.stats:
+                print("   Removed Dontauditxperm Rules: {0}".format(
+                      len(diff.removed_dontauditxperms)))
+                for r in sorted(diff.removed_dontauditxperms):
+                    print("      - {0}".format(r))
+
+            if diff.modified_dontauditxperms and not args.stats:
+                print("   Modified Dontauditxperm Rules: {0}".format(
+                      len(diff.modified_dontauditxperms)))
+
+                for rule, added_perms, removed_perms, matched_perms in sorted(
+                        diff.modified_dontauditxperms, key=lambda x: x.rule):
+
+                    # Process the string representation of the sets
+                    # so hex representation and ranges are preserved.
+                    # Check if the perm sets have contents, otherwise
+                    # split on empty string will be an empty string.
+                    # Add brackets to added and removed permissions
+                    # in case there is a range of permissions.
+                    perms = []
+                    if matched_perms:
+                        for p in str(matched_perms).split(" "):
+                            perms.append(p)
+                    if added_perms:
+                        for p in str(added_perms).split(" "):
+                            if '-' in p:
+                                perms.append("+[{0}]".format(p))
+                            else:
+                                perms.append("+{0}".format(p))
+                    if removed_perms:
+                        for p in str(removed_perms).split(" "):
+                            if '-' in p:
+                                perms.append("-[{0}]".format(p))
+                            else:
+                                perms.append("-{0}".format(p))
+
+                    rule_string = \
+                        "{0.ruletype} {0.source} {0.target}:{0.tclass} {0.xperm_type} {{ {1} }};". \
+                        format(rule, perms)
+
+                    print("      * {0.ruletype} {0.source} {0.target}:{0.tclass} {0.xperm_type} "
+                          "{{ {1} }};".format(rule, " ".join(perms)))
+
+            print()
+
+    if all_differences or args.type_trans:
+        if diff.added_type_transitions or diff.removed_type_transitions or \
+                diff.modified_type_transitions or args.type_trans:
+            print("Type_transition Rules ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_type_transitions), len(diff.removed_type_transitions),
+                len(diff.modified_type_transitions)))
+
+            if diff.added_type_transitions and not args.stats:
+                print("   Added Type_transition Rules: {0}".format(
+                    len(diff.added_type_transitions)))
+                for r in sorted(diff.added_type_transitions):
+                    print("      + {0}".format(r))
+
+            if diff.removed_type_transitions and not args.stats:
+                print("   Removed Type_transition Rules: {0}".format(
+                    len(diff.removed_type_transitions)))
+                for r in sorted(diff.removed_type_transitions):
+                    print("      - {0}".format(r))
+
+            if diff.modified_type_transitions and not args.stats:
+                print("   Modified Type_transition Rules: {0}".format(
+                    len(diff.modified_type_transitions)))
+
+                for rule, added_default, removed_default in sorted(diff.modified_type_transitions,
+                                                                   key=lambda x: x.rule):
+                    rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} +{1} -{2}".format(
+                        rule, added_default, removed_default)
+
+                    try:
+                        rule_string += " {0}".format(rule.filename)
+                    except AttributeError:
+                        pass
+
+                    rule_string += ";"
+
+                    try:
+                        rule_string += " [ {0} ]".format(rule.conditional)
+                    except AttributeError:
+                        pass
+
+                    print("      * {0}".format(rule_string))
+
+            print()
+
+    if all_differences or args.type_change:
+        if diff.added_type_changes or diff.removed_type_changes or \
+                diff.modified_type_changes or args.type_change:
+            print("Type_change Rules ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_type_changes), len(diff.removed_type_changes),
+                len(diff.modified_type_changes)))
+
+            if diff.added_type_changes and not args.stats:
+                print("   Added Type_change Rules: {0}".format(len(diff.added_type_changes)))
+                for r in sorted(diff.added_type_changes):
+                    print("      + {0}".format(r))
+
+            if diff.removed_type_changes and not args.stats:
+                print("   Removed Type_change Rules: {0}".format(len(diff.removed_type_changes)))
+                for r in sorted(diff.removed_type_changes):
+                    print("      - {0}".format(r))
+
+            if diff.modified_type_changes and not args.stats:
+                print("   Modified Type_change Rules: {0}".format(len(diff.modified_type_changes)))
+
+                for rule, added_default, removed_default in sorted(diff.modified_type_changes,
+                                                                   key=lambda x: x.rule):
+                    rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} +{1} -{2}".format(
+                        rule, added_default, removed_default)
+
+                    try:
+                        rule_string += " {0}".format(rule.filename)
+                    except AttributeError:
+                        pass
+
+                    rule_string += ";"
+
+                    try:
+                        rule_string += " [ {0} ]".format(rule.conditional)
+                    except AttributeError:
+                        pass
+
+                    print("      * {0}".format(rule_string))
+
+            print()
+
+    if all_differences or args.type_member:
+        if diff.added_type_members or diff.removed_type_members or \
+                diff.modified_type_members or args.type_member:
+            print("Type_member Rules ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_type_members), len(diff.removed_type_members),
+                len(diff.modified_type_members)))
+
+            if diff.added_type_members and not args.stats:
+                print("   Added Type_member Rules: {0}".format(len(diff.added_type_members)))
+                for r in sorted(diff.added_type_members):
+                    print("      + {0}".format(r))
+
+            if diff.removed_type_members and not args.stats:
+                print("   Removed Type_member Rules: {0}".format(len(diff.removed_type_members)))
+                for r in sorted(diff.removed_type_members):
+                    print("      - {0}".format(r))
+
+            if diff.modified_type_members and not args.stats:
+                print("   Modified Type_member Rules: {0}".format(len(diff.modified_type_members)))
+
+                for rule, added_default, removed_default in sorted(diff.modified_type_members,
+                                                                   key=lambda x: x.rule):
+                    rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} +{1} -{2}".format(
+                        rule, added_default, removed_default)
+
+                    try:
+                        rule_string += " {0}".format(rule.filename)
+                    except AttributeError:
+                        pass
+
+                    rule_string += ";"
+
+                    try:
+                        rule_string += " [ {0} ]".format(rule.conditional)
+                    except AttributeError:
+                        pass
+
+                    print("      * {0}".format(rule_string))
+
+            print()
+
+    if all_differences or args.role_allow:
+        if diff.added_role_allows or diff.removed_role_allows or args.role_allow:
+            print("Role allow Rules ({0} Added, {1} Removed)".format(
+                len(diff.added_role_allows), len(diff.removed_role_allows)))
+
+            if diff.added_role_allows and not args.stats:
+                print("   Added Role Allow Rules: {0}".format(
+                    len(diff.added_role_allows)))
+                for r in sorted(diff.added_role_allows):
+                    print("      + {0}".format(r))
+
+            if diff.removed_role_allows and not args.stats:
+                print("   Removed Role Allow Rules: {0}".format(
+                    len(diff.removed_role_allows)))
+                for r in sorted(diff.removed_role_allows):
+                    print("      - {0}".format(r))
+
+            print()
+
+    if all_differences or args.role_trans:
+        if diff.added_role_transitions or diff.removed_role_transitions or \
+                diff.modified_role_transitions or args.role_trans:
+            print("Role_transition Rules ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_role_transitions), len(diff.removed_role_transitions),
+                len(diff.modified_role_transitions)))
+
+            if diff.added_role_transitions and not args.stats:
+                print("   Added Role_transition Rules: {0}".format(
+                    len(diff.added_role_transitions)))
+                for r in sorted(diff.added_role_transitions):
+                    print("      + {0}".format(r))
+
+            if diff.removed_role_transitions and not args.stats:
+                print("   Removed Role_transition Rules: {0}".format(
+                    len(diff.removed_role_transitions)))
+                for r in sorted(diff.removed_role_transitions):
+                    print("      - {0}".format(r))
+
+            if diff.modified_role_transitions and not args.stats:
+                print("   Modified Role_transition Rules: {0}".format(
+                    len(diff.modified_role_transitions)))
+
+                for rule, added_default, removed_default in sorted(diff.modified_role_transitions,
+                                                                   key=lambda x: x.rule):
+                    rule_string = \
+                        "{0.ruletype} {0.source} {0.target}:{0.tclass} +{1} -{2}".format(
+                            rule, added_default, removed_default)
+
+                    print("      * {0}".format(rule_string))
+
+            print()
+
+    if all_differences or args.range_trans:
+        if diff.added_range_transitions or diff.removed_range_transitions or \
+                diff.modified_range_transitions or args.range_trans:
+            print("Range_transition Rules ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_range_transitions), len(diff.removed_range_transitions),
+                len(diff.modified_range_transitions)))
+
+            if diff.added_range_transitions and not args.stats:
+                print("   Added Range_transition Rules: {0}".format(
+                    len(diff.added_range_transitions)))
+                for r in sorted(diff.added_range_transitions):
+                    print("      + {0}".format(r))
+
+            if diff.removed_range_transitions and not args.stats:
+                print("   Removed Range_transition Rules: {0}".format(
+                    len(diff.removed_range_transitions)))
+                for r in sorted(diff.removed_range_transitions):
+                    print("      - {0}".format(r))
+
+            if diff.modified_range_transitions and not args.stats:
+                print("   Modified Range_transition Rules: {0}".format(
+                    len(diff.modified_range_transitions)))
+
+                for rule, added_default, removed_default in sorted(diff.modified_range_transitions,
+                                                                   key=lambda x: x.rule):
+                    # added brackets around range change for clarity since ranges
+                    # can have '-' and spaces.
+                    rule_string = \
+                        "{0.ruletype} {0.source} {0.target}:{0.tclass} +[{1}] -[{2}]".format(
+                            rule, added_default, removed_default)
+
+                    print("      * {0}".format(rule_string))
+
+            print()
+
+    if all_differences or args.constrain:
+        if diff.added_constrains or diff.removed_constrains or args.constrain:
+            print("Constraints ({0} Added, {1} Removed)".format(
+                len(diff.added_constrains), len(diff.removed_constrains)))
+
+            if diff.added_constrains and not args.stats:
+                print("   Added Constraints: {0}".format(
+                    len(diff.added_constrains)))
+                for r in sorted(diff.added_constrains):
+                    print("      + {0}".format(r))
+
+            if diff.removed_constrains and not args.stats:
+                print("   Removed Constraints: {0}".format(
+                    len(diff.removed_constrains)))
+                for r in sorted(diff.removed_constrains):
+                    print("      - {0}".format(r))
+
+            print()
+
+    if all_differences or args.mlsconstrain:
+        if diff.added_mlsconstrains or diff.removed_mlsconstrains or args.mlsconstrain:
+            print("MLS Constraints ({0} Added, {1} Removed)".format(
+                len(diff.added_mlsconstrains), len(diff.removed_mlsconstrains)))
+
+            if diff.added_mlsconstrains and not args.stats:
+                print("   Added MLS Constraints: {0}".format(
+                    len(diff.added_mlsconstrains)))
+                for r in sorted(diff.added_mlsconstrains):
+                    print("      + {0}".format(r))
+
+            if diff.removed_mlsconstrains and not args.stats:
+                print("   Removed MLS Constraints: {0}".format(
+                    len(diff.removed_mlsconstrains)))
+                for r in sorted(diff.removed_mlsconstrains):
+                    print("      - {0}".format(r))
+
+            print()
+
+    if all_differences or args.validatetrans:
+        if diff.added_validatetrans or diff.removed_validatetrans or args.validatetrans:
+            print("Validatetrans ({0} Added, {1} Removed)".format(
+                len(diff.added_validatetrans), len(diff.removed_validatetrans)))
+
+            if diff.added_validatetrans and not args.stats:
+                print("   Added Validatetrans: {0}".format(
+                    len(diff.added_validatetrans)))
+                for r in sorted(diff.added_validatetrans):
+                    print("      + {0}".format(r))
+
+            if diff.removed_validatetrans and not args.stats:
+                print("   Removed Validatetrans: {0}".format(
+                    len(diff.removed_validatetrans)))
+                for r in sorted(diff.removed_validatetrans):
+                    print("      - {0}".format(r))
+
+            print()
+
+    if all_differences or args.mlsvalidatetrans:
+        if diff.added_mlsvalidatetrans or diff.removed_mlsvalidatetrans or args.mlsvalidatetrans:
+            print("MLS Validatetrans ({0} Added, {1} Removed)".format(
+                len(diff.added_mlsvalidatetrans), len(diff.removed_mlsvalidatetrans)))
+
+            if diff.added_mlsvalidatetrans and not args.stats:
+                print("   Added MLS Validatetrans: {0}".format(
+                    len(diff.added_mlsvalidatetrans)))
+                for r in sorted(diff.added_mlsvalidatetrans):
+                    print("      + {0}".format(r))
+
+            if diff.removed_mlsvalidatetrans and not args.stats:
+                print("   Removed MLS Validatetrans: {0}".format(
+                    len(diff.removed_mlsvalidatetrans)))
+                for r in sorted(diff.removed_mlsvalidatetrans):
+                    print("      - {0}".format(r))
+
+            print()
+
+    if all_differences or args.initialsid:
+        if diff.added_initialsids or diff.removed_initialsids or diff.modified_initialsids \
+                or args.initialsid:
+            print("Initial SIDs ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_initialsids), len(diff.removed_initialsids),
+                len(diff.modified_initialsids)))
+            if diff.added_initialsids and not args.stats:
+                print("   Added Initial SIDs: {0}".format(len(diff.added_initialsids)))
+                for s in sorted(diff.added_initialsids):
+                    print("      + {0}".format(s.statement()))
+            if diff.removed_initialsids and not args.stats:
+                print("   Removed Initial SIDs: {0}".format(len(diff.removed_initialsids)))
+                for s in sorted(diff.removed_initialsids):
+                    print("      - {0}".format(s.statement()))
+            if diff.modified_initialsids and not args.stats:
+                print("   Modified Initial SIDs: {0}".format(len(diff.modified_initialsids)))
+                for name, mod in sorted(diff.modified_initialsids.items()):
+                    print("      * {0} +[{1.added_context}] -[{1.removed_context}];".format(
+                          name, mod))
+
+            print()
+
+    if all_differences or args.fs_use:
+        if diff.added_fs_uses or diff.removed_fs_uses or diff.modified_fs_uses \
+                or args.fs_use:
+            print("Fs_use ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_fs_uses), len(diff.removed_fs_uses),
+                len(diff.modified_fs_uses)))
+            if diff.added_fs_uses and not args.stats:
+                print("   Added Fs_use: {0}".format(len(diff.added_fs_uses)))
+                for s in sorted(diff.added_fs_uses):
+                    print("      + {0}".format(s))
+            if diff.removed_fs_uses and not args.stats:
+                print("   Removed Fs_use: {0}".format(len(diff.removed_fs_uses)))
+                for s in sorted(diff.removed_fs_uses):
+                    print("      - {0}".format(s))
+            if diff.modified_fs_uses and not args.stats:
+                print("   Modified Fs_use: {0}".format(len(diff.modified_fs_uses)))
+                for entry in sorted(diff.modified_fs_uses, key=lambda x: x.rule):
+                    print("      * {0.ruletype} {0.fs} +[{1}] -[{2}];".format(
+                          entry.rule, entry.added_context, entry.removed_context))
+
+            print()
+
+    if all_differences or args.genfscon:
+        if diff.added_genfscons or diff.removed_genfscons or diff.modified_genfscons \
+                or args.genfscon:
+            print("Genfscons ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_genfscons), len(diff.removed_genfscons),
+                len(diff.modified_genfscons)))
+            if diff.added_genfscons and not args.stats:
+                print("   Added Genfscons: {0}".format(len(diff.added_genfscons)))
+                for s in sorted(diff.added_genfscons):
+                    print("      + {0}".format(s))
+            if diff.removed_genfscons and not args.stats:
+                print("   Removed Genfscons: {0}".format(len(diff.removed_genfscons)))
+                for s in sorted(diff.removed_genfscons):
+                    print("      - {0}".format(s))
+            if diff.modified_genfscons and not args.stats:
+                print("   Modified Genfscons: {0}".format(len(diff.modified_genfscons)))
+                for entry in sorted(diff.modified_genfscons, key=lambda x: x.rule):
+                    print("      * genfscon {0.fs} {0.path} {0.filetype} +[{1}] -[{2}];".format(
+                          entry.rule, entry.added_context, entry.removed_context))
+
+            print()
+
+    if all_differences or args.netifcon:
+        if diff.added_netifcons or diff.removed_netifcons or \
+                diff.modified_netifcons or args.netifcon:
+            print("Netifcons ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_netifcons), len(diff.removed_netifcons),
+                len(diff.modified_netifcons)))
+            if diff.added_netifcons and not args.stats:
+                print("   Added Netifcons: {0}".format(len(diff.added_netifcons)))
+                for n in sorted(diff.added_netifcons):
+                    print("      + {0}".format(n))
+            if diff.removed_netifcons and not args.stats:
+                print("   Removed Netifcons: {0}".format(len(diff.removed_netifcons)))
+                for n in sorted(diff.removed_netifcons):
+                    print("      - {0}".format(n))
+            if diff.modified_netifcons and not args.stats:
+                print("   Modified Netifcons: {0}".format(len(diff.modified_netifcons)))
+                for entry in sorted(diff.modified_netifcons, key=lambda x: x.rule):
+                    # This output is different than other statements because
+                    # it becomes difficult to read if this was condensed
+                    # into a single line, especially if both contexts
+                    # are modified.
+                    change = []
+                    if entry.removed_context:
+                        change.append("Modified Context")
+                    if entry.removed_packet:
+                        change.append("Modified Packet Context")
+
+                    print("      * {0.netif} ({1})".format(entry.rule, ", ".join(change)))
+
+                    if entry.removed_context:
+                        print("          Context:")
+                        print("             + {0}".format(entry.added_context))
+                        print("             - {0}".format(entry.removed_context))
+                    if entry.removed_packet:
+                        print("          Packet Context:")
+                        print("             + {0}".format(entry.added_packet))
+                        print("             - {0}".format(entry.removed_packet))
+
+            print()
+
+    if all_differences or args.nodecon:
+        if diff.added_nodecons or diff.removed_nodecons or diff.modified_nodecons \
+                or args.nodecon:
+            print("Nodecons ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_nodecons), len(diff.removed_nodecons),
+                len(diff.modified_nodecons)))
+            if diff.added_nodecons and not args.stats:
+                print("   Added Nodecons: {0}".format(len(diff.added_nodecons)))
+                for n in sorted(diff.added_nodecons):
+                    print("      + {0}".format(n))
+            if diff.removed_nodecons and not args.stats:
+                print("   Removed Nodecons: {0}".format(len(diff.removed_nodecons)))
+                for n in sorted(diff.removed_nodecons):
+                    print("      - {0}".format(n))
+            if diff.modified_nodecons and not args.stats:
+                print("   Modified Nodecons: {0}".format(len(diff.modified_nodecons)))
+                for con, added_context, removed_context in sorted(diff.modified_nodecons,
+                                                                  key=lambda x: x.rule):
+                    print("      * nodecon {0.address} {0.netmask} +[{1}] -[{2}];".format(
+                          con, added_context, removed_context))
+
+            print()
+
+    if all_differences or args.portcon:
+        if diff.added_portcons or diff.removed_portcons or diff.modified_portcons \
+                or args.portcon:
+            print("Portcons ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_portcons), len(diff.removed_portcons),
+                len(diff.modified_portcons)))
+            if diff.added_portcons and not args.stats:
+                print("   Added Portcons: {0}".format(len(diff.added_portcons)))
+                for n in sorted(diff.added_portcons):
+                    print("      + {0}".format(n))
+            if diff.removed_portcons and not args.stats:
+                print("   Removed Portcons: {0}".format(len(diff.removed_portcons)))
+                for n in sorted(diff.removed_portcons):
+                    print("      - {0}".format(n))
+            if diff.modified_portcons and not args.stats:
+                print("   Modified Portcons: {0}".format(len(diff.modified_portcons)))
+                for con, added_context, removed_context in sorted(diff.modified_portcons,
+                                                                  key=lambda x: x.rule):
+                    low, high = con.ports
+                    if low == high:
+                        print("      * portcon {0.protocol} {1} +[{2}] -[{3}];".format(
+                              con, low, added_context, removed_context))
+                    else:
+                        print("      * portcon {0.protocol} {1}-{2} +[{3}] -[{4}];".format(
+                              con, low, high, added_context, removed_context))
+
+            print()
+
+    if all_differences or args.polcap:
+        if diff.added_polcaps or diff.removed_polcaps or args.polcap:
+            print("Policy Capabilities ({0} Added, {1} Removed)".format(
+                len(diff.added_polcaps), len(diff.removed_polcaps)))
+            if diff.added_polcaps and not args.stats:
+                print("   Added Policy Capabilities: {0}".format(len(diff.added_polcaps)))
+                for n in sorted(diff.added_polcaps):
+                    print("      + {0}".format(n))
+            if diff.removed_polcaps and not args.stats:
+                print("   Removed Policy Capabilities: {0}".format(len(diff.removed_polcaps)))
+                for n in sorted(diff.removed_polcaps):
+                    print("      - {0}".format(n))
+
+            print()
+
+    if all_differences or args.default:
+        if diff.added_defaults or diff.removed_defaults or args.default:
+            print("Defaults ({0} Added, {1} Removed, {2} Modified)".format(
+                len(diff.added_defaults), len(diff.removed_defaults), len(diff.modified_defaults)))
+            if diff.added_defaults and not args.stats:
+                print("   Added Defaults: {0}".format(len(diff.added_defaults)))
+                for d in sorted(diff.added_defaults):
+                    print("      + {0}".format(d))
+            if diff.removed_defaults and not args.stats:
+                print("   Removed Defaults: {0}".format(len(diff.removed_defaults)))
+                for d in sorted(diff.removed_defaults):
+                    print("      - {0}".format(d))
+            if diff.modified_defaults and not args.stats:
+                print("   Modified Defaults: {0}".format(len(diff.modified_defaults)))
+                for default, added_default, removed_default, added_range, removed_range in sorted(
+                        diff.modified_defaults, key=lambda x: x.rule):
+                    line = "      * {0.ruletype} {0.tclass} ".format(default)
+                    if removed_default:
+                        line += "+{0} -{1}".format(added_default, removed_default)
+                    else:
+                        line += default.default
+
+                    if removed_range:
+                        line += " +{0} -{1};".format(added_range, removed_range)
+                    else:
+                        try:
+                            line += " {0};".format(default.default_range)
+                        except AttributeError:
+                            line += ";"
+                    print(line)
+
+            print()
+
+    if all_differences or args.typebounds:
+        if diff.added_typebounds or diff.removed_typebounds or args.typebounds:
+            print("Typebounds ({0} Added, {1} Removed, {2} Modified)".format(
+                  len(diff.added_typebounds), len(diff.removed_typebounds),
+                  len(diff.modified_typebounds)))
+            if diff.added_typebounds and not args.stats:
+                print("   Added Typebounds: {0}".format(len(diff.added_typebounds)))
+                for d in sorted(diff.added_typebounds):
+                    print("      + {0}".format(d))
+            if diff.removed_typebounds and not args.stats:
+                print("   Removed Typebounds: {0}".format(len(diff.removed_typebounds)))
+                for d in sorted(diff.removed_typebounds):
+                    print("      - {0}".format(d))
+            if diff.modified_typebounds and not args.stats:
+                print("   Modified Typebounds: {0}".format(len(diff.modified_typebounds)))
+                for bound, added_bound, removed_bound in sorted(
+                        diff.modified_typebounds, key=lambda x: x.rule):
+                    print("      * {0.ruletype} +{1} -{2} {0.child};".format(
+                          bound, added_bound, removed_bound))
+
+            print()
+
+except Exception as err:
+    if args.debug:
+        logging.exception(str(err))
+    else:
+        print(err)
+
+    sys.exit(1)
diff --git a/prebuilts/bin/seinfo b/prebuilts/bin/seinfo
new file mode 100755
index 0000000..a6f6906
--- /dev/null
+++ b/prebuilts/bin/seinfo
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+if [ -z "${ANDROID_BUILD_TOP}" ]; then
+        echo 'ANDROID_BUILD_TOP not set. Have you run lunch?'
+        exit 1
+fi
+
+unamestr=`uname`
+if [ "$unamestr" = "Linux" -o "$unamestr" = "linux" ]; then
+	export LD_LIBRARY_PATH=$ANDROID_BUILD_TOP/external/selinux/prebuilts/lib
+	export PYTHONPATH=$ANDROID_BUILD_TOP/prebuilts/python/linux-x86/2.7.5/lib/python2.7/site-packages
+	exec python $ANDROID_BUILD_TOP/external/selinux/prebuilts/bin/seinfo.py "$@"
+else
+	echo "seinfo is only supported on linux"
+	exit 1
+fi
diff --git a/prebuilts/bin/seinfo.py b/prebuilts/bin/seinfo.py
new file mode 100755
index 0000000..e794da7
--- /dev/null
+++ b/prebuilts/bin/seinfo.py
@@ -0,0 +1,366 @@
+#!/usr/bin/env python
+# Copyright 2014-2015, Tresys Technology, LLC
+#
+# This file is part of SETools.
+#
+# SETools is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# SETools is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with SETools.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+from __future__ import print_function
+import setools
+import argparse
+import sys
+import logging
+
+
+def expand_attr(attr):
+    """Render type and role attributes."""
+    items = "\n\t".join(sorted(str(i) for i in attr.expand()))
+    contents = items if items else "<empty attribute>"
+    return "{0}\n\t{1}".format(attr.statement(), contents)
+
+parser = argparse.ArgumentParser(
+    description="SELinux policy information tool.")
+parser.add_argument("--version", action="version", version=setools.__version__)
+parser.add_argument("policy", help="Path to the SELinux policy to query.", nargs="?")
+parser.add_argument("-x", "--expand", action="store_true",
+                    help="Print additional information about the specified components.")
+parser.add_argument("--flat",  help="Print without item count nor indentation.",
+                    dest="flat", default=False, action="store_true")
+parser.add_argument("-v", "--verbose", action="store_true",
+                    help="Print extra informational messages")
+parser.add_argument("--debug", action="store_true", dest="debug", help="Enable debugging.")
+
+queries = parser.add_argument_group("Component Queries")
+queries.add_argument("-a", "--attribute",  help="Print type attributes.", dest="typeattrquery",
+                     nargs='?', const=True, metavar="ATTR")
+queries.add_argument("-b", "--bool", help="Print Booleans.", dest="boolquery",
+                     nargs='?', const=True, metavar="BOOL")
+queries.add_argument("-c", "--class", help="Print object classes.", dest="classquery",
+                     nargs='?', const=True, metavar="CLASS")
+queries.add_argument("-r", "--role", help="Print roles.", dest="rolequery",
+                     nargs='?', const=True, metavar="ROLE")
+queries.add_argument("-t", "--type", help="Print types.", dest="typequery",
+                     nargs='?', const=True, metavar="TYPE")
+queries.add_argument("-u", "--user", help="Print users.", dest="userquery",
+                     nargs='?', const=True, metavar="USER")
+queries.add_argument("--category", help="Print MLS categories.", dest="mlscatsquery",
+                     nargs='?', const=True, metavar="CAT")
+queries.add_argument("--common", help="Print common permission set.", dest="commonquery",
+                     nargs='?', const=True, metavar="COMMON")
+queries.add_argument("--constrain", help="Print constraints.", dest="constraintquery",
+                     nargs='?', const=True, metavar="CLASS")
+queries.add_argument("--default", help="Print default_* rules.", dest="defaultquery",
+                     nargs='?', const=True, metavar="CLASS")
+queries.add_argument("--fs_use", help="Print fs_use statements.", dest="fsusequery",
+                     nargs='?', const=True, metavar="FS_TYPE")
+queries.add_argument("--genfscon", help="Print genfscon statements.", dest="genfsconquery",
+                     nargs='?', const=True, metavar="FS_TYPE")
+queries.add_argument("--initialsid", help="Print initial SIDs (contexts).", dest="initialsidquery",
+                     nargs='?', const=True, metavar="NAME")
+queries.add_argument("--netifcon", help="Print netifcon statements.", dest="netifconquery",
+                     nargs='?', const=True, metavar="DEVICE")
+queries.add_argument("--nodecon", help="Print nodecon statements.", dest="nodeconquery",
+                     nargs='?', const=True, metavar="ADDR")
+queries.add_argument("--permissive", help="Print permissive types.", dest="permissivequery",
+                     nargs='?', const=True, metavar="TYPE")
+queries.add_argument("--polcap", help="Print policy capabilities.", dest="polcapquery",
+                     nargs='?', const=True, metavar="NAME")
+queries.add_argument("--portcon", help="Print portcon statements.", dest="portconquery",
+                     nargs='?', const=True, metavar="PORTNUM[-PORTNUM]")
+queries.add_argument("--sensitivity", help="Print MLS sensitivities.", dest="mlssensquery",
+                     nargs='?', const=True, metavar="SENS")
+queries.add_argument("--typebounds", help="Print typebounds statements.", dest="typeboundsquery",
+                     nargs='?', const=True, metavar="BOUND_TYPE")
+queries.add_argument("--validatetrans", help="Print validatetrans.", dest="validatetransquery",
+                     nargs='?', const=True, metavar="CLASS")
+queries.add_argument("--all", help="Print all of the above.  On a Xen policy, the Xen components "
+                     "will also be printed", dest="all", default=False, action="store_true")
+
+xen = parser.add_argument_group("Xen Component Queries")
+xen.add_argument("--ioportcon", help="Print all ioportcon statements.", dest="ioportconquery",
+                 default=False, action="store_true")
+xen.add_argument("--iomemcon", help="Print all iomemcon statements.", dest="iomemconquery",
+                 default=False, action="store_true")
+xen.add_argument("--pcidevicecon", help="Print all pcidevicecon statements.",
+                 dest="pcideviceconquery", default=False, action="store_true")
+xen.add_argument("--pirqcon", help="Print all pirqcon statements.", dest="pirqconquery",
+                 default=False, action="store_true")
+xen.add_argument("--devicetreecon", help="Print all devicetreecon statements.",
+                 dest="devicetreeconquery", default=False, action="store_true")
+
+
+args = parser.parse_args()
+
+if args.debug:
+    logging.basicConfig(level=logging.DEBUG,
+                        format='%(asctime)s|%(levelname)s|%(name)s|%(message)s')
+elif args.verbose:
+    logging.basicConfig(level=logging.INFO, format='%(message)s')
+else:
+    logging.basicConfig(level=logging.WARNING, format='%(message)s')
+
+try:
+    p = setools.SELinuxPolicy(args.policy)
+    components = []
+
+    if args.boolquery or args.all:
+        q = setools.BoolQuery(p)
+        if isinstance(args.boolquery, str):
+            q.name = args.boolquery
+
+        components.append(("Booleans", q, lambda x: x.statement()))
+
+    if args.mlscatsquery or args.all:
+        q = setools.CategoryQuery(p)
+        if isinstance(args.mlscatsquery, str):
+            q.name = args.mlscatsquery
+
+        components.append(("Categories", q, lambda x: x.statement()))
+
+    if args.classquery or args.all:
+        q = setools.ObjClassQuery(p)
+        if isinstance(args.classquery, str):
+            q.name = args.classquery
+
+        components.append(("Classes", q, lambda x: x.statement()))
+
+    if args.commonquery or args.all:
+        q = setools.CommonQuery(p)
+        if isinstance(args.commonquery, str):
+            q.name = args.commonquery
+
+        components.append(("Commons", q, lambda x: x.statement()))
+
+    if args.constraintquery or args.all:
+        q = setools.ConstraintQuery(p, ruletype=["constrain", "mlsconstrain"])
+        if isinstance(args.constraintquery, str):
+            q.tclass = [args.constraintquery]
+
+        components.append(("Constraints", q, lambda x: x.statement()))
+
+    if args.defaultquery or args.all:
+        q = setools.DefaultQuery(p)
+        if isinstance(args.defaultquery, str):
+            q.tclass = [args.defaultquery]
+
+        components.append(("Default rules", q, lambda x: x.statement()))
+
+    if args.fsusequery or args.all:
+        q = setools.FSUseQuery(p)
+        if isinstance(args.fsusequery, str):
+            q.fs = args.fsusequery
+
+        components.append(("Fs_use", q, lambda x: x.statement()))
+
+    if args.genfsconquery or args.all:
+        q = setools.GenfsconQuery(p)
+        if isinstance(args.genfsconquery, str):
+            q.fs = args.genfsconquery
+
+        components.append(("Genfscon", q, lambda x: x.statement()))
+
+    if args.initialsidquery or args.all:
+        q = setools.InitialSIDQuery(p)
+        if isinstance(args.initialsidquery, str):
+            q.name = args.initialsidquery
+
+        components.append(("Initial SIDs", q, lambda x: x.statement()))
+
+    if args.netifconquery or args.all:
+        q = setools.NetifconQuery(p)
+        if isinstance(args.netifconquery, str):
+            q.name = args.netifconquery
+
+        components.append(("Netifcon", q, lambda x: x.statement()))
+
+    if args.nodeconquery or args.all:
+        q = setools.NodeconQuery(p)
+        if isinstance(args.nodeconquery, str):
+            q.network = args.nodeconquery
+
+        components.append(("Nodecon", q, lambda x: x.statement()))
+
+    if args.permissivequery or args.all:
+        q = setools.TypeQuery(p, permissive=True, match_permissive=True)
+        if isinstance(args.permissivequery, str):
+            q.name = args.permissivequery
+
+        components.append(("Permissive Types", q, lambda x: x.statement()))
+
+    if args.polcapquery or args.all:
+        q = setools.PolCapQuery(p)
+        if isinstance(args.polcapquery, str):
+            q.name = args.polcapquery
+
+        components.append(("Polcap", q, lambda x: x.statement()))
+
+    if args.portconquery or args.all:
+        q = setools.PortconQuery(p)
+        if isinstance(args.portconquery, str):
+            try:
+                ports = [int(i) for i in args.portconquery.split("-")]
+            except ValueError:
+                parser.error("Enter a port number or range, e.g. 22 or 6000-6020")
+
+            if len(ports) == 2:
+                q.ports = ports
+            elif len(ports) == 1:
+                q.ports = (ports[0], ports[0])
+            else:
+                parser.error("Enter a port number or range, e.g. 22 or 6000-6020")
+
+        components.append(("Portcon", q, lambda x: x.statement()))
+
+    if args.rolequery or args.all:
+        q = setools.RoleQuery(p)
+        if isinstance(args.rolequery, str):
+            q.name = args.rolequery
+
+        components.append(("Roles", q, lambda x: x.statement()))
+
+    if args.mlssensquery or args.all:
+        q = setools.SensitivityQuery(p)
+        if isinstance(args.mlssensquery, str):
+            q.name = args.mlssensquery
+
+        components.append(("Sensitivities", q, lambda x: x.statement()))
+
+    if args.typeboundsquery or args.all:
+        q = setools.BoundsQuery(p, ruletype=["typebounds"])
+        if isinstance(args.typeboundsquery, str):
+            q.child = args.typeboundsquery
+
+        components.append(("Typebounds", q, lambda x: x.statement()))
+
+    if args.typequery or args.all:
+        q = setools.TypeQuery(p)
+        if isinstance(args.typequery, str):
+            q.name = args.typequery
+
+        components.append(("Types", q, lambda x: x.statement()))
+
+    if args.typeattrquery or args.all:
+        q = setools.TypeAttributeQuery(p)
+        if isinstance(args.typeattrquery, str):
+            q.name = args.typeattrquery
+
+        components.append(("Type Attributes", q, expand_attr))
+
+    if args.userquery or args.all:
+        q = setools.UserQuery(p)
+        if isinstance(args.userquery, str):
+            q.name = args.userquery
+
+        components.append(("Users", q, lambda x: x.statement()))
+
+    if args.validatetransquery or args.all:
+        q = setools.ConstraintQuery(p, ruletype=["validatetrans", "mlsvalidatetrans"])
+        if isinstance(args.validatetransquery, str):
+            q.tclass = [args.validatetransquery]
+
+        components.append(("Validatetrans", q, lambda x: x.statement()))
+
+    if p.target_platform == "xen":
+        if args.ioportconquery or args.all:
+            q = setools.IoportconQuery(p)
+            components.append(("Ioportcon", q, lambda x: x.statement()))
+
+        if args.iomemconquery or args.all:
+            q = setools.IomemconQuery(p)
+            components.append(("Iomemcon", q, lambda x: x.statement()))
+
+        if args.pcideviceconquery or args.all:
+            q = setools.PcideviceconQuery(p)
+            components.append(("Pcidevicecon", q, lambda x: x.statement()))
+
+        if args.pirqconquery or args.all:
+            q = setools.PirqconQuery(p)
+            components.append(("Pirqcon", q, lambda x: x.statement()))
+
+        if args.devicetreeconquery or args.all:
+            q = setools.DevicetreeconQuery(p)
+            components.append(("Devicetreecon", q, lambda x: x.statement()))
+
+    if (not components or args.all) and not args.flat:
+        mls = "enabled" if p.mls else "disabled"
+
+        print("Statistics for policy file: {0}".format(p))
+        print("Policy Version:             {0} (MLS {1})".format(p.version, mls))
+        print("Target Policy:              {0}".format(p.target_platform))
+        print("Handle unknown classes:     {0}".format(p.handle_unknown))
+        print("  Classes:         {0:7}    Permissions:     {1:7}".format(
+            p.class_count, p.permission_count))
+        print("  Sensitivities:   {0:7}    Categories:      {1:7}".format(
+            p.level_count, p.category_count))
+        print("  Types:           {0:7}    Attributes:      {1:7}".format(
+            p.type_count, p.type_attribute_count))
+        print("  Users:           {0:7}    Roles:           {1:7}".format(
+            p.user_count, p.role_count))
+        print("  Booleans:        {0:7}    Cond. Expr.:     {1:7}".format(
+            p.boolean_count, p.conditional_count))
+        print("  Allow:           {0:7}    Neverallow:      {1:7}".format(
+            p.allow_count, p.neverallow_count))
+        print("  Auditallow:      {0:7}    Dontaudit:       {1:7}".format(
+            p.auditallow_count, p.dontaudit_count))
+        print("  Type_trans:      {0:7}    Type_change:     {1:7}".format(
+            p.type_transition_count, p.type_change_count))
+        print("  Type_member:     {0:7}    Range_trans:     {1:7}".format(
+            p.type_member_count, p.range_transition_count))
+        print("  Role allow:      {0:7}    Role_trans:      {1:7}".format(
+            p.role_allow_count, p.role_transition_count))
+        print("  Constraints:     {0:7}    Validatetrans:   {1:7}".format(
+            p.constraint_count, p.validatetrans_count))
+        print("  MLS Constrain:   {0:7}    MLS Val. Tran:   {1:7}".format(
+            p.mlsconstraint_count, p.mlsvalidatetrans_count))
+        print("  Permissives:     {0:7}    Polcap:          {1:7}".format(
+            p.permissives_count, p.polcap_count))
+        print("  Defaults:        {0:7}    Typebounds:      {1:7}".format(
+            p.default_count, p.typebounds_count))
+
+        if p.target_platform == "selinux":
+            print("  Allowxperm:      {0:7}    Neverallowxperm: {1:7}".format(
+                p.allowxperm_count, p.neverallowxperm_count))
+            print("  Auditallowxperm: {0:7}    Dontauditxperm:  {1:7}".format(
+                p.auditallowxperm_count, p.dontauditxperm_count))
+            print("  Initial SIDs:    {0:7}    Fs_use:          {1:7}".format(
+                p.initialsids_count, p.fs_use_count))
+            print("  Genfscon:        {0:7}    Portcon:         {1:7}".format(
+                p.genfscon_count, p.portcon_count))
+            print("  Netifcon:        {0:7}    Nodecon:         {1:7}".format(
+                p.netifcon_count, p.nodecon_count))
+        elif p.target_platform == "xen":
+            print("  Initial SIDs:    {0:7}    Devicetreecon    {1:7}".format(
+                p.initialsids_count, p.devicetreecon_count))
+            print("  Iomemcon:        {0:7}    Ioportcon:       {1:7}".format(
+                p.iomemcon_count, p.ioportcon_count))
+            print("  Pcidevicecon:    {0:7}    Pirqcon:         {1:7}".format(
+                p.pcidevicecon_count, p.pirqcon_count))
+
+    for desc, component, expander in components:
+        results = sorted(component.results())
+        if not args.flat:
+            print("\n{0}: {1}".format(desc, len(results)))
+        for item in results:
+            result = expander(item) if args.expand else item
+            strfmt = "   {0}" if not args.flat else "{0}"
+            print(strfmt.format(result))
+
+except Exception as err:
+    if args.debug:
+        logging.exception(str(err))
+    else:
+        print(err)
+
+    sys.exit(1)
diff --git a/prebuilts/bin/sesearch b/prebuilts/bin/sesearch
new file mode 100755
index 0000000..4e2a588
--- /dev/null
+++ b/prebuilts/bin/sesearch
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+unamestr=`uname`
+if [ "$unamestr" = "Linux" -o "$unamestr" = "linux" ]; then
+	export PYTHONPATH=$ANDROID_BUILD_TOP/prebuilts/python/linux-x86/2.7.5/lib/python2.7/site-packages:$PYTHONPATH
+	python $ANDROID_BUILD_TOP/external/selinux/prebuilts/bin/sesearch.py "$@"
+else
+	echo "sesearch is only supported on linux"
+fi
diff --git a/prebuilts/bin/sesearch.py b/prebuilts/bin/sesearch.py
new file mode 100755
index 0000000..5879753
--- /dev/null
+++ b/prebuilts/bin/sesearch.py
@@ -0,0 +1,248 @@
+#!/usr/bin/env python
+# Copyright 2014-2015, Tresys Technology, LLC
+#
+# This file is part of SETools.
+#
+# SETools is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# SETools is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with SETools.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+from __future__ import print_function
+import setools
+import argparse
+import sys
+import logging
+
+parser = argparse.ArgumentParser(
+    description="SELinux policy rule search tool.",
+    epilog="TE/MLS rule searches cannot be mixed with RBAC rule searches.")
+parser.add_argument("--version", action="version", version=setools.__version__)
+parser.add_argument("policy", help="Path to the SELinux policy to search.", nargs="?")
+parser.add_argument("-v", "--verbose", action="store_true",
+                    help="Print extra informational messages")
+parser.add_argument("--debug", action="store_true", dest="debug", help="Enable debugging.")
+
+rtypes = parser.add_argument_group("TE Rule Types")
+rtypes.add_argument("-A", action="store_true", help="Search allow and allowxperm rules.")
+rtypes.add_argument("--allow", action="append_const",
+                    const="allow", dest="tertypes",
+                    help="Search allow rules.")
+rtypes.add_argument("--allowxperm", action="append_const",
+                    const="allowxperm", dest="tertypes",
+                    help="Search allowxperm rules.")
+rtypes.add_argument("--auditallow", action="append_const",
+                    const="auditallow", dest="tertypes",
+                    help="Search auditallow rules.")
+rtypes.add_argument("--auditallowxperm", action="append_const",
+                    const="auditallowxperm", dest="tertypes",
+                    help="Search auditallowxperm rules.")
+rtypes.add_argument("--dontaudit", action="append_const",
+                    const="dontaudit", dest="tertypes",
+                    help="Search dontaudit rules.")
+rtypes.add_argument("--dontauditxperm", action="append_const",
+                    const="dontauditxperm", dest="tertypes",
+                    help="Search dontauditxperm rules.")
+rtypes.add_argument("--neverallow", action="append_const",
+                    const="neverallow", dest="tertypes",
+                    help="Search neverallow rules.")
+rtypes.add_argument("--neverallowxperm", action="append_const",
+                    const="neverallowxperm", dest="tertypes",
+                    help="Search neverallowxperm rules.")
+rtypes.add_argument("-T", "--type_trans", action="append_const",
+                    const="type_transition", dest="tertypes",
+                    help="Search type_transition rules.")
+rtypes.add_argument("--type_change", action="append_const",
+                    const="type_change", dest="tertypes",
+                    help="Search type_change rules.")
+rtypes.add_argument("--type_member", action="append_const",
+                    const="type_member", dest="tertypes",
+                    help="Search type_member rules.")
+rbacrtypes = parser.add_argument_group("RBAC Rule Types")
+rbacrtypes.add_argument("--role_allow", action="append_const",
+                        const="allow", dest="rbacrtypes",
+                        help="Search role allow rules.")
+rbacrtypes.add_argument("--role_trans", action="append_const",
+                        const="role_transition", dest="rbacrtypes",
+                        help="Search role_transition rules.")
+
+mlsrtypes = parser.add_argument_group("MLS Rule Types")
+mlsrtypes.add_argument("--range_trans", action="append_const",
+                       const="range_transition", dest="mlsrtypes",
+                       help="Search range_transition rules.")
+
+expr = parser.add_argument_group("Expressions")
+expr.add_argument("-s", "--source",
+                  help="Source type/role of the TE/RBAC rule.")
+expr.add_argument("-t", "--target",
+                  help="Target type/role of the TE/RBAC rule.")
+expr.add_argument("-c", "--class", dest="tclass",
+                  help="Comma separated list of object classes")
+expr.add_argument("-p", "--perms", metavar="PERMS",
+                  help="Comma separated list of permissions.")
+expr.add_argument("-x", "--xperms", metavar="XPERMS",
+                  help="Comma separated list of extended permissions.")
+expr.add_argument("-D", "--default",
+                  help="Default of the rule. (type/role/range transition rules)")
+expr.add_argument("-b", "--bool", dest="boolean", metavar="BOOL",
+                  help="Comma separated list of Booleans in the conditional expression.")
+
+opts = parser.add_argument_group("Search options")
+opts.add_argument("-eb", action="store_true", dest="boolean_equal",
+                  help="Match Boolean list exactly instead of matching any listed Boolean.")
+opts.add_argument("-ep", action="store_true", dest="perms_equal",
+                  help="Match permission set exactly instead of matching any listed permission.")
+opts.add_argument("-ex", action="store_true", dest="xperms_equal",
+                  help="Match extended permission set exactly instead of matching any listed "
+                  "permission.")
+opts.add_argument("-ds", action="store_false", dest="source_indirect",
+                  help="Match source attributes directly instead of matching member types/roles.")
+opts.add_argument("-dt", action="store_false", dest="target_indirect",
+                  help="Match target attributes directly instead of matching member types/roles.")
+opts.add_argument("-rs", action="store_true", dest="source_regex",
+                  help="Use regular expression matching for the source type/role.")
+opts.add_argument("-rt", action="store_true", dest="target_regex",
+                  help="Use regular expression matching for the target type/role.")
+opts.add_argument("-rc", action="store_true", dest="tclass_regex",
+                  help="Use regular expression matching for the object class.")
+opts.add_argument("-rd", action="store_true", dest="default_regex",
+                  help="Use regular expression matching for the default type/role.")
+opts.add_argument("-rb", action="store_true", dest="boolean_regex",
+                  help="Use regular expression matching for Booleans.")
+
+args = parser.parse_args()
+
+if args.A:
+    try:
+        args.tertypes.extend(["allow", "allowxperm"])
+    except AttributeError:
+        args.tertypes = ["allow", "allowxperm"]
+
+if not args.tertypes and not args.mlsrtypes and not args.rbacrtypes:
+    parser.error("At least one rule type must be specified.")
+
+if args.debug:
+    logging.basicConfig(level=logging.DEBUG,
+                        format='%(asctime)s|%(levelname)s|%(name)s|%(message)s')
+elif args.verbose:
+    logging.basicConfig(level=logging.INFO, format='%(message)s')
+else:
+    logging.basicConfig(level=logging.WARNING, format='%(message)s')
+
+try:
+    p = setools.SELinuxPolicy(args.policy)
+
+    if args.tertypes:
+        q = setools.TERuleQuery(p,
+                                ruletype=args.tertypes,
+                                source=args.source,
+                                source_indirect=args.source_indirect,
+                                source_regex=args.source_regex,
+                                target=args.target,
+                                target_indirect=args.target_indirect,
+                                target_regex=args.target_regex,
+                                tclass_regex=args.tclass_regex,
+                                perms_equal=args.perms_equal,
+                                xperms_equal=args.xperms_equal,
+                                default=args.default,
+                                default_regex=args.default_regex,
+                                boolean_regex=args.boolean_regex,
+                                boolean_equal=args.boolean_equal)
+
+        # these are broken out from the above statement to prevent making a list
+        # with an empty string in it (split on empty string)
+        if args.tclass:
+            if args.tclass_regex:
+                q.tclass = args.tclass
+            else:
+                q.tclass = args.tclass.split(",")
+
+        if args.perms:
+            q.perms = args.perms.split(",")
+
+        if args.xperms:
+            xperms = []
+            for item in args.xperms.split(","):
+                rng = item.split("-")
+                if len(rng) == 2:
+                    xperms.append((int(rng[0], base=16), int(rng[1], base=16)))
+                elif len(rng) == 1:
+                    xperms.append((int(rng[0], base=16), int(rng[0], base=16)))
+                else:
+                    parser.error("Enter an extended permission or extended permission range, e.g. "
+                                 "0x5411 or 0x8800-0x88ff.")
+
+            q.xperms = xperms
+
+        if args.boolean:
+            if args.boolean_regex:
+                q.boolean = args.boolean
+            else:
+                q.boolean = args.boolean.split(",")
+
+        for r in sorted(q.results()):
+            print(r)
+
+    if args.rbacrtypes:
+        q = setools.RBACRuleQuery(p,
+                                  ruletype=args.rbacrtypes,
+                                  source=args.source,
+                                  source_indirect=args.source_indirect,
+                                  source_regex=args.source_regex,
+                                  target=args.target,
+                                  target_indirect=args.target_indirect,
+                                  target_regex=args.target_regex,
+                                  default=args.default,
+                                  default_regex=args.default_regex,
+                                  tclass_regex=args.tclass_regex)
+
+        # these are broken out from the above statement to prevent making a list
+        # with an empty string in it (split on empty string)
+        if args.tclass:
+            if args.tclass_regex:
+                q.tclass = args.tclass
+            else:
+                q.tclass = args.tclass.split(",")
+
+        for r in sorted(q.results()):
+            print(r)
+
+    if args.mlsrtypes:
+        q = setools.MLSRuleQuery(p,
+                                 ruletype=args.mlsrtypes,
+                                 source=args.source,
+                                 source_indirect=args.source_indirect,
+                                 source_regex=args.source_regex,
+                                 target=args.target,
+                                 target_indirect=args.target_indirect,
+                                 target_regex=args.target_regex,
+                                 tclass_regex=args.tclass_regex,
+                                 default=args.default)
+
+        # these are broken out from the above statement to prevent making a list
+        # with an empty string in it (split on empty string)
+        if args.tclass:
+            if args.tclass_regex:
+                q.tclass = args.tclass
+            else:
+                q.tclass = args.tclass.split(",")
+
+        for r in sorted(q.results()):
+            print(r)
+
+except Exception as err:
+    if args.debug:
+        logging.exception(str(err))
+    else:
+        print(err)
+
+    sys.exit(1)
diff --git a/python/audit2allow/audit2allow b/python/audit2allow/audit2allow
old mode 100644
new mode 100755
diff --git a/secilc/Android.bp b/secilc/Android.bp
new file mode 100644
index 0000000..022c74c
--- /dev/null
+++ b/secilc/Android.bp
@@ -0,0 +1,20 @@
+common_CFLAGS = [
+    "-Wall",
+    "-Werror",
+    "-Wshadow",
+]
+
+cc_binary {
+    name: "secilc",
+    host_supported: true,
+    cflags: common_CFLAGS,
+    srcs: ["secilc.c"],
+    target: {
+        android: {
+            static_libs: ["libsepol"],
+        },
+        host: {
+            shared_libs: ["libsepol"],
+        },
+    },
+}