Commits

Robert Craig  committed be60123

Rewrite SEAdmin b/c of DPMS deprecation.

DevicePolicyManagerService has dropped all
SELinux and MMAC interaction. This includes
enforcing status change, boolean support,
and policy file reload ability. This patch
set rewrites SEAdmin with these new constraints.

  • Participants
  • Parent commits 279e51a

Comments (0)

Files changed (19)

File AndroidManifest.xml

 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.seandroid_admin"
-    android:versionCode="1"
+    android:versionCode="2"
     android:versionName="1.0">
 
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
             </intent-filter>
         </activity>
 
-        <receiver android:name="SEAndroidAdmin$myDeviceAdminReceiver"
-                android:label="@string/device_admin_label"
-                android:description="@string/device_admin_desc"
-                android:permission="android.permission.BIND_DEVICE_ADMIN">
-            <meta-data android:name="android.app.device_admin"
-                       android:resource="@xml/device_admin" />
-            <intent-filter>
-                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
-            </intent-filter>
-        </receiver>
-
         <receiver android:name="BootReceiver">
           <intent-filter>
             <action android:name="android.intent.action.BOOT_COMPLETED" />

File res/layout/config_about_fragment.xml

+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="fill_parent"
+              android:layout_height="fill_parent"
+              android:orientation="vertical" >
+  <ScrollView android:id="@+id/ScrollView1"
+              android:layout_width="fill_parent"
+              android:layout_height="fill_parent">
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:padding="6dip"
+        >
+      <TextView
+          android:id="@+id/help_page_intro"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+          android:padding="2dip"
+          android:layout_weight="1"
+          />
+    </LinearLayout>
+  </ScrollView>
+</LinearLayout>

File res/layout/preference_header_switch_item.xml

-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2006 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<!-- Layout of a header item in PreferenceActivity. -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:minHeight="48dp"
-    android:background="?android:attr/activatedBackgroundIndicator"
-    android:gravity="center_vertical"
-    android:paddingRight="?android:attr/scrollbarSize">
-
-    <RelativeLayout
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginLeft="2dip"
-        android:layout_marginRight="6dip"
-        android:layout_marginTop="6dip"
-        android:layout_marginBottom="6dip"
-        android:layout_weight="1">
-
-        <TextView android:id="@+android:id/title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:singleLine="true"
-            android:textAppearance="?android:attr/textAppearanceMedium"
-            android:ellipsize="marquee"
-            android:fadingEdge="horizontal" />
-
-        <TextView android:id="@+android:id/summary"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_below="@android:id/title"
-            android:layout_alignLeft="@android:id/title"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:ellipsize="end"
-            android:maxLines="2" />
-
-    </RelativeLayout>
-
-   <Switch android:id="@+id/switchWidget"
-       android:layout_width="wrap_content"
-       android:layout_height="wrap_content"
-       android:layout_gravity="center"
-       android:padding="8dip"
-       android:focusable="false"
-       android:clickable="true" />
-
-</LinearLayout>

File res/values/strings.xml

     <string name="app_name">SEAdmin</string>
     <string name="activity_name">SEAdmin</string>
 
-    <string name="admin_category_title">Admin Support</string>
-
-    <string name="device_admin_label">SE Admin</string>
-    <string name="device_admin_desc">This app is the reference implementation
-        for an Device Admin app that can control SELinux and MMAC
-        settings and policies.</string>
-
-    <!-- Used in home page screen -->
-    <string name="selinux_disabled_fragment_title">SELinux Disabled</string>
-    <string name="selinux_not_enabled">SELinux is not enabled</string>
-
-    <string name="selinux_admin_title">SELinux Administration</string>
-    <string name="mmac_admin_title">MMAC Administration</string>
-    <string name="selinux_boolean_fragment_title">Booleans</string>
-
-    <!-- Used in Enforcing Fragment -->
-    <string name="enable_admins_cat">Enable admin</string>
-
-    <string name="device_admin_cb_title">Device administrator</string>
-    <string name="device_admin_cb_summary">Allow SEAdmin to become a Device Admin. This is required for the app to function.</string>
-
-    <string name="selinux_cat">SELinux capabilities</string>
-
-    <string name="selinux_admin_cb_title">SELinux admin</string>
-    <string name="selinux_admin_cb_summary">Allow SEAdmin to become a SELinux Admin. This is required for the app to set SELinux settings.</string>
-
-    <string name="selinux_reload_title">Reload SELinux policy</string>
-    <string name="selinux_restore_title">Restore default SELinux policy</string>
-
-    <string name="propertycontexts_reload_title">Reload Property Contexts policy</string>
-    <string name="propertycontexts_restore_title">Restore default Property Contexts policy</string>
-
-    <string name="filecontexts_reload_title">Reload File Contexts policy</string>
-    <string name="filecontexts_restore_title">Restore default File Contexts policy</string>
-
-    <string name="seappcontexts_reload_title">Reload SEApp Contexts policy</string>
-    <string name="seappcontexts_restore_title">Restore default SEApp Contexts policy</string>
-
-    <string name="seandroid_cat">MMAC capabilities</string>
-
-    <string name="mmac_admin_cb_title">MMAC admin</string>
-    <string name="mmac_admin_cb_summary">Allow SEAdmin to become a MMAC Admin. This is required for the app to set MMAC settings.</string>
-
-    <string name="mmac_reload_title">Reload MMAC policy</string>
-
-    <string name="mmac_restore_title">Restore default MMAC policy</string>
-
-    <string name="ext_storage_unavail">SDcard unavailable</string>
-
-    <!-- Used in Boolean Fragment -->
-    <string name="selinuxBooleans_title">SELinux Booleans</string>
-    <string name="selinuxBooleans_err_selinuxDisabled">SELinux is not enabled on this device.</string>
-    <string name="selinuxBooleans_err_notDeviceAdmin">SEAdmin is not yet a Device Admin. Go to the Enforcing screen to enable.</string>
-    <string name="selinuxBooleans_err_notSELinuxAdmin">SEAdmin is not yet a SELinux Admin. Go to the Enforcing screen to enable.</string>
-    <string name="selinuxBooleans_err_getBooleans">Error occurred while retrieving booleans</string>
-    <string name="selinuxBooleans_err_noBooleans">No SELinux booleans found</string>
-
     <!-- Update Categories -->
-    <string name="update_category_title">Update Options</string>
-    <string name="config_update_title">SELinux Policy</string>
-    <string name="config_update_mmac_title">Middleware Policy</string>
+    <string name="update_category_title">Policy Update Options</string>
     <string name="config_kernel_reload_title">Reload Kernel Policies</string>
+    <string name="config_update_mmac_title">Kernel and MMAC Policy</string>
     <string name="config_mmac_install_reload_title">Reload Install MAC Policy</string>
     <string name="config_eop_reload_title">Reload Eops Policy</string>
     <string name="config_ifw_reload_title">Reload Intent Firewall Policy</string>
     <string name="selinux_policy_reload">Reload Selinux Policies</string>
     <string name="mmac_policy_reload">Reload MMAC Policies</string>
 
+    <!-- About/Help Categories -->
+    <string name="about_category">About</string>
+    <string name="about_category_title">Info</string>
+    <string name="config_about_title">About</string>
+
+    <string name="about">    SEAdmin provides policy reload functionality utilizing AOSPs ConfigUpdater
+                         mechanism. Presently, it provides 4 types of policy reload options.\n\n
+
+                         * <font fgcolor="cyan">Kernel</font> : selinux policy updates for file_contexts,
+                         seapp_contexts, sepolicy and property_contexts files.\n
+                         * <font fgcolor="cyan">Install-time MAC</font> : policy to assign seinfo values to
+                         apps used for assigning SELinux security contexts and optionally to control
+                         whether an app can be installed at all. Applied via mac_permissions.xml file.\n
+                         * <font fgcolor="cyan">Eops</font> : policy supports enterprise controls over certain
+                         runtime application operations that build upon AppOps functionality. Applied via
+                         eops.xml file.\n
+                         * <font fgcolor="cyan">IntentFirewall</font> : policy to place restrictions on ICC.
+                         Ability to restrict starting activities, starting and stopping services, and
+                         broadcasting intents. Applied via ifw.xml file.\n\n
+
+                             All policy reloads are handled by supplying a signed bundle that must first be
+                         shipped to the device and then broadcast to interested receivers. Each interested
+                         receiver validates the bundle and then unpacks the policy files from it under
+                         /data/security. The intent firewall policy will actually get installed to
+                         /data/system/ifw though. A suite of tools are available to help in the construction of
+                         each type of policy bundle. For each type of policy reload option there is a
+                         corresponding buildbundle script that will build the correct policy bundle
+                         zip file and metadata file required by the ConfigUpdater mechanism. In order
+                         to utilize the suite of buildbundle tools for policy reloads you must first
+                         build each tool in turn; buildsebundle for kernel bundles, buildpermsbundle
+                         for install-time bundles, buildeopbundle for enterprise ops bundles, and
+                         buildifwbundle for intent firewall bundles. SEAdmin handles all broadcasts
+                         and policy triggers, only requiring a policy bundle placed on the sdcard
+                         in order to be applied.\n\n
+
+                             If you opened SEAdmim with the hope of toggling enfocing status or booleans,
+                         that functionality has been stripped. Recent policy advances have now
+                         restricted who can toggle SELinux enforcing mode and as such the ability
+                         has been dropped from SEAdmin. A similar change has recently appeared for
+                         our MMAC enforcing mode too. Since install-time MAC code is always in enforcing
+                         mode, SEAdmin\'s ability to switch that mode has been dropped. Further,
+                         SEAdmin\'s device admin hooks have been deprecated and as such all
+                         admin features including reload options through DPMS have been stripped.\n\n
+
+                         Please check external documentation at http://selinuxproject.org/page/SEAndroid
+                         for further details concerning SEAdmin and policy reload abilities.
+    </string>
+
 </resources>

File res/xml/config_update_fragment.xml

-<?xml version="1.0" encoding="utf-8"?>
-<PreferenceScreen
-    xmlns:android="http://schemas.android.com/apk/res/android">
-
-  <PreferenceCategory
-      android:title="@string/selinux_policy_reload">
-
-<!--
-    <CheckBoxPreference android:id="@+id/enforcing"
-                        android:key="key_enforcing"
-                        android:title="@string/enforcing_title" />
--->
-    <Preference android:id="@+id/kernel_config_reload"
-                android:title="@string/config_kernel_reload_title"
-                android:key="key_kernel_reload" />
-
-<!--
-    <Preference android:id="@+id/mmac_config_reload"
-                android:title="@string/config_mmac_reload_title"
-                android:key="key_mmac_reload" />
--->
-    </PreferenceCategory>
-
-</PreferenceScreen>

File res/xml/config_update_mmac_fragment.xml

     xmlns:android="http://schemas.android.com/apk/res/android">
 
   <PreferenceCategory
+      android:title="@string/selinux_policy_reload">
+
+    <Preference android:id="@+id/kernel_config_reload"
+                android:title="@string/config_kernel_reload_title"
+                android:key="key_selinux_reload" />
+
+  </PreferenceCategory>
+
+  <PreferenceCategory
       android:title="@string/mmac_policy_reload">
 
     <Preference android:id="@+id/mac_perms_config_reload"

File res/xml/device_admin.xml

-<?xml version="1.0" encoding="utf-8"?>
-
-<!-- BEGIN_INCLUDE(meta_data) -->
-<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
-    <uses-policies>
-        <enforce-selinux />
-        <enforce-mmac />
-    </uses-policies>
-</device-admin>
-<!-- END_INCLUDE(meta_data) -->

File res/xml/disabled_headers.xml

-<?xml version="1.0" encoding="utf-8"?>
-<preference-headers
-    xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <header
-        android:title="@string/selinux_disabled_fragment_title">
-    </header>
-
-</preference-headers>

File res/xml/enabled_headers.xml

 <preference-headers
     xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <header android:id="@+id/admin_category"
-        android:title="@string/admin_category_title"/>
-
-    <header android:id="@+id/enable_device_admin"
-        android:title="@string/device_admin_cb_title"
-        android:summary="@string/device_admin_cb_summary" />
-
-    <header android:id="@+id/selinux_admin"
-        android:fragment="com.android.seandroid_admin.SELinuxEnforcingFragment"
-        android:title="@string/selinux_admin_title" />
-
-    <header android:id="@+id/mmac_admin"
-        android:fragment="com.android.seandroid_admin.MMACFragment"
-        android:title="@string/mmac_admin_title" />
-
+    <!-- Policy Update Fragment -->
     <header android:id="@+id/update_category"
         android:title="@string/update_category_title" />
 
-    <header android:id="@+id/config_update"
-        android:fragment="com.android.seandroid_admin.ConfigUpdateFragment"
-        android:title="@string/config_update_title" />
-
     <header android:id="@+id/config_update_mmac"
-        android:fragment="com.android.seandroid_admin.ConfigUpdateMmacFragment"
+        android:fragment="com.android.seandroid_admin.ConfigUpdateFragment"
         android:title="@string/config_update_mmac_title" />
 
+    <!-- About Fragment -->
+    <header android:id="@+id/about_category"
+        android:title="@string/about_category_title" />
+
+    <header android:id="@+id/config_about"
+        android:fragment="com.android.seandroid_admin.AboutFragment"
+        android:title="@string/config_about_title" />
+
 </preference-headers>

File res/xml/mmac_fragment.xml

-<?xml version="1.0" encoding="utf-8"?>
-<PreferenceScreen
-    xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <PreferenceCategory
-        android:title="@string/seandroid_cat">
-
-        <Preference
-            android:title="@string/mmac_reload_title"
-            android:key="key_mmac_reload" />
-
-        <Preference
-            android:title="@string/mmac_restore_title"
-            android:key="key_mmac_restore" />
-
-    </PreferenceCategory>
-
-</PreferenceScreen>

File res/xml/selinux_enforcing_fragment.xml

-<?xml version="1.0" encoding="utf-8"?>
-<PreferenceScreen
-    xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <PreferenceCategory
-        android:title="@string/selinux_cat">
-
-        <Preference android:id="@+id/selinux_reload"
-            android:title="@string/selinux_reload_title"
-            android:key="key_selinux_reload" />
-
-        <Preference android:id="@+id/selinux_restore"
-            android:title="@string/selinux_restore_title"
-            android:key="key_selinux_restore" />
-
-        <Preference android:id="@+id/propertycontexts_reload"
-            android:title="@string/propertycontexts_reload_title"
-            android:key="key_propertycontexts_reload" />
-
-        <Preference android:id="@+id/propertycontexts_restore"
-            android:title="@string/propertycontexts_restore_title"
-            android:key="key_propertycontexts_restore" />
-
-        <Preference android:id="@+id/filecontexts_reload"
-            android:title="@string/filecontexts_reload_title"
-            android:key="key_filecontexts_reload" />
-
-        <Preference android:id="@+id/filecontexts_restore"
-            android:title="@string/filecontexts_restore_title"
-            android:key="key_filecontexts_restore" />
-
-        <Preference android:id="@+id/seappcontexts_reload"
-            android:title="@string/seappcontexts_reload_title"
-            android:key="key_seappcontexts_reload" />
-
-        <Preference android:id="@+id/seappcontexts_restore"
-            android:title="@string/seappcontexts_restore_title"
-            android:key="key_seappcontexts_restore" />
-
-    </PreferenceCategory>
-
-    <PreferenceCategory
-        android:title="@string/selinux_boolean_fragment_title">
-    </PreferenceCategory>
-
-</PreferenceScreen>

File src/com/android/seandroid_admin/AboutFragment.java

+package com.android.seandroid_admin;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.widget.TextView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.seandroid_admin.R;
+
+public class AboutFragment extends Fragment {
+
+    private static String TAG = "SEAdminAboutFragment";
+
+    static TextView about;
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                                 Bundle savedInstanceState) {
+        View view = inflater.inflate(R.layout.config_about_fragment,
+                                     container, false);
+
+        about = (TextView)view.findViewById(R.id.help_page_intro);
+        about.setText(R.string.about);
+
+        return view;
+    }
+}

File src/com/android/seandroid_admin/ConfigUpdateFragment.java

 import com.android.seandroid_admin.R;
 
 /**
- * Mock sepolicy updates using new ConfigUpdateInstallReceiver mechanism.
- * Code will help explore the new ConfigUpdate mechanism regarding selinux
- * policies. Ideally, you'll want to use the host based buildsebundle
+ * Mock policy updates using new ConfigUpdateInstallReceiver mechanism.
+ * Code will help explore the new ConfigUpdate mechanism regarding mmac
+ * and kernel policies. Ideally, you'll want to use the host based build*bundle
  * tool to first generate the zip file that will contain both the
  * approved OTA bundle format as well as the metadata file. The zip
  * is expected to be pushed to /sdcard. In a production environment
- * you'll clearly want to change the location as well as deal with
- * other various concurrency issues outlined below.
+ * you'll clearly want to change the location.
  */
 public class ConfigUpdateFragment extends PreferenceFragment implements
         OnPreferenceChangeListener, OnPreferenceClickListener {
     // Connection back out to the main fragment.
     protected SEAndroidAdminActivity mActivity;
 
-    // Policy file locations
-    private File mBundleFile = null;
-    private File mMetadataFile = null;
-    private File mZipFile = null;
-
-    // Preference elements to toggle policy values.
-    private Preference mReloadSELinux;
-    private CheckBoxPreference mEnforcingCheckbox;
-
-    // Keys from xml layout. Refer to preference elements.
-    private static final String KEY_RELOAD_SELINUX = "key_kernel_reload";
-    private static final String KEY_RELOAD_MMAC = "key_mmac_reload";
-    private static final String KEY_ENFORCING = "key_enforcing";
-
-    // Names of selinux policy bundles/files.
-    private static final String SEBUNDLE_FILE_PATH = "selinux_bundle";
-    private static final String SEMETADATA_FILE_PATH = "selinux_bundle_metadata";
-    private static final String SEBUNDLE_ZIP_PATH = "selinux_bundle.zip";
-
-    // Names of zip file entries. Derived from buildbundle tool generation.
-    private static final String ZIP_BUNDLE_ENTRY = "update_bundle";
-    private static final String ZIP_METADATA_ENTRY = "update_bundle_metadata";
-
-    // Map zip file entries to filesystem names. Useful when unzipping.
-    private static final Map<String, File> zipMap = new HashMap<String, File>(2);
-
     @Override
     public void onAttach(Activity activity) {
         super.onAttach(activity);
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
 
-        addPreferencesFromResource(R.xml.config_update_fragment);
+        addPreferencesFromResource(R.xml.config_update_mmac_fragment);
 
-        // The zip file entries get written to cache.
-        final File cacheDir = Environment.getDownloadCacheDirectory();
-        if (cacheDir != null) {
-            mBundleFile = new File(cacheDir, SEBUNDLE_FILE_PATH);
-            mMetadataFile = new File(cacheDir, SEMETADATA_FILE_PATH);
-        } else {
-            Log.e(TAG, "Download cache directory not found. " +
-                  "Policy updates won't work.");
-        }
+        new MacInstallConfigUpdate();
+        new EopsConfigUpdate();
+        new IfwConfigUpdate();
+        new SELinuxUpdate();
+    }
 
-        // The zip file is expected to live on sdcard.
-        // This location will likely need to change to a more secure
-        // spot when on an actually operational device.
-        final File extDir = Environment.getExternalStorageDirectory();
-        if (extDir != null) {
-            mZipFile = new File(extDir, SEBUNDLE_ZIP_PATH);
-        } else {
-            Log.e(TAG, "External storage directory not found. " +
-                  "Policy updates won't work.");
-        }
+    @Override
+    public void onStart() {
+        super.onStart();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        return true;
+    }
+
+    @Override
+    public boolean onPreferenceClick(Preference preference) {
+        return true;
+    }
+
+    private class ConfigUpdate {
+
+        private File mBundleFile = null;
+        private File mMetadataFile = null;
+        private File mZipFile = null;
+
+        private final Preference mReload;
+        private final String mKeyReload;
+        private final String mIntent;
+
+        private final String mBundleFilePath;
+        private final String mMetadataFilePath;
+        private final String mBundleZipPath;
 
-        // Map zip entries to writable filesystem locations.
-        zipMap.put(ZIP_BUNDLE_ENTRY, mBundleFile);
-        zipMap.put(ZIP_METADATA_ENTRY, mMetadataFile);
-
-        // Setup and listen when the 'Reload Policies' preference is triggered.
-        mReloadSELinux = getPreferenceScreen().findPreference(KEY_RELOAD_SELINUX);
-        mReloadSELinux.setOnPreferenceClickListener(new OnPreferenceClickListener() {
-            @Override
-            public boolean onPreferenceClick(Preference preference) {
-                Log.d(TAG, "Loading of policy bundle requested.");
-
-                // Todo: assurance that this preference isn't quickly double clicked.
-                // Todo: gurantee that the bundle hasn't been replaced or written
-                // to by the time this is called.
-
-                FileInputStream fis = null;
-                ZipInputStream zis = null;
-                // Maybe put in own thread....
-                try {
-                    fis = new FileInputStream(mZipFile);
-                    zis = new ZipInputStream(fis);
-                    ZipEntry ze;
-                    while ((ze = zis.getNextEntry()) != null) {
-                        String name = ze.getName();
-                        if (zipMap.containsKey(name)) {
-                            File output = zipMap.get(name);
-                            try {
-                                ByteArrayOutputStream baos = new ByteArrayOutputStream();
-                                byte[] data = new byte[1024];
-                                int n;
-                                while ((n = zis.read(data)) != -1) {
-                                    baos.write(data, 0, n);
+        private static final String ZIP_METADATA_ENTRY = "update_bundle_metadata";
+        private static final String ZIP_BUNDLE_ENTRY = "update_bundle";
+
+        private final Map<String, File> mZipMap = new HashMap<String, File>(2);
+
+        ConfigUpdate(String name, String intent) {
+            mKeyReload = "key_" + name + "_reload";
+            mBundleFilePath = name + "_bundle";
+            mMetadataFilePath = name + "_bundle_metadata";
+            mBundleZipPath = name + "_bundle.zip";
+
+            mIntent = "android.intent.action." + intent;
+
+            final File cacheDir = Environment.getDownloadCacheDirectory();
+            if (cacheDir != null) {
+                mBundleFile = new File(cacheDir, mBundleFilePath);
+                mMetadataFile = new File(cacheDir, mMetadataFilePath);
+            } else {
+                Log.e(TAG, "Download cache directory not found. " +
+                      "Policy updates won't work.");
+            }
+
+            mZipMap.put(ZIP_BUNDLE_ENTRY, mBundleFile);
+            mZipMap.put(ZIP_METADATA_ENTRY, mMetadataFile);
+
+            final File extDir = Environment.getExternalStorageDirectory();
+            if (extDir != null) {
+                mZipFile = new File(extDir, mBundleZipPath);
+            } else {
+                Log.e(TAG, "External storage directory not found. " +
+                      "Policy updates won't work.");
+            }
+
+            mReload = getPreferenceScreen().findPreference(mKeyReload);
+            mReload.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+                @Override
+                public boolean onPreferenceClick(Preference preference) {
+                    Log.d(TAG, "Loading of policy bundle requested.");
+
+                    FileInputStream fis = null;
+                    ZipInputStream zis = null;
+                    try {
+                        fis = new FileInputStream(mZipFile);
+                        zis = new ZipInputStream(fis);
+                        ZipEntry ze;
+                        while ((ze = zis.getNextEntry()) != null) {
+                            String name = ze.getName();
+                            if (mZipMap.containsKey(name)) {
+                                File output = mZipMap.get(name);
+                                try {
+                                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                                    byte[] data = new byte[1024];
+                                    int n;
+                                    while ((n = zis.read(data)) != -1) {
+                                        baos.write(data, 0, n);
+                                    }
+                                    atomicWriteToFile(cacheDir, output, baos.toByteArray());
+                                } catch (IOException ioex) {
+                                    throw new Exception("Failed extracting policy zip. " + ioex);
+                                } finally {
+                                    zis.closeEntry();
                                 }
-                                atomicWriteToFile(cacheDir, output, baos.toByteArray());
-                            } catch (IOException ioex) {
-                                throw new Exception("Failed extracting policy zip. " + ioex);
-                            } finally {
-                                zis.closeEntry();
                             }
                         }
+
+                        // Open the metadata file.
+                        Scanner scan = new Scanner(mMetadataFile);
+                        scan.useDelimiter(":");
+                        String requiredHash = scan.next();
+                        String signature = scan.next();
+                        String version = scan.next();
+
+                        // Build the intent to broadcast.
+                        Intent i = new Intent(mIntent);
+                        i.putExtra("CONTENT_PATH", mBundleFile.getPath());
+                        i.putExtra("REQUIRED_HASH", requiredHash);
+                        i.putExtra("SIGNATURE", signature);
+                        i.putExtra("VERSION", version);
+
+                        Log.d(TAG, mIntent + " intent being broadcast.");
+                        mActivity.sendBroadcast(i);
+
+                    } catch (Exception ex) {
+                        Toast.makeText(mActivity, ex.toString(), Toast.LENGTH_SHORT).show();
+                        mBundleFile.delete();
+                        mMetadataFile.delete();
+                        Log.e(TAG, "Exception loading policy.", ex);
+                    } finally {
+                        if (zis != null) Closeables.closeQuietly(zis);
+                        if (fis != null) Closeables.closeQuietly(fis);
                     }
 
-                    // Open the metadata file.
-                    Scanner scan = new Scanner(mMetadataFile);
-                    scan.useDelimiter(":");
-                    String requiredHash = scan.next();
-                    String signature = scan.next();
-                    String version = scan.next();
-
-                    // Build the intent to broadcast.
-                    Intent i = new Intent("android.intent.action.UPDATE_SEPOLICY");
-                    i.putExtra("CONTENT_PATH", mBundleFile.getPath());
-                    i.putExtra("REQUIRED_HASH", requiredHash);
-                    i.putExtra("SIGNATURE", signature);
-                    i.putExtra("VERSION", version);
-
-                    Log.d(TAG, "UPDATE_SEPOLICY intent being broadcast.");
-                    mActivity.sendBroadcast(i);
-
-                } catch (Exception ex) {
-                    Toast.makeText(mActivity, ex.toString(), Toast.LENGTH_SHORT).show();
-                    mBundleFile.delete();
-                    mMetadataFile.delete();
-                    Log.e(TAG, "Exception loading policy.", ex);
-                } finally {
-                    if (zis != null) Closeables.closeQuietly(zis);
-                    if (fis != null) Closeables.closeQuietly(fis);
+                    return true;
                 }
-
-                return true;
+            });
+
+            String mes = "Load from: ";
+            if (cacheDir == null || extDir == null) {
+                String where = (extDir == null) ? "sdcard" : "cache";
+                mes += "Can't locate " + where + ". Policy location error.";
+                mReload.setEnabled(false);
+                mReload.setSelectable(false);
+            } else {
+                mes += mZipFile.getPath();
             }
-        });
-
-        String mes = "Load from: ";
-        if (cacheDir == null || extDir == null) {
-            String where = (extDir == null) ? "sdcard" : "cache";
-            mes += "Can't locate " + where + ". Policy location error.";
-            mReloadSELinux.setEnabled(false);
-            mReloadSELinux.setSelectable(false);
-        } else {
-            mes += mZipFile.getPath();
+            mReload.setSummary(mes);
         }
-        mReloadSELinux.setSummary(mes);
-    }
 
-    private void atomicWriteToFile(File dir, File file, byte[] content) throws IOException {
-        FileOutputStream out = null;
-        File tmp = null;
-        try {
-            tmp = File.createTempFile("journal", "", dir);
-            tmp.setReadable(true, false);
-            out = new FileOutputStream(tmp);
-            out.write(content);
-            out.getFD().sync();
-            if (!tmp.renameTo(file)) {
-                throw new IOException("Failed to atomically rename " + file.getCanonicalPath());
-            }
-        } finally {
-            if (tmp != null) {
-                tmp.delete();
+        private void atomicWriteToFile(File dir, File file, byte[] content) throws IOException {
+            FileOutputStream out = null;
+            File tmp = null;
+            try {
+                tmp = File.createTempFile("journal", "", dir);
+                tmp.setReadable(true, false);
+                out = new FileOutputStream(tmp);
+                out.write(content);
+                out.getFD().sync();
+                if (!tmp.renameTo(file)) {
+                    throw new IOException("Failed to atomically rename " + file.getCanonicalPath());
+                }
+            } finally {
+                if (tmp != null) {
+                    tmp.delete();
+                }
+                Closeables.closeQuietly(out);
             }
-            Closeables.closeQuietly(out);
         }
     }
 
-    @Override
-    public void onStart() {
-        super.onStart();
+    private class MacInstallConfigUpdate extends ConfigUpdate {
+        MacInstallConfigUpdate() {
+            super("mac_perms", "UPDATE_MAC_PERMS");
+        }
     }
 
-    @Override
-    public void onResume() {
-        super.onResume();
+    private class EopsConfigUpdate extends ConfigUpdate {
+        EopsConfigUpdate() {
+            super("eops", "UPDATE_EOPS");
+        }
     }
 
-    @Override
-    public boolean onPreferenceChange(Preference preference, Object newValue) {
-        return true;
+    private class IfwConfigUpdate extends ConfigUpdate {
+        IfwConfigUpdate() {
+            super("ifw", "UPDATE_IFW");
+        }
     }
 
-    @Override
-    public boolean onPreferenceClick(Preference preference) {
-        return true;
+    private class SELinuxUpdate extends ConfigUpdate {
+        SELinuxUpdate() {
+            super("selinux", "UPDATE_SEPOLICY");
+        }
     }
 }

File src/com/android/seandroid_admin/ConfigUpdateMmacFragment.java

-package com.android.seandroid_admin;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.res.AssetManager;
-import android.os.Bundle;
-import android.os.Environment;
-import android.preference.CheckBoxPreference;
-import android.preference.Preference;
-import android.preference.PreferenceFragment;
-import android.preference.Preference.OnPreferenceChangeListener;
-import android.preference.Preference.OnPreferenceClickListener;
-import android.provider.Settings;
-import android.util.Log;
-import android.widget.Toast;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-import com.google.common.io.Closeables;
-import com.google.common.io.Files;
-import com.google.common.io.ByteStreams;
-
-import com.android.seandroid_admin.R;
-
-/**
- * Mock sepolicy updates using new ConfigUpdateInstallReceiver mechanism.
- * Code will help explore the new ConfigUpdate mechanism regarding mmac
- * policies. Ideally, you'll want to use the host based build*bundle
- * tool to first generate the zip file that will contain both the
- * approved OTA bundle format as well as the metadata file. The zip
- * is expected to be pushed to /sdcard. In a production environment
- * you'll clearly want to change the location as well as deal with
- * other various concurrency issues outlined below.
- */
-public class ConfigUpdateMmacFragment extends PreferenceFragment implements
-        OnPreferenceChangeListener, OnPreferenceClickListener {
-
-    private static String TAG = "SEAdminConfigUpdateMmacFragment";
-
-    // Connection back out to the main fragment.
-    protected SEAndroidAdminActivity mActivity;
-
-    @Override
-    public void onAttach(Activity activity) {
-        super.onAttach(activity);
-        mActivity = (SEAndroidAdminActivity) activity;
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-    }
-
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
-
-        addPreferencesFromResource(R.xml.config_update_mmac_fragment);
-
-        new MacInstallConfigUpdate();
-        new EopsConfigUpdate();
-        new IfwConfigUpdate();
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-    }
-
-    @Override
-    public boolean onPreferenceChange(Preference preference, Object newValue) {
-        return true;
-    }
-
-    @Override
-    public boolean onPreferenceClick(Preference preference) {
-        return true;
-    }
-
-    private class ConfigUpdate {
-
-        private File mBundleFile = null;
-        private File mMetadataFile = null;
-        private File mZipFile = null;
-
-        private final Preference mReload;
-        private final String mKeyReload;
-        private final String mIntent;
-
-        private final String mBundleFilePath;
-        private final String mMetadataFilePath;
-        private final String mBundleZipPath;
-
-        private static final String ZIP_METADATA_ENTRY = "update_bundle_metadata";
-        private static final String ZIP_BUNDLE_ENTRY = "update_bundle";
-
-        private final Map<String, File> mZipMap = new HashMap<String, File>(2);
-
-        ConfigUpdate(String name, String intent) {
-            mKeyReload = "key_" + name + "_reload";
-            mBundleFilePath = name + "_bundle";
-            mMetadataFilePath = name + "_bundle_metadata";
-            mBundleZipPath = name + "_bundle.zip";
-
-            mIntent = "android.intent.action." + intent;
-
-            final File cacheDir = Environment.getDownloadCacheDirectory();
-            if (cacheDir != null) {
-                mBundleFile = new File(cacheDir, mBundleFilePath);
-                mMetadataFile = new File(cacheDir, mMetadataFilePath);
-            } else {
-                Log.e(TAG, "Download cache directory not found. " +
-                      "Policy updates won't work.");
-            }
-
-            mZipMap.put(ZIP_BUNDLE_ENTRY, mBundleFile);
-            mZipMap.put(ZIP_METADATA_ENTRY, mMetadataFile);
-
-            final File extDir = Environment.getExternalStorageDirectory();
-            if (extDir != null) {
-                mZipFile = new File(extDir, mBundleZipPath);
-            } else {
-                Log.e(TAG, "External storage directory not found. " +
-                      "Policy updates won't work.");
-            }
-
-            mReload = getPreferenceScreen().findPreference(mKeyReload);
-            mReload.setOnPreferenceClickListener(new OnPreferenceClickListener() {
-                @Override
-                public boolean onPreferenceClick(Preference preference) {
-                    Log.d(TAG, "Loading of policy bundle requested.");
-
-                    FileInputStream fis = null;
-                    ZipInputStream zis = null;
-                    try {
-                        fis = new FileInputStream(mZipFile);
-                        zis = new ZipInputStream(fis);
-                        ZipEntry ze;
-                        while ((ze = zis.getNextEntry()) != null) {
-                            String name = ze.getName();
-                            if (mZipMap.containsKey(name)) {
-                                File output = mZipMap.get(name);
-                                try {
-                                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
-                                    byte[] data = new byte[1024];
-                                    int n;
-                                    while ((n = zis.read(data)) != -1) {
-                                        baos.write(data, 0, n);
-                                    }
-                                    atomicWriteToFile(cacheDir, output, baos.toByteArray());
-                                } catch (IOException ioex) {
-                                    throw new Exception("Failed extracting policy zip. " + ioex);
-                                } finally {
-                                    zis.closeEntry();
-                                }
-                            }
-                        }
-
-                        // Open the metadata file.
-                        Scanner scan = new Scanner(mMetadataFile);
-                        scan.useDelimiter(":");
-                        String requiredHash = scan.next();
-                        String signature = scan.next();
-                        String version = scan.next();
-
-                        // Build the intent to broadcast.
-                        Intent i = new Intent(mIntent);
-                        i.putExtra("CONTENT_PATH", mBundleFile.getPath());
-                        i.putExtra("REQUIRED_HASH", requiredHash);
-                        i.putExtra("SIGNATURE", signature);
-                        i.putExtra("VERSION", version);
-
-                        Log.d(TAG, mIntent + " intent being broadcast.");
-                        mActivity.sendBroadcast(i);
-
-                    } catch (Exception ex) {
-                        Toast.makeText(mActivity, ex.toString(), Toast.LENGTH_SHORT).show();
-                        mBundleFile.delete();
-                        mMetadataFile.delete();
-                        Log.e(TAG, "Exception loading policy.", ex);
-                    } finally {
-                        if (zis != null) Closeables.closeQuietly(zis);
-                        if (fis != null) Closeables.closeQuietly(fis);
-                    }
-
-                    return true;
-                }
-            });
-
-            String mes = "Load from: ";
-            if (cacheDir == null || extDir == null) {
-                String where = (extDir == null) ? "sdcard" : "cache";
-                mes += "Can't locate " + where + ". Policy location error.";
-                mReload.setEnabled(false);
-                mReload.setSelectable(false);
-            } else {
-                mes += mZipFile.getPath();
-            }
-            mReload.setSummary(mes);
-        }
-
-        private void atomicWriteToFile(File dir, File file, byte[] content) throws IOException {
-            FileOutputStream out = null;
-            File tmp = null;
-            try {
-                tmp = File.createTempFile("journal", "", dir);
-                tmp.setReadable(true, false);
-                out = new FileOutputStream(tmp);
-                out.write(content);
-                out.getFD().sync();
-                if (!tmp.renameTo(file)) {
-                    throw new IOException("Failed to atomically rename " + file.getCanonicalPath());
-                }
-            } finally {
-                if (tmp != null) {
-                    tmp.delete();
-                }
-                Closeables.closeQuietly(out);
-            }
-        }
-    }
-
-    private class MacInstallConfigUpdate extends ConfigUpdate {
-        MacInstallConfigUpdate() {
-            super("mac_perms", "UPDATE_MAC_PERMS");
-        }
-    }
-
-    private class EopsConfigUpdate extends ConfigUpdate {
-        EopsConfigUpdate() {
-            super("eops", "UPDATE_EOPS");
-        }
-    }
-
-    private class IfwConfigUpdate extends ConfigUpdate {
-        IfwConfigUpdate() {
-            super("ifw", "UPDATE_IFW");
-        }
-    }
-}

File src/com/android/seandroid_admin/MMACFragment.java

-package com.android.seandroid_admin;
-
-import android.app.admin.DevicePolicyManager;
-import android.os.Bundle;
-import android.os.SystemProperties;
-import android.preference.CheckBoxPreference;
-import android.preference.Preference;
-import android.preference.Preference.OnPreferenceChangeListener;
-import android.preference.Preference.OnPreferenceClickListener;
-import android.util.Log;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.google.common.io.Files;
-
-import java.io.File;
-import java.io.IOException;
-
-public class MMACFragment extends SEAndroidAdminFragment
-        implements OnPreferenceChangeListener {
-    public static final String TAG = "MMACfragment";
-
-    private static final String KEY_MMAC_RELOAD = "key_mmac_reload";
-    private static final String KEY_MMAC_RESTORE = "key_mmac_restore";
-
-    private static final String MMAC_POLICY_FILE = "mac_permissions.xml";
-
-    private CheckBoxPreference mMMACenforceCheckbox;
-    private Preference mMMACreload;
-    private Preference mMMACrestore;
-
-    private File mMMACpolicyFile = null;
-
-    private TextView mEmptyView;
-
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
-        mAdmin.updateMMACstate();
-
-        addPreferencesFromResource(R.xml.mmac_fragment);
-
-        mEmptyView = (TextView) getView().findViewById(android.R.id.empty);
-        getListView().setEmptyView(mEmptyView);
-
-        if (!mAdmin.isDeviceAdmin) {
-            addMessagePreference("not device admin");
-
-        } else if (!mAdmin.isMMACadmin) {
-            addMessagePreference("not mmac admin");
-
-        } else {
-            File extFileDir = mActivity.getExternalFilesDir(null);
-            if (extFileDir != null) {
-                mMMACpolicyFile = new File(extFileDir, MMAC_POLICY_FILE);
-            }
-
-            mMMACreload =
-                    getPreferenceScreen().findPreference(KEY_MMAC_RELOAD);
-            mMMACreload.setOnPreferenceClickListener(new OnPreferenceClickListener() {
-                @Override
-                public boolean onPreferenceClick(Preference preference) {
-                    Log.v(TAG, "Reload of MMAC policy requested");
-                    try {
-                        byte[] policy = Files.toByteArray(mMMACpolicyFile);
-                        if (!mAdmin.mDPM.setCustomPolicyFile(mAdmin.mDeviceAdmin,
-                                DevicePolicyManager.MMAC_POLICY_FILE, policy)) {
-                            Toast.makeText(mActivity, "Unable to set policy", Toast.LENGTH_SHORT).show();
-                        } else {
-                            Toast.makeText(mActivity, "Success", Toast.LENGTH_SHORT).show();
-                        }
-                    } catch (IOException ioex) {
-                        Log.e(TAG, "Exception ocurred", ioex);
-                        Toast.makeText(mActivity, ioex.toString(), Toast.LENGTH_SHORT).show();
-                    }
-                    return false;
-                }
-            });
-
-            mMMACrestore =
-                    getPreferenceScreen().findPreference(KEY_MMAC_RESTORE);
-            mMMACrestore.setOnPreferenceClickListener(new OnPreferenceClickListener() {
-                @Override
-                public boolean onPreferenceClick(Preference preference) {
-                    Log.v(TAG, "Delete custom MMAC policy requested");
-                    if (!mAdmin.mDPM.setCustomPolicyFile(mAdmin.mDeviceAdmin,
-                            DevicePolicyManager.MMAC_POLICY_FILE, null)) {
-                        Toast.makeText(mActivity, "Unable to remove custom policy", Toast.LENGTH_SHORT).show();
-                    } else {
-                        Toast.makeText(mActivity, "Success", Toast.LENGTH_SHORT).show();
-                    }
-                    return false;
-                }
-            });
-        }
-    }
-
-    @Override
-    public void onResume() {
-        // XXX Unfortunately, both super.onResume and updateViews will update
-        // the Admin state to the same thing.
-        super.onResume();
-        updateViews();
-    }
-
-    @Override
-    public boolean onPreferenceChange(Preference preference, Object newValue) {
-        if (super.onPreferenceChange(preference, newValue)) {
-            return true;
-        }
-
-        return false;
-    }
-
-    private void updateViews() {
-        if (TRACE_UPDATE) { Log.v(TAG, "UPDATE updateViews()"); }
-
-        if (mAdmin.isMMACadmin) {
-            mMMACreload.setEnabled(true);
-            if (mMMACpolicyFile != null) {
-                mMMACreload.setSummary(mMMACpolicyFile.getPath());
-            } else {
-                mMMACreload.setSummary(R.string.ext_storage_unavail);
-            }
-
-            mMMACrestore.setEnabled(true);
-        }
-    }
-
-    private void addMessagePreference(int messageId) {
-        if (mEmptyView != null) mEmptyView.setText(messageId);
-        getPreferenceScreen().removeAll();
-    }
-
-    private void addMessagePreference(String message) {
-        if (mEmptyView != null) mEmptyView.setText(message);
-        getPreferenceScreen().removeAll();
-    }
-}

File src/com/android/seandroid_admin/SEAndroidAdmin.java

-package com.android.seandroid_admin;
-
-import android.app.admin.DeviceAdminReceiver;
-import android.app.admin.DevicePolicyManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Log;
-
-/**
- * Keeps track of the state of the Device Admin system.
- */
-public class SEAndroidAdmin {
-    public static final String TAG = "SEAdmin";
-    public static final boolean TRACE_UPDATE = false; // traces some of the update method call chains
-
-    public static final int REQUEST_CODE_ENABLE_ADMIN = 1;
-
-    // XXX Maybe this should be split out to do GUI-like stuff for callbacks.
-    public static class myDeviceAdminReceiver extends DeviceAdminReceiver { }
-
-    final SEAndroidAdminActivity mActivity;
-    final DevicePolicyManager mDPM;
-    final ComponentName mDeviceAdmin;
-
-    /** True if we are an active device administrator */
-    boolean isDeviceAdmin;
-
-    /** True if we are a SELinux admin */
-    boolean isSELinuxAdmin;
-
-    /** True if we are a MMAC admin */
-    boolean isMMACadmin;
-
-    SEAndroidAdmin(SEAndroidAdminActivity activity) {
-        mActivity = activity;
-        mDPM = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
-        mDeviceAdmin = new ComponentName(activity, myDeviceAdminReceiver.class);
-        updateState();
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder(TAG+'@'+Integer.toHexString(super.hashCode())+"={");
-        sb.append("isDeviceAdmin : "+isDeviceAdmin+", ");
-        sb.append("isSELinuxAdmin : "+isSELinuxAdmin+", ");
-        sb.append("isMMACadmin : "+isMMACadmin+", ");
-        sb.delete(sb.length()-2, sb.length()); //delete comma
-        sb.append('}');
-        return sb.toString();
-    }
-
-    /**
-     * Be very careful about using this. This only exists because removeActiveAdmin()
-     * defers removal of this admin from the admins list until later and returns
-     * almost immediately. So this code could execute before the list removal is executed,
-     * resulting in isActiveAdmin() returning true. To mitigate this, we set mAdminActive
-     * to the value we want and proceed with updating state as normal.
-     */
-    void setAdminState(boolean value) {
-        if (TRACE_UPDATE) { Log.v(TAG, "UPDATE setAdminState(value="+value+")"); }
-        boolean old = isDeviceAdmin;
-        isDeviceAdmin = value;
-        if (old != isDeviceAdmin) { Log.v(TAG, "mAdminActive: " + old + " -> " + isDeviceAdmin); }
-        updateSELinuxState();
-        updateMMACstate();
-    }
-
-    void updateState() {
-        updateDeviceAdminState();
-        updateSELinuxState();
-        updateMMACstate();
-    }
-
-    /** Updates Device Admin state with current values */
-    void updateDeviceAdminState() {
-        if (TRACE_UPDATE) { Log.v(TAG, "UPDATE updateDeviceAdminState()"); }
-        boolean old = isDeviceAdmin;
-        isDeviceAdmin = mDPM.isAdminActive(mDeviceAdmin);
-        if (old != isDeviceAdmin) { Log.v(TAG, "mAdminActive: " + old + " -> " + isDeviceAdmin); }
-    }
-
-    /** Updates SELinux state with current device values */
-    void updateSELinuxState() {
-        if (TRACE_UPDATE) { Log.v(TAG, "UPDATE updateSELinuxState()"); }
-
-        boolean old;
-
-        // Device Admin necessary for SELinux Admin
-        old = isSELinuxAdmin;
-        isSELinuxAdmin = isDeviceAdmin && mDPM.isSELinuxAdmin(mDeviceAdmin);
-        if (old != isSELinuxAdmin) { Log.v(TAG, "mSELinuxAdmin: " + old + " -> " + isSELinuxAdmin); }
-    }
-
-    /** Updates MMAC state with current device values */
-    void updateMMACstate() {
-        if (TRACE_UPDATE) { Log.v(TAG, "UPDATE updateMMACState()"); }
-
-        boolean old;
-
-        // Device Admin necessary for MMAC Admin
-        old = isMMACadmin;
-        isMMACadmin = isDeviceAdmin && mDPM.isMMACadmin(mDeviceAdmin);
-        if (old != isMMACadmin) { Log.v(TAG, "mMMACadmin: " + old + " -> " + isMMACadmin); }
-    }
-
-    /** Asks user to enable SEAdmin as a Device Admin */
-    void enableAdmin() {
-        Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
-        intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mDeviceAdmin);
-        mActivity.startActivityForResult(intent, REQUEST_CODE_ENABLE_ADMIN);
-    }
-
-    void removeAdmin() {
-        mDPM.removeActiveAdmin(mDeviceAdmin);
-        setAdminState(false);   // removeActiveAdmin returns immediately but the
-        updateSELinuxState();   // removal is delayed, so set state manually
-        updateMMACstate();
-    }
-}

File src/com/android/seandroid_admin/SEAndroidAdminActivity.java

 import android.content.Intent;
 import android.preference.PreferenceActivity;
 import android.os.Bundle;
-import android.os.SELinux;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.widget.ArrayAdapter;
 import android.widget.CompoundButton;
 import android.widget.ListAdapter;
-import android.widget.Switch;
 import android.widget.TextView;
 
 import com.android.seandroid_admin.R;
 import java.util.List;
 
 public class SEAndroidAdminActivity extends PreferenceActivity {
-    private static final String TAG = "SEAdminActivity";
-    static final boolean TRACE_LIFECYCLE = false;
-    static final boolean TRACE_UPDATE = SEAndroidAdmin.TRACE_UPDATE;
 
     private List<Header> mHeaders;
 
-    SEAndroidAdmin mAdmin;
-
-    private View mDeviceAdminView;
-    private View mSELinuxAdminView;
-    private View mMMACadminView;
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
-        if (TRACE_LIFECYCLE) { Log.v(TAG, "LIFECYCLE ACTIVITY onCreate()"); }
         super.onCreate(savedInstanceState);
-        mAdmin = new SEAndroidAdmin(this);
     }
 
     @Override
     protected boolean isValidFragment(String fragmentName) {
-        return "com.android.seandroid_admin.SELinuxEnforcingFragment".equals(fragmentName) ||
-            "com.android.seandroid_admin.MMACFragment".equals(fragmentName) ||
-            "com.android.seandroid_admin.ConfigUpdateFragment".equals(fragmentName) ||
-            "com.android.seandroid_admin.ConfigUpdateMmacFragment".equals(fragmentName);
+        return "com.android.seandroid_admin.AboutFragment".equals(fragmentName) ||
+            "com.android.seandroid_admin.ConfigUpdateFragment".equals(fragmentName);
     }
 
     @Override
     public void onBuildHeaders(List<Header> headers) {
-        if (TRACE_LIFECYCLE) { Log.v(TAG, "LIFECYCLE ACTIVITY onBuildHeaders()"); }
-        if (!SELinux.isSELinuxEnabled()) {
-            // TODO Match with rest of theme
-            Log.v(TAG, "SELinux disabled");
-            loadHeadersFromResource(R.xml.disabled_headers, headers);
-            return;
-        }
-
         loadHeadersFromResource(R.xml.enabled_headers, headers);
         updateHeaderList(headers);
     }
 
     private void updateHeaderList(List<Header> target) {
         //TODO maybe enable or disable headers here based on whether were device admin?
+        // if selinux is disabled then we should gray out the selinux reload option
     }
 
     @Override
     public PreferenceActivity.Header onGetInitialHeader() {
         Header h = new PreferenceActivity.Header();
-        h.fragment = SELinuxEnforcingFragment.class.getCanonicalName();
+        //h.fragment = SELinuxEnforcingFragment.class.getCanonicalName();
+        h.fragment = ConfigUpdateFragment.class.getCanonicalName();
         return h;
     }
 
     private static class HeaderViewHolder {
         TextView title;
-        Switch switch_;
         TextView summary;
     }
 
     private static class HeaderAdapter extends ArrayAdapter<Header> {
         static final int HEADER_TYPE_CATEGORY = 0;
         static final int HEADER_TYPE_NORMAL = 1;
-        static final int HEADER_TYPE_SWITCH = 2;
-        private static final int HEADER_TYPE_COUNT = HEADER_TYPE_SWITCH + 1;
+        private static final int HEADER_TYPE_COUNT = HEADER_TYPE_NORMAL + 1;
 
-        private SEAndroidAdminActivity mActivity;
         private LayoutInflater mInflater;
 
         static int getHeaderType(Header header) {
             int id = (int) header.id; // ids are integers, so downcast is okay
             switch (id) {
-                case R.id.admin_category:
                 case R.id.update_category:
+                case R.id.about_category:
                     return HEADER_TYPE_CATEGORY;
-                case R.id.enable_device_admin:
-                case R.id.selinux_admin:
-                case R.id.mmac_admin:
-                    return HEADER_TYPE_SWITCH;
                 default:
                     return HEADER_TYPE_NORMAL;
             }
         @Override
         public boolean areAllItemsEnabled() {
             return false; // because of categories
-            //return SELinux.isSELinuxEnabled();
         }
 
         @Override
 
         public HeaderAdapter(Context context, List<Header> objects) {
             super(context, 0, objects);
-            mActivity = (SEAndroidAdminActivity) context;
             mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         }
 
                         holder.title = (TextView) view;
                         break;
 
-                    case HEADER_TYPE_SWITCH:
-                        view = mInflater.inflate(R.layout.preference_header_switch_item,
-                                parent, false);
-                        holder.title = (TextView) view.findViewById(
-                                com.android.internal.R.id.title);
-                        holder.summary = (TextView) view.findViewById(
-                                com.android.internal.R.id.summary);
-                        holder.switch_ = (Switch) view.findViewById(R.id.switchWidget);
-                        break;
-
                     case HEADER_TYPE_NORMAL:
                         view = mInflater.inflate(R.layout.preference_header_item, parent,
                                 false);
             switch (headerType) {
                 case HEADER_TYPE_CATEGORY:
                     holder.title.setText(header.getTitle(getContext().getResources()));
-                    Log.v(TAG, "Updated view for header CATEGORY " + holder.title.getText());
                     break;
 
-                case HEADER_TYPE_SWITCH:
-                    holder.switch_.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
-                        @Override
-                        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-                            mActivity.updateSwitchAssignment(header, (Switch) buttonView);
-                            SEAndroidAdmin mAdmin = mActivity.mAdmin;
-
-                            if (header.id == R.id.enable_device_admin) {
-                                boolean adminActive = mAdmin.isDeviceAdmin;
-                                Log.v(TAG, "Clicked Device admin: " + adminActive + " -> " + isChecked);
-                                if (isChecked != adminActive) {
-                                    if (isChecked) {
-                                        mAdmin.enableAdmin();
-                                        // Don't need to update views because onResume() fires
-                                        // after coming back from approval screen.
-                                    } else {
-                                        mAdmin.removeAdmin();
-                                        mActivity.setDeviceAdminView(false);
-                                    }
-                                }
-                                return;
-
-                            } else if (header.id == R.id.selinux_admin) {
-                                boolean adminActive = mAdmin.isSELinuxAdmin;
-                                Log.v(TAG, "Clicked SELinux admin: " + adminActive + " -> " + isChecked);
-                                if (isChecked != adminActive) {
-                                    boolean ret = mAdmin.mDPM.setSELinuxAdmin(mAdmin.mDeviceAdmin, isChecked);
-                                    // TODO show failure with toast or something
-                                    mAdmin.updateSELinuxState();
-                                    mActivity.updateSELinuxView();
-                                }
-                                return;
-
-                            } else if (header.id == R.id.mmac_admin) {
-                                boolean adminActive = mAdmin.isMMACadmin;
-                                Log.v(TAG, "Clicked MMAC admin: " + adminActive + " -> " + isChecked);
-                                if (isChecked != adminActive) {
-                                    boolean ret = mAdmin.mDPM.setMMACadmin(mAdmin.mDeviceAdmin, isChecked);
-                                    // TODO show failure  with toast or something
-                                    mAdmin.updateMMACstate();
-                                    mActivity.updateMMACview();
-                                }
-                                return;
-                            }
-                        }
-                    });
-                    // fallthrough to update common fields
-
                 case HEADER_TYPE_NORMAL:
                     holder.title.setText(header.getTitle(getContext().getResources()));
-                    Log.v(TAG, "Updated view for header " + holder.title.getText());
                     CharSequence summary = header.getSummary(getContext().getResources());
                     if (!TextUtils.isEmpty(summary)) {
                         holder.summary.setVisibility(View.VISIBLE);
                         holder.summary.setVisibility(View.GONE);
                     }
             }
-            mActivity.updateViewAssignment(header, view);
 
             return view;
         }
-
     }
 
     @Override
     public void setListAdapter(ListAdapter adapter) {
-        if (TRACE_LIFECYCLE) { Log.v(TAG, "LIFECYCLE ACTIVITY setListAdapter()"); }
         if (mHeaders == null) {
             mHeaders = new ArrayList<Header>();
             for (int i = 0; i < adapter.getCount(); i++) {
         super.setListAdapter(new HeaderAdapter(this, mHeaders));
     }
 
-    /** Tells the activity which View is what feature and updates that view
-     * with the current state. */
-    private void updateViewAssignment(Header header, View view) {
-        int id = (int) header.id; // ids are integers, so downcast is okay
-        switch (id) {
-            case R.id.enable_device_admin:
-                mDeviceAdminView = view;
-                updateDeviceAdminView();
-                break;
-            case R.id.mmac_admin:
-                mMMACadminView = view;
-                updateMMACview();
-                break;
-            case R.id.selinux_admin:
-                mSELinuxAdminView = view;
-                updateSELinuxView();
-                break;
-        }
-    }
-
-    /** Tells the activity which switch belongs to what view. This was needed
-     * because in the onCheckedChangeReceiver, the buttonView was different
-     * from the Switch in the saved Views. Not sure why. */
-    private void updateSwitchAssignment(Header header, Switch switch_) {
-        int id = (int) header.id; // ids are integers, so downcast is okay
-        switch (id) {
-            case R.id.enable_device_admin:
-                ((HeaderViewHolder) mDeviceAdminView.getTag()).switch_ = switch_;
-                break;
-            case R.id.selinux_admin:
-                ((HeaderViewHolder) mSELinuxAdminView.getTag()).switch_ = switch_;
-                break;
-            case R.id.mmac_admin:
-                ((HeaderViewHolder) mMMACadminView.getTag()).switch_ = switch_;
-                break;
-        }
-    }
-
     @Override
     public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        if (TRACE_LIFECYCLE) { Log.v(TAG, "LIFECYCLE ACTIVITY onActivityResult()"); }
         super.onActivityResult(requestCode, resultCode, data);
-        if (requestCode == SEAndroidAdmin.REQUEST_CODE_ENABLE_ADMIN) {
-            mAdmin.updateState();
-        }
     }
 
     @Override
     public void onResume() {
-        if (TRACE_LIFECYCLE) { Log.v(TAG, "LIFECYCLE ACTIVITY onResume()"); }
         super.onResume();
-        mAdmin.updateState();
-        updateDeviceAdminView();
-        updateMMACview();
-        updateSELinuxView();
-    }
-
-    /**
-     * Reset the SEAndroidAdmin's device admin state to reflect adminState and
-     * updates the UI.
-     * 
-     * Read {@link SEAndroidAdmin#setAdminState(boolean)} for more usage
-     * information before using this.
-     */
-    private void setDeviceAdminView(boolean adminState) {
-        if (SEAndroidAdmin.TRACE_UPDATE) { Log.v(TAG, "UPDATE ACTIVITY setDeviceAdminView("+adminState+")"); }
-        //mAdmin.setAdminState(adminState);
-
-        Switch switch_ = ((HeaderViewHolder) mDeviceAdminView.getTag()).switch_;
-        switch_.setChecked(mAdmin.isDeviceAdmin);
-        updateSELinuxView();
-        updateMMACview();
-    }
-
-    /** Updates the Device Admin view to reflect current device state.
-     * If you want truly current state, ask SEAndroidAdmin to update
-     * the state first. */
-    private void updateDeviceAdminView() {
-        if (SEAndroidAdmin.TRACE_UPDATE) { Log.v(TAG, "UPDATE ACTIVITY updateDeviceAdminView()"); }
-        //mAdmin.updateDeviceAdminState();
-
-        if (mDeviceAdminView != null) { // else, nothing to update yet
-            Switch switch_ = ((HeaderViewHolder) mDeviceAdminView.getTag()).switch_;
-            switch_.setChecked(mAdmin.isDeviceAdmin);
-        }
-    }
-
-    /** Updates the SELinux view to reflect the current device state.
-     * If you want truly current state, ask SEAndroidAdmin to update
-     * the state first. */
-    private void updateSELinuxView() {
-        if (SEAndroidAdmin.TRACE_UPDATE) { Log.v(TAG, "UPDATE ACTIVITY updateSELinuxView()"); }
-        //mAdmin.updateSELinuxState();
-
-        if (mSELinuxAdminView != null) { // else, nothing to update yet
-            Switch switch_ = ((HeaderViewHolder) mSELinuxAdminView.getTag()).switch_;
-            switch_.setEnabled(mAdmin.isDeviceAdmin);
-            switch_.setChecked(mAdmin.isSELinuxAdmin);
-        }
-    }
-
-    /** Updates the MMAC view to reflect the current device state.
-     * If you want truly current state, ask SEAndroidAdmin to update
-     * the state first. */
-    private void updateMMACview() {
-        if (SEAndroidAdmin.TRACE_UPDATE) { Log.v(TAG, "UPDATE ACTIVITY updateMMACview()"); }
-        //mAdmin.updateMMACstate();
-
-        if (mMMACadminView != null) { // else, nothing to update yet
-            Switch switch_ = ((HeaderViewHolder) mMMACadminView.getTag()).switch_;
-            switch_.setEnabled(mAdmin.isDeviceAdmin);
-            switch_.setChecked(mAdmin.isMMACadmin);
-        }
     }
 }

File src/com/android/seandroid_admin/SEAndroidAdminFragment.java

-package com.android.seandroid_admin;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.preference.Preference;
-import android.preference.PreferenceFragment;
-import android.preference.Preference.OnPreferenceChangeListener;
-import android.preference.Preference.OnPreferenceClickListener;
-import android.util.Log;
-
-/**
- * Common fragment code for DevicePolicyManager access. Provides two shared elements:
- *   1. Provides instance variables to access activity/context, DevicePolicyManager, etc.
- *   2. You can also place data shared by multiple Fragments in SEAdmin here.
- */
-public class SEAndroidAdminFragment extends PreferenceFragment implements
-        OnPreferenceChangeListener, OnPreferenceClickListener {
-
-    private static final String TAG = "SEAdminFragment";
-    protected static final boolean TRACE_LIFECYCLE = SEAndroidAdminActivity.TRACE_LIFECYCLE;
-    protected static final boolean TRACE_UPDATE = SEAndroidAdminActivity.TRACE_UPDATE;
-
-    protected SEAndroidAdminActivity mActivity;
-    protected SEAndroidAdmin mAdmin;
-
-    @Override
-    public void onAttach(Activity activity) {
-        if (TRACE_LIFECYCLE) { Log.v(TAG, "LIFECYCLE FRAGMENT onAttach()"); }
-        super.onAttach(activity);
-        mActivity = (SEAndroidAdminActivity) activity;
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        if (TRACE_LIFECYCLE) { Log.v(TAG, "LIFECYCLE FRAGMENT onCreate()"); }
-        super.onCreate(savedInstanceState);
-    }
-
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        if (TRACE_LIFECYCLE) { Log.v(TAG, "LIFECYCLE FRAGMENT onActivityCreated()"); }
-        super.onActivityCreated(savedInstanceState);
-
-        mAdmin = mActivity.mAdmin;
-    }
-
-    @Override
-    public void onStart() {
-        if (TRACE_LIFECYCLE) { Log.v(TAG, "LIFECYCLE FRAGMENT onStart()"); }
-        super.onStart();
-    }
-
-    @Override
-    public void onResume() {
-        if (TRACE_LIFECYCLE) { Log.v(TAG, "LIFECYCLE FRAGMENT onResume()"); }
-        super.onResume();
-
-        mAdmin.updateState();
-    }
-
-    @Override
-    public boolean onPreferenceClick(Preference preference) {
-        return false;
-    }
-
-    @Override
-    public boolean onPreferenceChange(Preference preference, Object newValue) {
-        return false;
-    }
-
-}

File src/com/android/seandroid_admin/SELinuxEnforcingFragment.java

-package com.android.seandroid_admin;
-
-import android.app.admin.DevicePolicyManager;
-import android.os.Bundle;
-import android.os.SELinux;
-import android.preference.CheckBoxPreference;
-import android.preference.Preference;
-import android.preference.Preference.OnPreferenceChangeListener;
-import android.preference.Preference.OnPreferenceClickListener;
-import android.util.Log;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.android.seandroid_admin.R;
-
-import com.google.common.io.Files;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Collections;
-import java.util.List;
-
-//TODO may want to rename to reflect real functionality
-public class SELinuxEnforcingFragment extends SEAndroidAdminFragment
-        implements OnPreferenceChangeListener {
-    public static final String TAG = "SELinuxFragment";
-
-    private static final String KEY_SELINUX_RELOAD = "key_selinux_reload";
-    private static final String KEY_SELINUX_RESTORE = "key_selinux_restore";
-    private static final String KEY_PROPERTYCONTEXTS_RELOAD = "key_propertycontexts_reload";
-    private static final String KEY_PROPERTYCONTEXTS_RESTORE = "key_propertycontexts_restore";
-    private static final String KEY_FILECONTEXTS_RELOAD = "key_filecontexts_reload";
-    private static final String KEY_FILECONTEXTS_RESTORE = "key_filecontexts_restore";
-    private static final String KEY_SEAPPCONTEXTS_RELOAD = "key_seappcontexts_reload";
-    private static final String KEY_SEAPPCONTEXTS_RESTORE = "key_seappcontexts_restore";
-
-    private static final String SELINUX_POLICY_FILE = "sepolicy";
-    private static final String PROPERTY_CONTEXTS_FILE = "property_contexts";
-    private static final String FILE_CONTEXTS_FILE = "file_contexts";
-    private static final String SEAPP_CONTEXTS_FILE = "seapp_contexts";
-
-    private Preference mSELinuxReload, mSELinuxRestore;
-    private Preference mPropertyContextsReload, mPropertyContextsRestore;
-    private Preference mFileContextsReload, mFileContextsRestore;
-    private Preference mSEAppContextsReload, mSEAppContextsRestore;
-
-    private File mSELinuxPolicyFile = null;
-    private File mPropertyContextsPolicyFile = null;
-    private File mFileContextsPolicyFile = null;
-    private File mSEAppContextsPolicyFile = null;
-
-    private TextView mEmptyView;
-
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
-        mAdmin.updateSELinuxState();
-
-        addPreferencesFromResource(R.xml.selinux_enforcing_fragment);
-
-        mEmptyView = (TextView) getView().findViewById(android.R.id.empty);