com.noshufou.android.su.util.Util.java Source code

Java tutorial

Introduction

Here is the source code for com.noshufou.android.su.util.Util.java

Source

/*******************************************************************************
 * Copyright (c) 2011 Adam Shanks (ChainsDD)
 * 
 * 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.
 ******************************************************************************/
package com.noshufou.android.su.util;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.text.format.DateFormat;
import android.util.Log;
import android.util.SparseArray;

import com.noshufou.android.su.HomeActivity;
import com.noshufou.android.su.R;
import com.noshufou.android.su.UpdaterActivity;
import com.noshufou.android.su.preferences.Preferences;
import com.noshufou.android.su.preferences.PreferencesActivity;
import com.noshufou.android.su.preferences.PreferencesActivityHC;
import com.noshufou.android.su.provider.PermissionsProvider.Apps.AllowType;
import com.noshufou.android.su.service.UpdaterService;

public class Util {
    private static final String TAG = "Su.Util";

    public static final int MALICIOUS_NOT = 0;
    public static final int MALICIOUS_UID = 1;
    public static final int MALICIOUS_RESPOND = 2;
    public static final int MALICIOUS_PROVIDER_WRITE = 3;

    private static final SparseArray<String> sSystemUids = new SparseArray<String>(32);
    static {
        sSystemUids.put(0, "root");
        sSystemUids.put(1000, "system");
        sSystemUids.put(1001, "radio");
        sSystemUids.put(1002, "bluetooth");
        sSystemUids.put(1003, "graphics");
        sSystemUids.put(1004, "input");
        sSystemUids.put(1005, "audio");
        sSystemUids.put(1006, "camera");
        sSystemUids.put(1007, "log");
        sSystemUids.put(1008, "compass");
        sSystemUids.put(1009, "mount");
        sSystemUids.put(1010, "wifi");
        sSystemUids.put(1011, "adb");
        sSystemUids.put(1012, "install");
        sSystemUids.put(1013, "media");
        sSystemUids.put(1014, "dhcp");
        sSystemUids.put(1015, "sdcard_rw");
        sSystemUids.put(1016, "vpn");
        sSystemUids.put(1017, "keystore");
        sSystemUids.put(1018, "usb");
        sSystemUids.put(1021, "gps");
        sSystemUids.put(1025, "nfc");
        sSystemUids.put(2000, "shell");
        sSystemUids.put(2001, "cache");
        sSystemUids.put(2002, "diag");
        sSystemUids.put(3001, "net_bt_admin");
        sSystemUids.put(3002, "net_bt");
        sSystemUids.put(3003, "inet");
        sSystemUids.put(3004, "net_raw");
        sSystemUids.put(3005, "net_admin");
        sSystemUids.put(9998, "misc");
        sSystemUids.put(9999, "nobody");
    }

    public static class MenuId {
        public static final int ELITE = 0;
        public static final int TOGGLE = 1;
        public static final int FORGET = 2;
        public static final int INFO = 3;
        public static final int LOG = 4;
        public static final int CLEAR_LOG = 5;
        public static final int USE_APP_SETTINGS = 6;
        public static final int NOTIFICATIONS = 7;
        public static final int LOGGING = 8;
        public static final int TEMP_UNROOT = 9;
        public static final int OTA_SURVIVE = 10;
        public static final int PREFERENCES = 11;
    }

    public static class VersionInfo {
        public String version = "";
        public int versionCode = 0;
    }

    public static String getAppName(Context c, int uid, boolean withUid) {
        if (sSystemUids.get(uid) != null) {
            return sSystemUids.get(uid);
        }

        PackageManager pm = c.getPackageManager();
        String appName = "Unknown";
        String[] packages = pm.getPackagesForUid(uid);

        if (packages != null) {
            try {
                if (packages.length == 1) {
                    appName = pm.getApplicationLabel(pm.getApplicationInfo(packages[0], 0)).toString();
                } else if (packages.length > 1) {
                    appName = "";
                    for (int i = 0; i < packages.length; i++) {
                        appName += packages[i];
                        if (i < packages.length - 1) {
                            appName += ", ";
                        }
                    }
                }
            } catch (NameNotFoundException e) {
                Log.e(TAG, "Package name not found", e);
            }
        } else {
            Log.e(TAG, "Package not found for uid " + uid);
        }

        if (withUid) {
            appName += " (" + uid + ")";
        }

        return appName;
    }

    public static String getAppPackage(Context c, int uid) {
        if (sSystemUids.get(uid) != null) {
            return sSystemUids.get(uid);
        }

        PackageManager pm = c.getPackageManager();
        String[] packages = pm.getPackagesForUid(uid);
        String appPackage = "unknown";

        if (packages != null) {
            if (packages.length == 1) {
                appPackage = packages[0];
            } else if (packages.length > 1) {
                appPackage = "";
                for (int i = 0; i < packages.length; i++) {
                    appPackage += packages[i];
                    if (i < packages.length - 1) {
                        appPackage += ", ";
                    }
                }
            }
        } else {
            Log.e(TAG, "Package not found");
        }

        return appPackage;
    }

    public static Drawable getAppIcon(Context c, int uid) {
        PackageManager pm = c.getPackageManager();
        Drawable appIcon = c.getResources().getDrawable(R.drawable.sym_def_app_icon);
        String[] packages = pm.getPackagesForUid(uid);

        if (packages != null) {
            //            if (packages.length == 1) {
            try {
                ApplicationInfo appInfo = pm.getApplicationInfo(packages[0], 0);
                appIcon = pm.getApplicationIcon(appInfo);
            } catch (NameNotFoundException e) {
                Log.e(TAG, "No package found matching with the uid " + uid);
            }
            //            }
        } else {
            Log.e(TAG, "Package not found for uid " + uid);
        }

        return appIcon;
    }

    public static Drawable getStatusIconDrawable(Context context, int allow) {
        int[][] statusButtons = { { R.drawable.perm_deny_dot, R.drawable.perm_allow_dot },
                { R.drawable.perm_deny_emo, R.drawable.perm_allow_emo } };

        String iconTypeString = PreferenceManager.getDefaultSharedPreferences(context)
                .getString(Preferences.STATUS_ICON_TYPE, "emote");
        int statusIconType = 1;
        if (iconTypeString.equals("dot")) {
            statusIconType = 0;
        } else if (iconTypeString.equals("emote")) {
            statusIconType = 1;
        }

        if (allow < 0 || allow > 1) {
            Log.e(TAG, "Bad value given to getStatusButtonDrawable(int). Expecting 0 or 1, got " + allow);
            return null;
        }

        Drawable drawable = context.getResources().getDrawable(statusButtons[statusIconType][allow]);
        return drawable;
    }

    public static String getUidName(Context c, int uid, boolean withUid) {
        PackageManager pm = c.getPackageManager();
        String uidName = "";
        if (uid == 0) {
            uidName = "root";
        } else {
            pm.getNameForUid(uid);
        }

        if (withUid) {
            uidName += " (" + uid + ")";
        }

        return uidName;
    }

    public static void goHome(Context context) {
        // Be clever here, otherwise the home button doesn't work as advertised.
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        Intent intent;
        if (prefs.getBoolean(Preferences.GHOST_MODE, false)) {
            intent = new Intent(context, HomeActivity.class);
        } else {
            intent = new Intent();
            intent.setComponent(new ComponentName("com.noshufou.android.su", "com.noshufou.android.su.Su"));
        }
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        context.startActivity(intent);
    }

    public static String getSuVersion() {
        Process process = null;
        String inLine = null;

        try {
            process = Runtime.getRuntime().exec("sh");
            DataOutputStream os = new DataOutputStream(process.getOutputStream());
            BufferedReader is = new BufferedReader(
                    new InputStreamReader(new DataInputStream(process.getInputStream())), 64);
            os.writeBytes("su -v\n");

            // We have to hold up the thread to make sure that we're ready to read
            // the stream, using increments of 5ms makes it return as quick as
            // possible, and limiting to 1000ms makes sure that it doesn't hang for
            // too long if there's a problem.
            for (int i = 0; i < 400; i++) {
                if (is.ready()) {
                    break;
                }
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    Log.w(TAG, "Sleep timer got interrupted...");
                }
            }
            if (is.ready()) {
                inLine = is.readLine();
                if (inLine != null) {
                    return inLine;
                }
            } else {
                os.writeBytes("exit\n");
            }
        } catch (IOException e) {
            Log.e(TAG, "Problems reading current version.", e);
            return null;
        } finally {
            if (process != null) {
                process.destroy();
            }
        }

        return null;
    }

    public static int getSuVersionCode() {
        Process process = null;
        String inLine = null;

        try {
            process = Runtime.getRuntime().exec("sh");
            DataOutputStream os = new DataOutputStream(process.getOutputStream());
            BufferedReader is = new BufferedReader(
                    new InputStreamReader(new DataInputStream(process.getInputStream())), 64);
            os.writeBytes("su -v\n");

            // We have to hold up the thread to make sure that we're ready to read
            // the stream, using increments of 5ms makes it return as quick as
            // possible, and limiting to 1000ms makes sure that it doesn't hang for
            // too long if there's a problem.
            for (int i = 0; i < 400; i++) {
                if (is.ready()) {
                    break;
                }
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    Log.w(TAG, "Sleep timer got interrupted...");
                }
            }
            if (is.ready()) {
                inLine = is.readLine();
                if (inLine != null && Integer.parseInt(inLine.substring(0, 1)) > 2) {
                    inLine = null;
                    os.writeBytes("su -V\n");
                    inLine = is.readLine();
                    if (inLine != null) {
                        return Integer.parseInt(inLine);
                    }
                } else {
                    return 0;
                }
            } else {
                os.writeBytes("exit\n");
            }
        } catch (IOException e) {
            Log.e(TAG, "Problems reading current version.", e);
            return 0;
        } finally {
            if (process != null) {
                process.destroy();
            }
        }
        return 0;
    }

    public static VersionInfo getSuVersionInfo() {
        VersionInfo info = new VersionInfo();
        Process process = null;
        String inLine = null;

        try {
            process = Runtime.getRuntime().exec("sh");
            DataOutputStream os = new DataOutputStream(process.getOutputStream());
            BufferedReader is = new BufferedReader(
                    new InputStreamReader(new DataInputStream(process.getInputStream())), 64);
            os.writeBytes("su -v\n");

            // We have to hold up the thread to make sure that we're ready to read
            // the stream, using increments of 5ms makes it return as quick as
            // possible, and limiting to 1000ms makes sure that it doesn't hang for
            // too long if there's a problem.
            for (int i = 0; i < 400; i++) {
                if (is.ready()) {
                    break;
                }
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    Log.w(TAG, "Sleep timer got interrupted...");
                }
            }
            if (is.ready()) {
                inLine = is.readLine();
                if (inLine != null) {
                    info.version = inLine;
                }
            } else {
                // If 'su -v' isn't supported, neither is 'su -V'. return legacy info
                os.writeBytes("exit\n");
                info.version = "legacy";
                info.versionCode = 0;
                return info;
            }

            os.writeBytes("su -v\n");

            // We have to hold up the thread to make sure that we're ready to read
            // the stream, using increments of 5ms makes it return as quick as
            // possible, and limiting to 1000ms makes sure that it doesn't hang for
            // too long if there's a problem.
            for (int i = 0; i < 400; i++) {
                if (is.ready()) {
                    break;
                }
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    Log.w(TAG, "Sleep timer got interrupted...");
                }
            }
            if (is.ready()) {
                inLine = is.readLine();
                if (inLine != null && Integer.parseInt(inLine.substring(0, 1)) > 2) {
                    inLine = null;
                    os.writeBytes("su -V\n");
                    inLine = is.readLine();
                    if (inLine != null) {
                        info.versionCode = Integer.parseInt(inLine);
                    }
                } else {
                    info.versionCode = 0;
                }
            } else {
                os.writeBytes("exit\n");
            }
        } catch (IOException e) {
            Log.e(TAG, "Problems reading current version.", e);
        } finally {
            if (process != null) {
                process.destroy();
            }
        }
        return info;
    }

    public static VersionInfo getSuperuserVersionInfo(Context context) {
        VersionInfo info = new VersionInfo();
        PackageManager pm = context.getPackageManager();
        try {
            PackageInfo pInfo = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_META_DATA);
            info.version = pInfo.versionName;
            info.versionCode = pInfo.versionCode;
        } catch (NameNotFoundException e) {
            Log.e(TAG, "Superuser is not installed?", e);
        }
        return info;
    }

    public static boolean isSuCurrent() {
        if (getSuVersionCode() < 10) {
            return false;
        }
        return true;
    }

    public static String formatDate(Context context, long date) {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        String format = prefs.getString("pref_date_format", "default");
        if (format.equals("default")) {
            return DateFormat.getDateFormat(context).format(date);
        } else {
            return (String) DateFormat.format(format, date);
        }
    }

    public static String formatTime(Context context, long time) {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        boolean hour24 = prefs.getBoolean("pref_24_hour_format", true);
        boolean showSeconds = prefs.getBoolean("pref_show_seconds", false);
        String hour = "kk";
        String min = "mm";
        String sec = ":ss";
        String post = "";

        if (hour24) {
            hour = "kk";
        } else {
            hour = "hh";
            post = "aa";
        }

        if (showSeconds) {
            sec = ":ss";
        } else {
            sec = "";
        }

        String format = String.format("%s:%s%s%s", hour, min, sec, post);
        return (String) DateFormat.format(format, time);
    }

    public static String formatDateTime(Context context, long date) {
        return formatDate(context, date) + " " + formatTime(context, date);
    }

    public static boolean elitePresent(Context context, boolean versionCheck, int minVersion) {
        PackageManager pm = context.getPackageManager();
        int sigs = pm.checkSignatures("com.noshufou.android.su", "com.noshufou.android.su.elite");
        if (sigs != PackageManager.SIGNATURE_MATCH) {
            return false;
        } else {
            if (versionCheck) {
                PackageInfo pi;
                try {
                    pi = pm.getPackageInfo("com.noshufou.android.su.elite", 0);
                    if (pi.versionCode >= minVersion) {
                        return true;
                    } else {
                        return false;
                    }
                } catch (NameNotFoundException e) {
                    return false;
                }
            } else {
                return true;
            }
        }
    }

    public static void launchPreferences(Context context) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
            context.startActivity(new Intent(context, PreferencesActivity.class));
        } else {
            context.startActivity(new Intent(context, PreferencesActivityHC.class));
        }
    }

    public static String getHash(String pin) {
        MessageDigest digest;
        try {
            digest = MessageDigest.getInstance("SHA-1");
        } catch (NoSuchAlgorithmException e) {
            Log.e("Utils", "NoSuchAlgorithm, storing in plain text...", e);
            return pin;
        }
        digest.reset();
        try {
            byte[] input = digest.digest(pin.getBytes("UTF-8"));
            String base64 = Base64.encode(input);
            return base64;
        } catch (UnsupportedEncodingException e) {
            Log.e("Utils", "UnsupportedEncoding, storing in plain text...", e);
            return pin;
        }
    }

    public static boolean checkPin(Context context, String pin) {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        String setPin = prefs.getString("pin", "");
        return getHash(pin).equals(setPin);
    }

    public static void toggleAppIcon(Context context, boolean newState) {
        ComponentName componentName = new ComponentName("com.noshufou.android.su", "com.noshufou.android.su.Su");
        context.getPackageManager().setComponentEnabledSetting(componentName,
                newState ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
                        : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                PackageManager.DONT_KILL_APP);
    }

    public static List<String> findMaliciousPackages(Context context) {
        List<String> maliciousApps = new ArrayList<String>();
        List<PackageInfo> installedApps = context.getPackageManager()
                .getInstalledPackages(PackageManager.GET_PERMISSIONS);

        for (PackageInfo pkg : installedApps) {
            int result = isPackageMalicious(context, pkg);
            if (result != 0) {
                maliciousApps.add(pkg.packageName + ":" + result);
            }
        }
        return maliciousApps;
    }

    public static int isPackageMalicious(Context context, PackageInfo packageInfo) {
        // If the package being checked is this one, it's not malicious
        if (packageInfo.packageName.equals(context.getPackageName())) {
            return MALICIOUS_NOT;
        }

        // If the package being checked shares a UID with Superuser, it's
        // probably malicious
        if (packageInfo.applicationInfo.uid == context.getApplicationInfo().uid) {
            return MALICIOUS_UID;
        }

        // Finally we check for any permissions that other apps should not have.
        if (packageInfo.requestedPermissions != null) {
            String[] bannedPermissions = new String[] { "com.noshufou.android.su.RESPOND",
                    "com.noshufou.android.su.provider.WRITE" };
            for (String s : packageInfo.requestedPermissions) {
                for (int i = 0; i < 2; i++) {
                    if (s.equals(bannedPermissions[i])
                            && context.getPackageManager().checkPermission(bannedPermissions[i],
                                    packageInfo.packageName) == PackageManager.PERMISSION_GRANTED) {
                        return i + 2;
                    }
                }
            }
        }

        return MALICIOUS_NOT;
    }

    public static void showOutdatedNotification(Context context) {
        if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean(Preferences.OUTDATED_NOTIFICATION,
                true)) {
            NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
            PendingIntent contentIntent = PendingIntent.getActivity(context, 0,
                    new Intent(context, UpdaterActivity.class), 0);
            Notification notification = new NotificationCompat.Builder(context).setSmallIcon(R.drawable.stat_su)
                    .setTicker(context.getText(R.string.notif_outdated_ticker)).setWhen(System.currentTimeMillis())
                    .setContentTitle(context.getText(R.string.notif_outdated_title))
                    .setContentText(context.getText(R.string.notif_outdated_text)).setContentIntent(contentIntent)
                    .setAutoCancel(true).setOnlyAlertOnce(true).getNotification();
            nm.notify(UpdaterService.NOTIFICATION_ID, notification);
        }
    }

    public static String whichSu() {
        for (String s : System.getenv("PATH").split(":")) {
            File su = new File(s + "/su");
            if (su.exists() && su.isFile()) {
                try {
                    if (su.getAbsolutePath().equals(su.getCanonicalPath())) {
                        return su.getAbsolutePath();
                    }
                } catch (IOException e) {
                    // If we get an exception here, it's probably not the right file,
                    // Log it and move on
                    Log.w(TAG, "IOException while finding canonical path of " + su.getAbsolutePath(), e);
                }
            }
        }
        return null;
    }

    public static String ensureSuTools(Context context) {
        File suTools = context.getFileStreamPath("sutools");
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        int sutoolsVersion = prefs.getInt("sutoolsVersion", 0);

        PackageManager pm = context.getPackageManager();
        int appVersionCode;
        try {
            PackageInfo info = pm.getPackageInfo(context.getPackageName(), 0);
            appVersionCode = info.versionCode;
        } catch (NameNotFoundException e) {
            appVersionCode = 0;
        }

        if (suTools.exists() && appVersionCode == sutoolsVersion) {
            return suTools.getAbsolutePath();
        }
        Log.d(TAG, "extracting sutools");

        try {
            copyFromAssets(context, "sutools-" + Build.CPU_ABI.split("-")[0], "sutools");
        } catch (IOException e) {
            Log.e(TAG, "Could not extract sutools");
            return null;
        }

        Process process;
        try {
            process = new ProcessBuilder().command("chmod", "700", suTools.getAbsolutePath())
                    .redirectErrorStream(true).start();
            process.waitFor();
            process.destroy();
        } catch (IOException e) {
            Log.e(TAG, "Failed to set filemode of sutools");
            return null;
        } catch (InterruptedException e) {
            Log.w(TAG, "process interrupted");
        }

        prefs.edit().putInt("sutoolsVersion", appVersionCode).commit();
        return suTools.getAbsolutePath();
    }

    public static final void copyFromAssets(Context context, String source, String destination) throws IOException {

        // read file from the apk
        InputStream is = context.getAssets().open(source);
        int size = is.available();
        byte[] buffer = new byte[size];
        is.read(buffer);
        is.close();

        // write files in app private storage
        FileOutputStream output = context.openFileOutput(destination, Context.MODE_PRIVATE);
        output.write(buffer);
        output.close();

        Log.d(TAG, source + " asset copied to " + destination);
    }

    public static final Boolean isSuid(Context context, String filename) {

        try {

            Process p = Runtime.getRuntime().exec(context.getFilesDir() + "/test -u " + filename);
            p.waitFor();
            if (p.exitValue() == 0) {
                Log.d(TAG, filename + " is set-user-ID");
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        Log.d(TAG, filename + " is not set-user-ID");
        return false;

    }

    public static ArrayList<String> run(String command) {
        return run("/system/bin/sh", command);
    }

    public static ArrayList<String> run(String shell, String command) {
        return run(shell, new String[] { command });
    }

    public static ArrayList<String> run(String shell, ArrayList<String> commands) {
        String[] commandsArray = new String[commands.size()];
        commands.toArray(commandsArray);
        return run(shell, commandsArray);
    }

    public static ArrayList<String> run(String shell, String[] commands) {
        ArrayList<String> output = new ArrayList<String>();

        try {
            Process process = Runtime.getRuntime().exec(shell);

            BufferedOutputStream shellInput = new BufferedOutputStream(process.getOutputStream());
            BufferedReader shellOutput = new BufferedReader(new InputStreamReader(process.getInputStream()));

            for (String command : commands) {
                Log.i(TAG, "command: " + command);
                shellInput.write((command + " 2>&1\n").getBytes());
            }

            shellInput.write("exit\n".getBytes());
            shellInput.flush();

            String line;
            while ((line = shellOutput.readLine()) != null) {
                Log.d(TAG, "command output: " + line);
                output.add(line);
            }

            process.waitFor();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return output;
    }

    public static boolean writeStoreFile(Context context, int uid, int execUid, String cmd, int allow) {
        File storeFile = getStoreFile(context, uid, execUid);
        if (storeFile.exists()) {
            return appendStoreFile(storeFile, cmd, allow);
        } else {
            HashMap<String, String> cmds = new HashMap<String, String>(1);
            cmds.put(cmd, getAllowString(allow));
            return writeStoreFile(storeFile, cmds);
        }
    }

    public static boolean deleteStoreFile(Context context, int uid, int execUid, String cmd) {
        File storeFile = getStoreFile(context, uid, execUid);
        if (!storeFile.exists())
            return true;
        HashMap<String, String> cmds = readStoreFile(storeFile);
        cmds.remove(cmd);
        if (cmds.isEmpty() || cmds.containsKey("any")) {
            return storeFile.delete();
        } else {
            return writeStoreFile(storeFile, cmds);
        }
    }

    private static String getAllowString(int allow) {
        switch (allow) {
        case AllowType.ALLOW:
            return "allow";
        case AllowType.DENY:
            return "deny";
        default:
            return "prompt";
        }
    }

    private static File getStoreFile(Context context, int uid, int execUid) {
        File storedDir = new File(context.getFilesDir().getAbsolutePath() + File.separator + "stored");
        storedDir.mkdirs();
        String fileName = uid + "-" + execUid;
        return new File(storedDir, fileName);
    }

    private static boolean writeStoreFile(File storeFile, HashMap<String, String> cmds) {
        try {
            OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(storeFile));
            if (cmds.containsKey("any")) {
                out.write(cmds.get("any") + '\n');
                out.write("any\n");
            } else {
                for (Map.Entry<String, String> entry : cmds.entrySet()) {
                    out.write(entry.getValue() + '\n');
                    out.write(entry.getKey() + '\n');
                }
            }
            out.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return false;
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return false;
        }
        return true;
    }

    private static boolean appendStoreFile(File storeFile, String cmd, int allow) {
        HashMap<String, String> cmds = readStoreFile(storeFile);
        cmds.put(cmd, getAllowString(allow));
        writeStoreFile(storeFile, cmds);
        return true;
    }

    private static HashMap<String, String> readStoreFile(File storeFile) {
        try {
            BufferedReader br = new BufferedReader(
                    new InputStreamReader(new DataInputStream(new FileInputStream(storeFile))));
            HashMap<String, String> cmds = new HashMap<String, String>();
            String allow, cmd;
            while ((allow = br.readLine()) != null && (cmd = br.readLine()) != null) {
                cmds.put(cmd, allow);
            }
            br.close();
            return cmds;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

    public static boolean writeDefaultStoreFile(Context context) {
        File storedDir = new File(context.getFilesDir().getAbsolutePath() + File.separator + "stored");
        storedDir.mkdirs();
        File defFile = new File(storedDir.getAbsolutePath() + File.separator + "default");
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        String action = prefs.getString(Preferences.AUTOMATIC_ACTION, "prompt");
        try {
            OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(defFile.getAbsolutePath()));
            out.write(action);
            out.write("\n");
            out.flush();
            out.close();
        } catch (FileNotFoundException e) {
            Log.w(TAG, "Default file not written", e);
            return false;
        } catch (IOException e) {
            Log.w(TAG, "Default file not written", e);
            return false;
        }
        return true;
    }

    public static boolean writeOptionsFile(Context context) {
        File storedDir = new File(context.getFilesDir().getAbsolutePath() + File.separator + "stored");
        storedDir.mkdirs();
        File optFile = new File(storedDir.getAbsolutePath() + File.separator + "options");
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        String ownerMode = prefs.getString(Preferences.USER_MODE, "owner_only");
        try {
            OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(optFile.getAbsolutePath()));
            out.write(ownerMode);
            out.write("\n");
            out.flush();
            out.close();
        } catch (FileNotFoundException e) {
            Log.w(TAG, "Options file not written", e);
            return false;
        } catch (IOException e) {
            Log.w(TAG, "Options file not written", e);
            return false;
        }
        return true;
    }

    public static boolean isUserOwner(Context context) {
        PackageManager pm = context.getPackageManager();
        try {
            ApplicationInfo ai = pm.getApplicationInfo(context.getPackageName(), 0);
            if (ai.uid < 99999) {
                return true;
            } else {
                return false;
            }
        } catch (NameNotFoundException e) {
            Log.e(TAG, "Divided by zero...");
            return false;
        }
    }

}