Commits

Anonymous committed 482698a

This patch enables the avc denied logging feature in the SE Android SEAndroidManager application.

Comments (0)

Files changed (16)

 
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 
+LOCAL_JNI_SHARED_LIBRARIES := libjni_klogctl
+
+LOCAL_REQUIRED_MODULES := libjni_klogctl
+
 include $(BUILD_PACKAGE)
 
 # Use the folloing include to make our test apk.
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE    := libjni_klogctl
+LOCAL_SRC_FILES := \
+		klogctl.c \
+		exception.c
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SHARED_LIBRARIES := liblog
+
+include $(BUILD_SHARED_LIBRARY)
+
+#include <string.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define LOG_TAG "exception"
+
+#include <cutils/log.h>
+
+#include "klogctl.h"
+
+/* Bionic does not define this*/
+#ifndef ERESTARTSYS
+#define ERESTARTSYS     512
+#endif
+
+static jint
+throwNoClassDefError(JNIEnv *env, char *message) {
+
+	jclass exClass;
+	char *className = "java/lang/NoClassDefFoundError" ;
+
+	exClass = (*env)->FindClass( env, className );
+
+	if(exClass == NULL) {
+        LOGE("Could no throw java/lang/NoClassDefFoundError");
+        /* If this happens, the program is out of control */
+        exit(42);
+    }
+	return (*env)->ThrowNew( env, exClass, message );
+}
+
+static jint
+throwIllegalArgumentException(JNIEnv *env, char *message) {
+
+	jclass exClass;
+	char *className = "java/lang/IllegalArgumentException" ;
+
+	exClass = (*env)->FindClass(env, className);
+	if (exClass == NULL) {
+        return throwNoClassDefError(env, className);
+	}
+
+	return (*env)->ThrowNew(env, exClass, message);
+}
+
+static jint
+throwAccessControlException(JNIEnv *env, char *message) {
+
+	jclass exClass;
+	char *className = "java/security/AccessControlException" ;
+
+	exClass = (*env)->FindClass(env, className);
+	if (exClass == NULL) {
+        return throwNoClassDefError(env, className);
+	}
+
+	return (*env)->ThrowNew(env, exClass, message);
+}
+
+static jint
+throwInterruptedException(JNIEnv *env, char *message) {
+
+	jclass exClass;
+	char *className = "java/lang/InterruptedException" ;
+
+	exClass = (*env)->FindClass(env, className);
+	if (exClass == NULL) {
+        return throwNoClassDefError(env, className);
+	}
+
+	return (*env)->ThrowNew(env, exClass, message);
+}
+
+static jint
+throwUnsupportedOperationException(JNIEnv *env, char *message) {
+
+	jclass exClass;
+	char *className = "java/lang/UnsupportedOperationException" ;
+
+	exClass = (*env)->FindClass(env, className);
+	if (exClass == NULL) {
+        return throwNoClassDefError(env, className);
+	}
+
+	return (*env)->ThrowNew(env, exClass, message);
+}
+
+static jint
+throwException(JNIEnv *env, char *message) {
+
+	jclass exClass;
+	char *className = "java/lang/Exception" ;
+
+	exClass = (*env)->FindClass(env, className);
+	if (exClass == NULL) {
+        return throwNoClassDefError(env, className);
+	}
+
+	return (*env)->ThrowNew(env, exClass, message);
+}
+
+void exception_throw(int error, JNIEnv *env) {
+
+	char message[128];
+	snprintf(message, 128, "Error reading kernel buffer : %s", strerror(error));
+
+	switch(error) {
+		case ENOSYS:
+			throwUnsupportedOperationException(env, message);
+			break;
+
+		case EPERM:
+			throwAccessControlException(env, message);
+			break;
+
+		case ERESTARTSYS:
+			throwInterruptedException(env, message);
+			break;
+
+		case EINVAL:
+			throwIllegalArgumentException(env, message);
+			break;
+
+		default:
+			throwException(env, message);
+			break;
+	}
+	return;
+}
+#ifndef _EXCEPTION_H_
+#define _EXCEPTION_H_
+
+#include <jni.h>
+
+extern void exception_throw(int error, JNIEnv *env);
+
+#endif
+#include <string.h>
+#include <jni.h>
+#include <errno.h>
+
+#define LOG_TAG "klogctl"
+
+#include <cutils/log.h>
+
+#include "klogctl.h"
+#include "exception.h"
+
+extern int klogctl(int type, char *buf, int length);
+
+JNIEXPORT
+jint
+JNICALL
+Java_com_android_seandroid_1manager_KLogCtl_kLogCtl(JNIEnv *env, jclass thiz, jint type, jbyteArray buf, jint length) {
+
+	jint error;
+	jboolean is_copy;
+	jbyte *bytes = (buf) ? (*env)->GetByteArrayElements(env, buf, &is_copy) : NULL;
+
+	error = klogctl(type, (char *)bytes, length);
+
+	if(bytes) {
+		(*env)->ReleaseByteArrayElements(env, buf, bytes, 0);
+	}
+
+	if(error == -1) {
+		exception_throw(errno, env);
+		return 0;
+	}
+
+	return error;
+}
+
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_android_seandroid_manager_KLogCtl */
+
+#ifndef _Included_com_android_seandroid_manager_KLogCtl
+#define _Included_com_android_seandroid_manager_KLogCtl
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class:     com_android_seandroid_manager_KLogCtl
+ * Method:    kLogCtl
+ * Signature: (I[BI)I
+ */
+JNIEXPORT jint JNICALL Java_com_android_seandroid_1manager_KLogCtl_kLogCtl
+  (JNIEnv *, jclass, jint, jbyteArray, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
+

project.properties

 # project structure.
 
 # Project target.
-target=android-13
+target=android-15

res/layout/avc_denied_menu_toast.xml

+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:id="@+id/avc_denied_menu_toast"
+              android:orientation="horizontal"
+              android:layout_width="fill_parent"
+              android:layout_height="fill_parent"
+              android:padding="10dp"
+              android:background="#DAAA"
+              >
+    <TextView android:id="@+id/text"
+              android:layout_width="wrap_content"
+              android:layout_height="fill_parent"
+              android:textColor="#FFF"
+              android:textSize="30sp"
+              />
+</LinearLayout>

res/layout/avc_denied_options.xml

+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+        <item android:id="@+id/fileSave"
+          android:title="Save">
+    </item>
+</menu>

res/layout/avc_denied_reader.xml

+<?xml version="1.0" encoding="utf-8"?>
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/linearLayout1"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:orientation="vertical" >
+
+        <RelativeLayout
+            android:id="@+id/linearLayout2"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" >
+
+            <Button
+                android:id="@+id/refreshButton"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_centerHorizontal="true"
+                android:layout_centerVertical="true"
+                android:layout_alignParentRight="@+id/linearLayout2"
+                android:text="@string/avc_denied_log_refresh_button_label"
+                android:layout_toRightOf="@+id/textView1" />
+
+        </RelativeLayout>
+        
+        <ScrollView
+            android:id="@+id/avcLogScrollView"
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent">    
+          
+            <TextView
+                android:id="@+id/avcLogTextView"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/avc_denied_log_reload_msg" 
+                android:textIsSelectable="true"/>
+            
+        </ScrollView>
+            
+        
+    </LinearLayout>

res/values/strings.xml

     <string name="selinux_manage_booleans">SELinux Booleans</string>
     <string name="selinux_manage_booleans_summary">Manage SELinux Booleans</string>
     <string name="selinux_no_booleans">No Booleans</string>
+    <string name="avc_denied_log_fragment_title">AVC Denied Logs</string>
+    <string name="avc_denied_log_reload_msg">Press The "Refresh" Button to Update AVC Denied Messages</string>
+    <string name="avc_denied_log_refresh_button_label">Refresh</string>
+    <string name="avc_denied_log_refresh_text">Refresh AVC Logs</string>
+    <string name="avc_progress_bar_message">Loading&#8230;</string>
+    <string name="avc_logs_key">AVCLogs</string>
+    <string name="avc_dialog_title_error">Error</string>
+    <string name="avc_dialog_title_info">Info</string>
+    <string name="avc_ok_button_text">OK</string>
+    <string name="avc_option_save_button_title">Save</string>
+    <string name="avc_denied_log_filename">avc_denied_logs</string>
+    <string name="avc_denied_log_onsave_dialog_title">File Saved</string>
+    <string name="avc_denied_log_onsave_dialog_message">Logs Saved To: </string>
+    <string name="avc_logs_pulled_key">logsPulled</string>
+    <string name="avc_denied_log_empty_log_msg">No Logs To Save</string>
+    <!--
+		In java and in reg expressions "\" is an escape
+     	character, so it takes 4 to get 1
+     	See http://www.regular-expressions.info/java.html for
+     	more info
+     	This string determines how a date time object is formated into
+     	a filename.
+    -->
+    <string name="avc_denied_timestamp_format_regex">[\\\\,: ]</string>
+    
+    <!-- Everything matched in the regular expression above is replaced with the below string -->
+    <string name="avc_denied_timestamp_replacement_char">_</string>
+    <string name="avc_denied_log_file_extension">.log</string>
 </resources>
+

res/xml/enabled_headers.xml

         android:title="@string/selinux_boolean_fragment_title">
     </header>
 
+    <header
+        android:fragment="com.android.seandroid_manager.SEAndroidManager$AVCDeniedReaderFragment"
+        android:title="@string/avc_denied_log_fragment_title">
+    </header>
+
 </preference-headers>

src/com/android/seandroid_manager/AVCCallback.java

+
+package com.android.seandroid_manager;
+
+/**
+ * Used to handle events from parsing the kernel logs looking for AVC messages.
+ */
+public abstract class AVCCallback {
+
+    /**
+     * Before parsing begins, this function is called. By default nothing
+     * happens, as this function is not required for proper function, but can be
+     * overridden by subclass for perhaps starting a progress dialog.
+     */
+    public void onStart() {
+    }
+
+    /**
+     * For each AVC denied message encountered, this callback is called. This
+     * must be implemented, each raw kernel AVC message is passed to this
+     * callback. From here further processing can be done.
+     * 
+     * @param message The exact AVC denied message encountered while parsing.
+     */
+    public abstract void onEvent(String logMessage);
+
+    /**
+     * This is called at the end of parsing, when no more messages are found. By
+     * default nothing happens, as this function is not required for proper
+     * function, but can be overridden by a subclass for perhaps starting a
+     * progress dialog.
+     */
+    public void onFinish() {
+    }
+
+    /**
+     * Called when an exception occurs.
+     * The exception generated while trying to get the kernel ring buffer.
+     * 
+     * @param e The exception generated
+     */
+    public void onException(Exception e) {
+    }
+
+}

src/com/android/seandroid_manager/AVCReader.java

+
+package com.android.seandroid_manager;
+
+import java.io.ByteArrayInputStream;
+import java.util.Scanner;
+
+/**
+ * Reads the kernel message log and sends AVCDenied messages to the registered
+ * handler.
+ */
+public class AVCReader extends Thread {
+
+    private String avcSearchTag = "avc";
+    private AVCCallback callback = null;
+
+    public AVCReader(AVCCallback avcHandler) {
+        super();
+        callback = avcHandler;
+    }
+
+    /**
+     * Reads from kernel logs, parses them and calls the callbacks set by
+     * AVCReader(AVCCallback avcHandler) for each avc denied message
+     * encountered.
+     */
+    private void parseLogs() {
+
+        int value;
+        byte logs[];
+        String line;
+
+        callback.onStart();
+
+        try {
+            value = KLogCtl.kLogCtl(10, null, 0);
+            logs = new byte[value];
+            value = KLogCtl.kLogCtl(3, logs, value);
+
+            Scanner stream = new Scanner(new ByteArrayInputStream(logs));
+
+            while (stream.hasNextLine() && !interrupted()) {
+                line = stream.nextLine();
+                if (line.contains(avcSearchTag)) {
+                    callback.onEvent(line);
+                }
+            }
+        } catch (Exception e) {
+            callback.onException(e);
+        } finally {
+            callback.onFinish();
+        }
+        return;
+    }
+
+    @Override
+    public void run() {
+        parseLogs();
+    }
+
+}

src/com/android/seandroid_manager/KLogCtl.java

+
+package com.android.seandroid_manager;
+
+public class KLogCtl {
+
+    /**
+     * Reads from the kernel logs. See man klogctl.
+     * 
+     * @param type Determines the action to be taken by this function. Quoting
+     *            from kernel/printk.c: Commands to sys_syslog: 0 -- Close the
+     *            log. Currently a NOP. 1 -- Open the log. Currently a NOP. 2 --
+     *            Read from the log. 3 -- Read all messages remaining in the
+     *            ring buffer. 4 -- Read and clear all messages remaining in the
+     *            ring buffer 5 -- Clear ring buffer. 6 -- Disable printk to
+     *            console 7 -- Enable printk to console 8 -- Set level of
+     *            messages printed to console 9 -- Return number of unread
+     *            characters in the log buffer 10 -- Return size of the log
+     *            buffer
+     * @throws An exception for each std error the syscall can fail with ENOSYS
+     *             --> MissingResourceException, EPERM -->
+     *             AccessControlException, ERESTARTSYS --> InterruptedException,
+     *             EINVAL --> IllegalArgumentException, and an unknown error not
+     *             listed above will throw --> Exception
+     * @param buf Buffer to read the kernel log into.
+     * @param length The number of bytes to read.
+     * @return bytes read on commands 2, 3, 4 and 6, else 0 on success.
+     */
+    public static native int kLogCtl(int type, byte buf[], int length) throws Exception;
+
+    static {
+        System.loadLibrary("jni_klogctl");
+    }
+}

src/com/android/seandroid_manager/SEAndroidManager.java

+
 package com.android.seandroid_manager;
 
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Fragment;
 import android.app.ListFragment;
+import android.app.ProgressDialog;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.content.SharedPreferences;
 import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
 import android.os.SELinux;
 import android.preference.CheckBoxPreference;
 import android.preference.Preference;
 import android.preference.PreferenceActivity;
 import android.preference.PreferenceFragment;
 import android.preference.PreferenceScreen;
-import android.provider.Settings;
 import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
 import android.view.View;
+import android.view.View.OnClickListener;
 import android.view.ViewGroup;
-import android.widget.CompoundButton;
 import android.widget.ArrayAdapter;
+import android.widget.Button;
 import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.ScrollView;
 import android.widget.TextView;
-
+import android.provider.Settings;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.util.Date;
 import java.util.List;
 
 public class SEAndroidManager extends PreferenceActivity {
         }
     }
 
+    public static class AVCDeniedReaderFragment extends Fragment {
+
+        private TextView logs;
+        private ScrollView scrollView;
+        private ProgressDialog progressDialog;
+        private Handler handler;
+        private Activity activity;
+
+        private String defaultText;
+        private String replacement;
+        private String regExp;
+        private String fileExtension;
+        private String fileOnSave;
+        private String logPrefix;
+        private String emptyLogMessage;
+        private String dialogInfo;
+        private String dialogError;
+        private String logsKey;
+        private String okButtonText;
+
+        private final static int DIALOG_ERROR = 0;
+        private final static int DIALOG_INFO = 1;
+
+        private AVCCallback handleMessage = new AVCCallback() {
+
+            @Override
+            public void onStart() {
+                handler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        progressDialog.show();
+                    }
+                });
+            }
+
+            @Override
+            public void onEvent(final String logMessage) {
+
+                handler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        logs.append(logMessage + "\n\n");
+                    }
+                });
+            }
+
+            @Override
+            public void onFinish() {
+                scrollView.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        scrollView.fullScroll(ScrollView.FOCUS_DOWN);
+                        progressDialog.cancel();
+                    }
+                });
+            }
+
+            @Override
+            public void onException(Exception e) {
+                showDialog(e.getMessage(), DIALOG_ERROR);
+            }
+
+        };
+
+        private OnClickListener onAVCRefreshClick = new OnClickListener() {
+
+            @Override
+            public void onClick(View v) {
+                logs.setText(null);
+                AVCReader logReader = new AVCReader(handleMessage);
+                logReader.start();
+            }
+        };
+
+        @Override
+        public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+
+            View v = inflater.inflate(R.layout.avc_denied_reader, container, false);
+
+            Button b = (Button) v.findViewById(R.id.refreshButton);
+            b.setOnClickListener(onAVCRefreshClick);
+
+            logs = (TextView) v.findViewById(R.id.avcLogTextView);
+
+            scrollView = (ScrollView) v.findViewById(R.id.avcLogScrollView);
+
+            handler = new Handler();
+
+            activity = getActivity();
+
+            defaultText = getString(R.string.avc_denied_log_reload_msg);
+            replacement = getString(R.string.avc_denied_timestamp_replacement_char);
+            fileExtension = getString(R.string.avc_denied_log_file_extension);
+            regExp = getString(R.string.avc_denied_timestamp_format_regex);
+            fileOnSave = getString(R.string.avc_denied_log_onsave_dialog_message);
+            logPrefix = getString(R.string.avc_denied_log_filename);
+            dialogInfo = getString(R.string.avc_dialog_title_info);
+            dialogError = getString(R.string.avc_dialog_title_error);
+            logsKey = getString(R.string.avc_logs_key);
+            emptyLogMessage = getString(R.string.avc_denied_log_empty_log_msg);
+            okButtonText = getString(R.string.avc_ok_button_text);
+
+            progressDialog = new ProgressDialog(activity);
+            progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
+            progressDialog.setMessage(getString(R.string.avc_progress_bar_message));
+
+            if (savedInstanceState != null) {
+                CharSequence oldLogs = savedInstanceState.getCharSequence(logsKey);
+                if (oldLogs != null) {
+                    logs.setText(oldLogs);
+                }
+            }
+
+            setHasOptionsMenu(true);
+            return v;
+        }
+
+        @Override
+        public void onSaveInstanceState(Bundle savedInstanceState) {
+            savedInstanceState.putCharSequence(logsKey, logs.getText());
+            super.onSaveInstanceState(savedInstanceState);
+        }
+
+        @Override
+        public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+            inflater.inflate(R.layout.avc_denied_options, menu);
+            return;
+        }
+
+        @Override
+        public boolean onOptionsItemSelected(MenuItem item) {
+
+            int id = item.getItemId();
+
+            switch (id) {
+
+                case R.id.fileSave:
+
+                    int length = logs.getText().length();
+                    if (length != defaultText.length() &&
+                            !defaultText.equals(logs.getText())) {
+
+                        String fileName = logPrefix + replacement +
+                                DateFormat.getDateTimeInstance().format(new Date()).toLowerCase() +
+                                fileExtension;
+
+                        // See res/strings.xml for details on regExp formatting
+                        fileName = fileName.replaceAll(regExp, replacement);
+                        saveLogs(fileName);
+                    }
+                    else if (length == 0) {
+                        showDialog(emptyLogMessage, DIALOG_INFO);
+                    }
+                    else {
+                        showDialog(defaultText, DIALOG_ERROR);
+                    }
+                    break;
+                default:
+                    return super.onOptionsItemSelected(item);
+            }
+            return true;
+        }
+
+        /**
+         * Displays a pop up ERROR dialog. Safe to call from non gui thread.
+         *
+         * @param message
+         * @param type The type of message to be displayed, see DIALOG_ERROR and
+         *            friends.
+         */
+        private void showDialog(final String message, final int type) {
+
+            handler.post(new Runnable() {
+
+                @Override
+                public void run() {
+
+                    String title = (type == DIALOG_INFO) ? dialogInfo : dialogError;
+
+                    AlertDialog alertDialog = new AlertDialog.Builder(activity).create();
+                    alertDialog.setTitle(title);
+                    alertDialog.setMessage(message);
+
+                    alertDialog.setButton(DialogInterface.BUTTON_NEUTRAL, okButtonText,
+                            new DialogInterface.OnClickListener() {
+                                public void onClick(DialogInterface dialog, int which) {
+                                    // intentionally left empty
+                                }
+                            });
+
+                    // alertDialog
+                    alertDialog.show();
+                    return;
+                }
+            });
+        }
+
+        /**
+         * Saves the AVC denied message logs to disk. This function runs on a
+         * separate thread.
+         *
+         * @param filename The filename to save the logs as
+         */
+        private void saveLogs(final String filename) {
+
+            new Thread(new Runnable() {
+                @Override
+                public void run() {
+
+                    try {
+
+                        final File file =
+                                new File(Environment.getExternalStorageDirectory(), filename);
+
+                        FileOutputStream fos = new FileOutputStream(file);
+                        CharSequence c = logs.getText();
+                        fos.write(c.toString().getBytes());
+                        fos.flush();
+                        fos.close();
+                        showDialog(fileOnSave + filename, DIALOG_INFO);
+
+                    }
+                    catch (FileNotFoundException e) {
+
+                        showDialog(e.toString(), DIALOG_ERROR);
+                    }
+                    catch (IOException e) {
+
+                        showDialog(e.toString(), DIALOG_ERROR);
+                    }
+                }
+            }).run();
+        }
+    }
+
     public static class SELinuxEnforcingFragment extends PreferenceFragment {
 
         private static final String KEY_SELINUX_ENFORCING = "selinux_enforcing";
-
         private CheckBoxPreference mSELinuxToggleEnforce;
 
         @Override
         public void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
-
             addPreferencesFromResource(R.xml.selinux_enforcing_fragment);
-            mSELinuxToggleEnforce = (CheckBoxPreference) getPreferenceScreen().findPreference(KEY_SELINUX_ENFORCING);
+
+            mSELinuxToggleEnforce = (CheckBoxPreference) getPreferenceScreen().findPreference(
+                    KEY_SELINUX_ENFORCING);
             mSELinuxToggleEnforce.setChecked(SELinux.isSELinuxEnforced());
         }
 
         }
 
         @Override
-        public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
+        public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
+                Preference preference) {
             final String key = preference.getKey();
             if (preference == mSELinuxToggleEnforce) {
                 SELinux.setSELinuxEnforce(!SELinux.isSELinuxEnforced());
 
             public myBooleanAdapter(Context context, int textViewResourceId, String[] items) {
                 super(context, textViewResourceId, items);
-                mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+                mInflater = (LayoutInflater) context
+                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                 mBooleans = items;
             }
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.