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.text.TextUtils;
20 import android.util.Base64;
22 import com.koushikdutta.superuser.Helper;
24 public class Settings {
25 SQLiteDatabase mDatabase;
28 private Settings(Context context) {
30 SQLiteOpenHelper helper = new SQLiteOpenHelper(mContext, "settings.db", null, 1) {
31 private final static String mDDL = "CREATE TABLE settings (key TEXT PRIMARY KEY, value TEXT);";
34 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
39 public void onCreate(SQLiteDatabase db) {
43 mDatabase = helper.getWritableDatabase();
46 private static Settings mInstance;
48 public static Settings getInstance(Context context) {
49 if (mInstance == null) {
50 mInstance = new Settings(context.getApplicationContext());
55 public void setString(String name, String value) {
56 ContentValues cv = new ContentValues();
58 cv.put("value", value);
59 mDatabase.replace("settings", null, cv);
62 public String getString(String name) {
63 return getString(name, null);
66 public String getString(String name, String defaultValue) {
67 Cursor cursor = mDatabase.query("settings", new String[] { "value" }, "key='" + name + "'", null, null, null, null);
69 if (cursor.moveToNext())
70 return cursor.getString(0);
78 public void setInt(String name, int value) {
79 setString(name, ((Integer) value).toString());
82 public int getInt(String name, int defaultValue) {
84 return Integer.parseInt(getString(name, null));
86 catch (Exception ex) {
91 public void setLong(String name, long value) {
92 setString(name, ((Long) value).toString());
95 public long getLong(String name, long defaultValue) {
97 return Long.parseLong(getString(name, null));
99 catch (Exception ex) {
104 public void setBoolean(String name, boolean value) {
105 setString(name, ((Boolean) value).toString());
108 public boolean getBoolean(String name, boolean defaultValue) {
110 return Boolean.parseBoolean(getString(name, ((Boolean) defaultValue).toString()));
112 catch (Exception ex) {
113 ex.printStackTrace();
118 private static final String KEY_LOGGING = "logging";
119 public static boolean getLogging(Context context) {
120 return getInstance(context).getBoolean(KEY_LOGGING, true);
123 public static void setLogging(Context context, boolean logging) {
124 getInstance(context).setBoolean(KEY_LOGGING, logging);
127 private static final String KEY_TIMEOUT = "timeout";
128 public static final int REQUEST_TIMEOUT_DEFAULT = 30;
129 public static int getRequestTimeout(Context context) {
130 return getInstance(context).getInt(KEY_TIMEOUT, REQUEST_TIMEOUT_DEFAULT);
133 public static void setTimeout(Context context, int timeout) {
134 getInstance(context).setInt(KEY_TIMEOUT, timeout);
137 private static final String KEY_NOTIFICATION = "notification";
138 public static final int NOTIFICATION_TYPE_NONE = 0;
139 public static final int NOTIFICATION_TYPE_TOAST = 1;
140 public static final int NOTIFICATION_TYPE_NOTIFICATION = 2;
141 public static final int NOTIFICATION_TYPE_DEFAULT = NOTIFICATION_TYPE_TOAST;
142 public static int getNotificationType(Context context) {
143 switch (getInstance(context).getInt(KEY_NOTIFICATION, NOTIFICATION_TYPE_DEFAULT)) {
144 case NOTIFICATION_TYPE_NONE:
145 return NOTIFICATION_TYPE_NONE;
146 case NOTIFICATION_TYPE_NOTIFICATION:
147 return NOTIFICATION_TYPE_NOTIFICATION;
148 case NOTIFICATION_TYPE_TOAST:
149 return NOTIFICATION_TYPE_TOAST;
151 return NOTIFICATION_TYPE_DEFAULT;
155 public static void setNotificationType(Context context, int notification) {
156 getInstance(context).setInt(KEY_NOTIFICATION, notification);
159 public static final String KEY_PIN = "pin";
160 public static final boolean isPinProtected(Context context) {
161 return Settings.getInstance(context).getString(KEY_PIN) != null;
164 private static String digest(String value) {
165 // ok, there's honestly no point in digesting the pin.
166 // if someone gets a hold of the hash, there's really only like
167 // 10^n possible values to brute force, where N is generally
168 // 4. Ie, 10000. Yay, security theater. This really ought
170 if (TextUtils.isEmpty(value))
173 MessageDigest digester = MessageDigest.getInstance("MD5");
174 return Base64.encodeToString(digester.digest(value.getBytes()), Base64.DEFAULT);
176 catch (Exception e) {
181 public static void setPin(Context context, String pin) {
182 Settings.getInstance(context).setString(KEY_PIN, digest(pin));
185 public static boolean checkPin(Context context, String pin) {
187 String hashed = Settings.getInstance(context).getString(KEY_PIN);
188 if (TextUtils.isEmpty(pin))
189 return TextUtils.isEmpty(hashed);
190 return pin.equals(hashed);
193 private static final String KEY_REQUIRE_PREMISSION = "require_permission";
194 public static boolean getRequirePermission(Context context) {
195 return getInstance(context).getBoolean(KEY_REQUIRE_PREMISSION, false);
198 public static void setRequirePermission(Context context, boolean require) {
199 getInstance(context).setBoolean(KEY_REQUIRE_PREMISSION, require);
202 private static final String KEY_AUTOMATIC_RESPONSE = "automatic_response";
203 public static final int AUTOMATIC_RESPONSE_PROMPT = 0;
204 public static final int AUTOMATIC_RESPONSE_ALLOW = 1;
205 public static final int AUTOMATIC_RESPONSE_DENY = 2;
206 public static final int AUTOMATIC_RESPONSE_DEFAULT = AUTOMATIC_RESPONSE_PROMPT;
207 public static int getAutomaticResponse(Context context) {
208 switch (getInstance(context).getInt(KEY_AUTOMATIC_RESPONSE, AUTOMATIC_RESPONSE_DEFAULT)) {
209 case AUTOMATIC_RESPONSE_ALLOW:
210 return AUTOMATIC_RESPONSE_ALLOW;
211 case AUTOMATIC_RESPONSE_PROMPT:
212 return AUTOMATIC_RESPONSE_PROMPT;
213 case AUTOMATIC_RESPONSE_DENY:
214 return AUTOMATIC_RESPONSE_DENY;
216 return AUTOMATIC_RESPONSE_DEFAULT;
220 public static void setAutomaticResponse(Context context, int response) {
221 getInstance(context).setInt(KEY_AUTOMATIC_RESPONSE, response);
225 static public String readFile(String filename) throws IOException {
226 return readFile(new File(filename));
229 static public String readFile(File file) throws IOException {
230 byte[] buffer = new byte[(int) file.length()];
231 DataInputStream input = new DataInputStream(new FileInputStream(file));
232 input.readFully(buffer);
233 return new String(buffer);
236 public static void writeFile(File file, String string) throws IOException {
237 writeFile(file.getAbsolutePath(), string);
240 public static void writeFile(String file, String string) throws IOException {
241 File f = new File(file);
242 f.getParentFile().mkdirs();
243 DataOutputStream dout = new DataOutputStream(new FileOutputStream(f));
244 dout.write(string.getBytes());
248 public static byte[] readToEndAsArray(InputStream input) throws IOException {
249 DataInputStream dis = new DataInputStream(input);
250 byte[] stuff = new byte[1024];
251 ByteArrayOutputStream buff = new ByteArrayOutputStream();
253 while ((read = dis.read(stuff)) != -1)
255 buff.write(stuff, 0, read);
258 return buff.toByteArray();
261 public static String readToEnd(InputStream input) throws IOException {
262 return new String(readToEndAsArray(input));
265 public static final int MULTIUSER_MODE_OWNER_ONLY = 0;
266 public static final int MULTIUSER_MODE_OWNER_MANAGED = 1;
267 public static final int MULTIUSER_MODE_USER = 2;
268 public static final int MULTIUSER_MODE_NONE = 3;
270 private static final String MULTIUSER_VALUE_OWNER_ONLY = "owner";
271 private static final String MULTIUSER_VALUE_OWNER_MANAGED = "managed";
272 private static final String MULTIUSER_VALUE_USER = "user";
274 public static final int getMultiuserMode(Context context) {
275 if (Build.VERSION.SDK_INT < 17)
276 return MULTIUSER_MODE_NONE;
278 if (!Helper.supportsMultipleUsers(context))
279 return MULTIUSER_MODE_NONE;
283 if (Helper.isAdminUser(context)) {
284 File file = context.getFileStreamPath("multiuser_mode");
285 mode = readFile(file);
288 Process p = Runtime.getRuntime().exec("su -u");
289 mode = readToEnd(p.getInputStream()).trim();
292 if (MULTIUSER_VALUE_OWNER_MANAGED.equals(mode))
293 return MULTIUSER_MODE_OWNER_MANAGED;
294 if (MULTIUSER_VALUE_USER.equals(mode))
295 return MULTIUSER_MODE_USER;
296 if (MULTIUSER_VALUE_OWNER_ONLY.equals(mode))
297 return MULTIUSER_MODE_OWNER_ONLY;
299 catch (Exception e) {
301 return MULTIUSER_MODE_OWNER_ONLY;
304 public static void setMultiuserMode(Context context, int mode) {
305 if (!Helper.isAdminUser(context))
308 File file = context.getFileStreamPath("multiuser_mode");
310 case MULTIUSER_MODE_OWNER_MANAGED:
311 writeFile(file, MULTIUSER_VALUE_OWNER_MANAGED);
313 case MULTIUSER_MODE_USER:
314 writeFile(file, MULTIUSER_VALUE_USER);
316 case MULTIUSER_MODE_NONE:
320 writeFile(file, MULTIUSER_VALUE_OWNER_ONLY);
324 catch (Exception ex) {