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.lang.reflect.Method;
12 import java.security.MessageDigest;
14 import android.app.Activity;
15 import android.content.ContentValues;
16 import android.content.Context;
17 import android.database.Cursor;
18 import android.database.sqlite.SQLiteDatabase;
19 import android.os.Build;
20 import android.text.TextUtils;
21 import android.util.Base64;
22 import android.util.Log;
24 import com.koushikdutta.superuser.Helper;
25 import com.koushikdutta.superuser.db.SuperuserDatabaseHelper;
27 public class Settings {
28 static final String TAG = "Superuser";
29 SQLiteDatabase mDatabase;
32 public static void setString(Context context, String name, String value) {
33 ContentValues cv = new ContentValues();
35 cv.put("value", value);
36 SQLiteDatabase db = new SuperuserDatabaseHelper(context).getWritableDatabase();
38 db.replace("settings", null, cv);
45 public static String getString(Context context, String name) {
46 return getString(context, name, null);
49 public static String getString(Context context, String name, String defaultValue) {
50 SQLiteDatabase db = new SuperuserDatabaseHelper(context).getReadableDatabase();
51 Cursor cursor = db.query("settings", new String[] { "value" }, "key='" + name + "'", null, null, null, null);
53 if (cursor.moveToNext())
54 return cursor.getString(0);
63 public static void setInt(Context context, String name, int value) {
64 setString(context, name, ((Integer) value).toString());
67 public static int getInt(Context context, String name, int defaultValue) {
69 return Integer.parseInt(getString(context, name, null));
71 catch (Exception ex) {
76 public static void setLong(Context context, String name, long value) {
77 setString(context, name, ((Long) value).toString());
80 public static long getLong(Context context, String name, long defaultValue) {
82 return Long.parseLong(getString(context, name, null));
84 catch (Exception ex) {
89 public static void setBoolean(Context context, String name, boolean value) {
90 setString(context, name, ((Boolean) value).toString());
93 public static boolean getBoolean(Context context, String name, boolean defaultValue) {
95 return Boolean.parseBoolean(getString(context, name, ((Boolean) defaultValue).toString()));
97 catch (Exception ex) {
103 private static final String KEY_LOGGING = "logging";
104 public static boolean getLogging(Context context) {
105 return getBoolean(context, KEY_LOGGING, true);
108 public static void setLogging(Context context, boolean logging) {
109 setBoolean(context, KEY_LOGGING, logging);
112 private static final String KEY_TIMEOUT = "timeout";
113 public static final int REQUEST_TIMEOUT_DEFAULT = 30;
114 public static int getRequestTimeout(Context context) {
115 return getInt(context, KEY_TIMEOUT, REQUEST_TIMEOUT_DEFAULT);
118 public static void setTimeout(Context context, int timeout) {
119 setInt(context, KEY_TIMEOUT, timeout);
122 private static final String KEY_NOTIFICATION = "notification";
123 public static final int NOTIFICATION_TYPE_NONE = 0;
124 public static final int NOTIFICATION_TYPE_TOAST = 1;
125 public static final int NOTIFICATION_TYPE_NOTIFICATION = 2;
126 public static final int NOTIFICATION_TYPE_DEFAULT = NOTIFICATION_TYPE_TOAST;
127 public static int getNotificationType(Context context) {
128 switch (getInt(context, KEY_NOTIFICATION, NOTIFICATION_TYPE_DEFAULT)) {
129 case NOTIFICATION_TYPE_NONE:
130 return NOTIFICATION_TYPE_NONE;
131 case NOTIFICATION_TYPE_NOTIFICATION:
132 return NOTIFICATION_TYPE_NOTIFICATION;
133 case NOTIFICATION_TYPE_TOAST:
134 return NOTIFICATION_TYPE_TOAST;
136 return NOTIFICATION_TYPE_DEFAULT;
140 public static void setNotificationType(Context context, int notification) {
141 setInt(context, KEY_NOTIFICATION, notification);
144 public static final String KEY_PIN = "pin";
145 public static final boolean isPinProtected(Context context) {
146 return Settings.getString(context, KEY_PIN) != null;
149 private static String digest(String value) {
150 // ok, there's honestly no point in digesting the pin.
151 // if someone gets a hold of the hash, there's really only like
152 // 10^n possible values to brute force, where N is generally
153 // 4. Ie, 10000. Yay, security theater. This really ought
155 if (TextUtils.isEmpty(value))
158 MessageDigest digester = MessageDigest.getInstance("MD5");
159 return Base64.encodeToString(digester.digest(value.getBytes()), Base64.DEFAULT);
161 catch (Exception e) {
166 public static void setPin(Context context, String pin) {
167 Settings.setString(context, KEY_PIN, digest(pin));
170 public static boolean checkPin(Context context, String pin) {
172 String hashed = Settings.getString(context, KEY_PIN);
173 if (TextUtils.isEmpty(pin))
174 return TextUtils.isEmpty(hashed);
175 return pin.equals(hashed);
178 private static final String KEY_REQUIRE_PREMISSION = "require_permission";
179 public static boolean getRequirePermission(Context context) {
180 return getBoolean(context, KEY_REQUIRE_PREMISSION, false);
183 public static void setRequirePermission(Context context, boolean require) {
184 setBoolean(context, KEY_REQUIRE_PREMISSION, require);
187 private static final String KEY_AUTOMATIC_RESPONSE = "automatic_response";
188 public static final int AUTOMATIC_RESPONSE_PROMPT = 0;
189 public static final int AUTOMATIC_RESPONSE_ALLOW = 1;
190 public static final int AUTOMATIC_RESPONSE_DENY = 2;
191 public static final int AUTOMATIC_RESPONSE_DEFAULT = AUTOMATIC_RESPONSE_PROMPT;
192 public static int getAutomaticResponse(Context context) {
193 switch (getInt(context, KEY_AUTOMATIC_RESPONSE, AUTOMATIC_RESPONSE_DEFAULT)) {
194 case AUTOMATIC_RESPONSE_ALLOW:
195 return AUTOMATIC_RESPONSE_ALLOW;
196 case AUTOMATIC_RESPONSE_PROMPT:
197 return AUTOMATIC_RESPONSE_PROMPT;
198 case AUTOMATIC_RESPONSE_DENY:
199 return AUTOMATIC_RESPONSE_DENY;
201 return AUTOMATIC_RESPONSE_DEFAULT;
205 public static void setAutomaticResponse(Context context, int response) {
206 setInt(context, KEY_AUTOMATIC_RESPONSE, response);
210 static public String readFile(String filename) throws IOException {
211 return readFile(new File(filename));
214 static public String readFile(File file) throws IOException {
215 byte[] buffer = new byte[(int) file.length()];
216 DataInputStream input = new DataInputStream(new FileInputStream(file));
217 input.readFully(buffer);
219 return new String(buffer);
222 public static void writeFile(File file, String string) throws IOException {
223 writeFile(file.getAbsolutePath(), string);
226 public static void writeFile(String file, String string) throws IOException {
227 File f = new File(file);
228 f.getParentFile().mkdirs();
229 DataOutputStream dout = new DataOutputStream(new FileOutputStream(f));
230 dout.write(string.getBytes());
234 public static byte[] readToEndAsArray(InputStream input) throws IOException {
235 DataInputStream dis = new DataInputStream(input);
236 byte[] stuff = new byte[1024];
237 ByteArrayOutputStream buff = new ByteArrayOutputStream();
239 while ((read = dis.read(stuff)) != -1)
241 buff.write(stuff, 0, read);
244 return buff.toByteArray();
247 public static String readToEnd(InputStream input) throws IOException {
248 return new String(readToEndAsArray(input));
251 public static final int MULTIUSER_MODE_OWNER_ONLY = 0;
252 public static final int MULTIUSER_MODE_OWNER_MANAGED = 1;
253 public static final int MULTIUSER_MODE_USER = 2;
254 public static final int MULTIUSER_MODE_NONE = 3;
256 private static final String MULTIUSER_VALUE_OWNER_ONLY = "owner";
257 private static final String MULTIUSER_VALUE_OWNER_MANAGED = "managed";
258 private static final String MULTIUSER_VALUE_USER = "user";
260 public static final int getMultiuserMode(Context context) {
261 if (Build.VERSION.SDK_INT < 17)
262 return MULTIUSER_MODE_NONE;
264 if (!Helper.supportsMultipleUsers(context))
265 return MULTIUSER_MODE_NONE;
269 if (Helper.isAdminUser(context)) {
270 File file = context.getFileStreamPath("multiuser_mode");
271 mode = readFile(file);
274 Process p = Runtime.getRuntime().exec("su -u");
275 mode = readToEnd(p.getInputStream()).trim();
278 if (MULTIUSER_VALUE_OWNER_MANAGED.equals(mode))
279 return MULTIUSER_MODE_OWNER_MANAGED;
280 if (MULTIUSER_VALUE_USER.equals(mode))
281 return MULTIUSER_MODE_USER;
282 if (MULTIUSER_VALUE_OWNER_ONLY.equals(mode))
283 return MULTIUSER_MODE_OWNER_ONLY;
285 catch (Exception e) {
287 return MULTIUSER_MODE_OWNER_ONLY;
290 public static void setMultiuserMode(Context context, int mode) {
291 if (!Helper.isAdminUser(context))
294 File file = context.getFileStreamPath("multiuser_mode");
296 case MULTIUSER_MODE_OWNER_MANAGED:
297 writeFile(file, MULTIUSER_VALUE_OWNER_MANAGED);
299 case MULTIUSER_MODE_USER:
300 writeFile(file, MULTIUSER_VALUE_USER);
302 case MULTIUSER_MODE_NONE:
306 writeFile(file, MULTIUSER_VALUE_OWNER_ONLY);
310 catch (Exception ex) {
315 public static final int SUPERUSER_ACCESS_DISABLED = 0;
316 public static final int SUPERUSER_ACCESS_APPS_ONLY = 1;
317 public static final int SUPERUSER_ACCESS_ADB_ONLY = 2;
318 public static final int SUPERUSER_ACCESS_APPS_AND_ADB = 3;
319 public static int getSuperuserAccess() {
321 Class<?> c = Class.forName("android.os.SystemProperties");
322 Method m = c.getMethod("get", String.class);
323 String value = (String)m.invoke(null, "persist.sys.root_access");
324 int val = Integer.valueOf(value);
326 case SUPERUSER_ACCESS_DISABLED:
327 case SUPERUSER_ACCESS_APPS_ONLY:
328 case SUPERUSER_ACCESS_ADB_ONLY:
329 case SUPERUSER_ACCESS_APPS_AND_ADB:
332 return SUPERUSER_ACCESS_APPS_AND_ADB;
335 catch (Exception e) {
336 return SUPERUSER_ACCESS_APPS_AND_ADB;
340 public static void setSuperuserAccess(int mode) {
342 if (android.os.Process.myUid() == android.os.Process.SYSTEM_UID) {
343 Class<?> c = Class.forName("android.os.SystemProperties");
344 Method m = c.getMethod("set", String.class, String.class);
345 m.invoke(null, "persist.sys.root_access", String.valueOf(mode));
346 if (mode == getSuperuserAccess()) return;
348 String command = "setprop persist.sys.root_access " + mode;
349 Process p = Runtime.getRuntime().exec("su");
350 p.getOutputStream().write(command.getBytes());
351 p.getOutputStream().close();
352 int ret = p.waitFor();
353 if (ret != 0) Log.w(TAG, "su failed: " + ret);
355 catch (Exception ex) {
356 Log.w(TAG, "got exception: ", ex);
360 private static final String CHECK_SU_QUIET = "check_su_quiet";
361 public static final int getCheckSuQuietCounter(Context context) {
362 return getInt(context, CHECK_SU_QUIET, 0);
365 public static final void setCheckSuQuietCounter(Context context, int counter) {
366 setInt(context, CHECK_SU_QUIET, counter);
369 private static final String KEY_THEME = "theme";
370 public static final int THEME_LIGHT = 0;
371 public static final int THEME_DARK = 1;
372 public static void applyDarkThemeSetting(Activity activity, int dark) {
373 if (!"com.koushikdutta.superuser".equals(activity.getPackageName()))
376 if (getTheme(activity) == THEME_DARK)
377 activity.setTheme(dark);
379 catch (Exception e) {
383 public static final int getTheme(Context context) {
384 return getInt(context, KEY_THEME, THEME_LIGHT);
387 public static final void setTheme(Context context, int theme) {
388 setInt(context, KEY_THEME, theme);