1 package com.koushikdutta.superuser.util;
3 import java.io.ByteArrayOutputStream;
4 import java.io.DataInputStream;
5 import java.io.DataOutputStream;
7 import java.io.FileInputStream;
8 import java.io.FileOutputStream;
9 import java.io.IOException;
10 import java.io.InputStream;
11 import java.security.MessageDigest;
13 import android.content.ContentValues;
14 import android.content.Context;
15 import android.database.Cursor;
16 import android.database.sqlite.SQLiteDatabase;
17 import android.database.sqlite.SQLiteOpenHelper;
18 import android.os.Build;
19 import android.util.Base64;
21 import com.koushikdutta.superuser.Helper;
23 public class Settings {
24 SQLiteDatabase mDatabase;
27 private Settings(Context context) {
29 SQLiteOpenHelper helper = new SQLiteOpenHelper(mContext, "settings.db", null, 1) {
30 private final static String mDDL = "CREATE TABLE settings (key TEXT PRIMARY KEY, value TEXT);";
33 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
38 public void onCreate(SQLiteDatabase db) {
42 mDatabase = helper.getWritableDatabase();
45 private static Settings mInstance;
47 public static Settings getInstance(Context context) {
48 if (mInstance == null) {
49 mInstance = new Settings(context.getApplicationContext());
54 public void setString(String name, String value) {
55 ContentValues cv = new ContentValues();
57 cv.put("value", value);
58 mDatabase.replace("settings", null, cv);
61 public String getString(String name) {
62 return getString(name, null);
65 public String getString(String name, String defaultValue) {
66 Cursor cursor = mDatabase.query("settings", new String[] { "value" }, "key='" + name + "'", null, null, null, null);
68 if (cursor.moveToNext())
69 return cursor.getString(0);
77 public void setInt(String name, int value) {
78 setString(name, ((Integer) value).toString());
81 public int getInt(String name, int defaultValue) {
83 return Integer.parseInt(getString(name, null));
85 catch (Exception ex) {
90 public void setLong(String name, long value) {
91 setString(name, ((Long) value).toString());
94 public long getLong(String name, long defaultValue) {
96 return Long.parseLong(getString(name, null));
98 catch (Exception ex) {
103 public void setBoolean(String name, boolean value) {
104 setString(name, ((Boolean) value).toString());
107 public boolean getBoolean(String name, boolean defaultValue) {
109 return Boolean.parseBoolean(getString(name, ((Boolean) defaultValue).toString()));
111 catch (Exception ex) {
112 ex.printStackTrace();
117 private static final String KEY_LOGGING = "logging";
118 public static boolean getLogging(Context context) {
119 return getInstance(context).getBoolean(KEY_LOGGING, true);
122 public static void setLogging(Context context, boolean logging) {
123 getInstance(context).setBoolean(KEY_LOGGING, logging);
126 private static final String KEY_TIMEOUT = "timeout";
127 public static final int REQUEST_TIMEOUT_DEFAULT = 30;
128 public static int getRequestTimeout(Context context) {
129 return getInstance(context).getInt(KEY_TIMEOUT, REQUEST_TIMEOUT_DEFAULT);
132 public static void setTimeout(Context context, int timeout) {
133 getInstance(context).setInt(KEY_TIMEOUT, timeout);
136 private static final String KEY_NOTIFICATION = "notification";
137 public static final int NOTIFICATION_TYPE_NONE = 0;
138 public static final int NOTIFICATION_TYPE_TOAST = 1;
139 public static final int NOTIFICATION_TYPE_NOTIFICATION = 2;
140 public static final int NOTIFICATION_TYPE_DEFAULT = NOTIFICATION_TYPE_TOAST;
141 public static int getNotificationType(Context context) {
142 switch (getInstance(context).getInt(KEY_NOTIFICATION, NOTIFICATION_TYPE_DEFAULT)) {
143 case NOTIFICATION_TYPE_NONE:
144 return NOTIFICATION_TYPE_NONE;
145 case NOTIFICATION_TYPE_NOTIFICATION:
146 return NOTIFICATION_TYPE_NOTIFICATION;
147 case NOTIFICATION_TYPE_TOAST:
148 return NOTIFICATION_TYPE_TOAST;
150 return NOTIFICATION_TYPE_DEFAULT;
154 public static void setNotificationType(Context context, int notification) {
155 getInstance(context).setInt(KEY_NOTIFICATION, notification);
158 public static final String KEY_PIN = "pin";
159 public static final boolean isPinProtected(Context context) {
160 return Settings.getInstance(context).getString(KEY_PIN) != null;
163 private static String digest(String value) {
164 // ok, there's honestly no point in digesting the pin.
165 // if someone gets a hold of the hash, there's really only like
166 // 10^n possible values to brute force, where N is generally
167 // 4. Ie, 10000. Yay, security theater. This really ought
169 if (value == null || value.length() == 0)
172 MessageDigest digester = MessageDigest.getInstance("MD5");
173 return Base64.encodeToString(digester.digest(value.getBytes()), Base64.DEFAULT);
175 catch (Exception e) {
180 public static void setPin(Context context, String pin) {
181 Settings.getInstance(context).setString(KEY_PIN, digest(pin));
184 public static boolean checkPin(Context context, String pin) {
186 String hashed = Settings.getInstance(context).getString(KEY_PIN);
188 return hashed == null || hashed.length() == 0;
189 return pin.equals(hashed);
192 private static final String KEY_REQUIRE_PREMISSION = "require_permission";
193 public static boolean getRequirePermission(Context context) {
194 return getInstance(context).getBoolean(KEY_REQUIRE_PREMISSION, false);
197 public static void setRequirePermission(Context context, boolean require) {
198 getInstance(context).setBoolean(KEY_REQUIRE_PREMISSION, require);
201 private static final String KEY_AUTOMATIC_RESPONSE = "automatic_response";
202 public static final int AUTOMATIC_RESPONSE_PROMPT = 0;
203 public static final int AUTOMATIC_RESPONSE_ALLOW = 1;
204 public static final int AUTOMATIC_RESPONSE_DENY = 2;
205 public static final int AUTOMATIC_RESPONSE_DEFAULT = AUTOMATIC_RESPONSE_PROMPT;
206 public static int getAutomaticResponse(Context context) {
207 switch (getInstance(context).getInt(KEY_AUTOMATIC_RESPONSE, AUTOMATIC_RESPONSE_DEFAULT)) {
208 case AUTOMATIC_RESPONSE_ALLOW:
209 return AUTOMATIC_RESPONSE_ALLOW;
210 case AUTOMATIC_RESPONSE_PROMPT:
211 return AUTOMATIC_RESPONSE_PROMPT;
212 case AUTOMATIC_RESPONSE_DENY:
213 return AUTOMATIC_RESPONSE_DENY;
215 return AUTOMATIC_RESPONSE_DEFAULT;
219 public static void setAutomaticResponse(Context context, int response) {
220 getInstance(context).setInt(KEY_AUTOMATIC_RESPONSE, response);
224 static public String readFile(String filename) throws IOException {
225 return readFile(new File(filename));
228 static public String readFile(File file) throws IOException {
229 byte[] buffer = new byte[(int) file.length()];
230 DataInputStream input = new DataInputStream(new FileInputStream(file));
231 input.readFully(buffer);
232 return new String(buffer);
235 public static void writeFile(File file, String string) throws IOException {
236 writeFile(file.getAbsolutePath(), string);
239 public static void writeFile(String file, String string) throws IOException {
240 File f = new File(file);
241 f.getParentFile().mkdirs();
242 DataOutputStream dout = new DataOutputStream(new FileOutputStream(f));
243 dout.write(string.getBytes());
247 public static byte[] readToEndAsArray(InputStream input) throws IOException {
248 DataInputStream dis = new DataInputStream(input);
249 byte[] stuff = new byte[1024];
250 ByteArrayOutputStream buff = new ByteArrayOutputStream();
252 while ((read = dis.read(stuff)) != -1)
254 buff.write(stuff, 0, read);
257 return buff.toByteArray();
260 public static String readToEnd(InputStream input) throws IOException {
261 return new String(readToEndAsArray(input));
264 public static final int MULTIUSER_MODE_OWNER_ONLY = 0;
265 public static final int MULTIUSER_MODE_OWNER_MANAGED = 1;
266 public static final int MULTIUSER_MODE_USER = 2;
267 public static final int MULTIUSER_MODE_NONE = 3;
269 private static final String MULTIUSER_VALUE_OWNER_ONLY = "owner";
270 private static final String MULTIUSER_VALUE_OWNER_MANAGED = "managed";
271 private static final String MULTIUSER_VALUE_USER = "user";
273 public static final int getMultiuserMode(Context context) {
274 if (Build.VERSION.SDK_INT < 17)
275 return MULTIUSER_MODE_NONE;
277 if (!Helper.supportsMultipleUsers(context))
278 return MULTIUSER_MODE_NONE;
282 if (Helper.isAdminUser(context)) {
283 File file = context.getFileStreamPath("multiuser_mode");
284 mode = readFile(file);
287 Process p = Runtime.getRuntime().exec("su -u");
288 mode = readToEnd(p.getInputStream()).trim();
291 if (MULTIUSER_VALUE_OWNER_MANAGED.equals(mode))
292 return MULTIUSER_MODE_OWNER_MANAGED;
293 if (MULTIUSER_VALUE_USER.equals(mode))
294 return MULTIUSER_MODE_USER;
295 if (MULTIUSER_VALUE_OWNER_ONLY.equals(mode))
296 return MULTIUSER_MODE_OWNER_ONLY;
298 catch (Exception e) {
300 return MULTIUSER_MODE_OWNER_ONLY;
303 public static void setMultiuserMode(Context context, int mode) {
304 if (!Helper.isAdminUser(context))
307 File file = context.getFileStreamPath("multiuser_mode");
309 case MULTIUSER_MODE_OWNER_MANAGED:
310 writeFile(file, MULTIUSER_VALUE_OWNER_MANAGED);
312 case MULTIUSER_MODE_USER:
313 writeFile(file, MULTIUSER_VALUE_USER);
315 case MULTIUSER_MODE_NONE:
319 writeFile(file, MULTIUSER_VALUE_OWNER_ONLY);
323 catch (Exception ex) {