android:protectionLevel="signatureOrSystem" />
<application
+ android:name=".SuApplication"
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
- android:launchMode="singleTask"
- android:name="com.koushikdutta.superuser.MainActivity"
- android:label="@string/app_name"
- android:permission="com.koushikdutta.superuser.REQUEST" >
+ android:configChanges="keyboardHidden|orientation|screenSize"
+ android:name=".MainActivity"
+ android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
-
- <activity android:name="com.koushikdutta.superuser.MultitaskSuRequestActivity" android:theme="@style/AppTheme" android:label="@string/request" />
+ <activity
+ android:configChanges="keyboardHidden|orientation|screenSize"
+ android:name=".RequestActivity"
+ android:label="@string/app_name"
+ android:launchMode="singleTask"
+ android:permission="com.koushikdutta.superuser.REQUEST" />
+ <activity
+ android:configChanges="keyboardHidden|orientation|screenSize"
+ android:name=".MultitaskSuRequestActivity"
+ android:label="@string/request"
+ android:theme="@style/AppTheme" />
<receiver
android:name=".SuReceiver"
public final class R {
public static final class attr {
}
+ public static final class color {
+ public static final int background_dark=0x7f070000;
+ }
public static final class dimen {
/** Default screen margins, per the Android Design guidelines.
public static final int ic_launcher=0x7f020000;
}
public static final class id {
- public static final int allow=0x7f07000c;
- public static final int allow_temporarily=0x7f07000a;
- public static final int always_allow=0x7f070009;
- public static final int app_header=0x7f070003;
- public static final int command_header=0x7f070006;
- public static final int deny=0x7f07000b;
- public static final int image=0x7f07000d;
- public static final int incoming=0x7f070000;
- public static final int list=0x7f070007;
- public static final int package_header=0x7f070004;
- public static final int ready=0x7f070001;
- public static final int request=0x7f070002;
- public static final int summary=0x7f07000f;
- public static final int title=0x7f07000e;
- public static final int uid_header=0x7f070005;
- public static final int unknown=0x7f070008;
+ public static final int allow=0x7f080012;
+ public static final int app_header=0x7f080000;
+ public static final int command_header=0x7f080003;
+ public static final int deny=0x7f080011;
+ public static final int image=0x7f080005;
+ public static final int incoming=0x7f080009;
+ public static final int list=0x7f080004;
+ public static final int package_header=0x7f080001;
+ public static final int ready=0x7f08000a;
+ public static final int remember=0x7f08000d;
+ public static final int remember_for=0x7f08000f;
+ public static final int remember_forever=0x7f080010;
+ public static final int request=0x7f08000b;
+ public static final int root=0x7f080008;
+ public static final int summary=0x7f080007;
+ public static final int this_time_only=0x7f08000e;
+ public static final int title=0x7f080006;
+ public static final int uid_header=0x7f080002;
+ public static final int unknown=0x7f08000c;
}
public static final class layout {
- public static final int activity_main=0x7f030000;
- public static final int packageinfo=0x7f030001;
+ public static final int app_info=0x7f030000;
+ public static final int app_layout=0x7f030001;
+ public static final int packageinfo=0x7f030002;
+ public static final int request=0x7f030003;
+ public static final int request_buttons=0x7f030004;
}
public static final class string {
public static final int allow=0x7f050001;
- public static final int allow_temporarily=0x7f050008;
- public static final int always_allow=0x7f050009;
- public static final int app_header=0x7f05000b;
+ public static final int app_header=0x7f050009;
public static final int app_name=0x7f050000;
public static final int application_request=0x7f050005;
- public static final int command_header=0x7f05000d;
+ public static final int command_header=0x7f05000b;
public static final int deny=0x7f050002;
public static final int info=0x7f050006;
- public static final int package_header=0x7f05000a;
+ public static final int package_header=0x7f050008;
+ public static final int remember_for=0x7f05000d;
+ public static final int remember_forever=0x7f05000e;
public static final int request=0x7f050007;
public static final int status_incoming=0x7f050003;
- public static final int uid_header=0x7f05000c;
+ public static final int this_time_only=0x7f05000c;
+ public static final int uid_header=0x7f05000a;
public static final int unknown_uid=0x7f050004;
}
public static final class style {
/** API 14 theme customizations can go here.
- API 14 theme customizations can go here.
*/
public static final int AppBaseDarkTheme=0x7f060002;
/**
backward-compatibility can go here.
- Base application theme for API 11+. This theme completely replaces
- AppBaseTheme from res/values/styles.xml on API 11+ devices.
-
- API 11 theme customizations can go here.
-
Base application theme for API 14+. This theme completely replaces
AppBaseTheme from BOTH res/values/styles.xml and
res/values-v11/styles.xml on API 14+ devices.
All customizations that are NOT specific to a particular API-level can go here.
*/
public static final int AppTheme=0x7f060001;
- public static final int Dialog=0x7f060005;
public static final int FlatButton=0x7f060004;
}
}
return -1;
}
-int send_result(struct su_context *ctx, allow_t allow) {
+int send_result(struct su_context *ctx, policy_t allow) {
return 0;
}
int send_request(struct su_context *ctx) {
- pid_t pid;
-
// if su is operating in MULTIUSER_MODEL_OWNER,
// and the user requestor is not the owner,
// the owner needs to be notified of the request.
return -1;
}
- pid = fork();
- if (!pid) {
- if (needs_owner_login_prompt) {
- // in multiuser mode, the owner gets the su prompt
- pid = fork();
- if (!pid) {
- char notify_command[ARG_MAX];
-
- // start the activity that confirms the request
- snprintf(notify_command, sizeof(notify_command),
- "exec /system/bin/am " ACTION_NOTIFY " --ei caller_uid %d --user %d",
- ctx->from.uid, ctx->user.android_user_id);
- return silent_run(notify_command);
- }
- }
-
- char request_command[ARG_MAX];
+ int ret;
+ if (needs_owner_login_prompt) {
+ // in multiuser mode, the owner gets the su prompt
+ char notify_command[ARG_MAX];
// start the activity that confirms the request
- snprintf(request_command, sizeof(request_command),
- "exec /system/bin/am " ACTION_REQUEST " --es socket '%s' %s",
- ctx->sock_path, user);
+ snprintf(notify_command, sizeof(notify_command),
+ "exec /system/bin/am " ACTION_NOTIFY " --ei caller_uid %d --user %d",
+ ctx->from.uid, ctx->user.android_user_id);
- return silent_run(request_command);
- }
-
- /* Parent */
- if (pid < 0) {
- PLOGE("fork");
- return -1;
+ int ret = silent_run(notify_command);
+ if (ret) {
+ return ret;
+ }
}
- return 0;
+
+ char request_command[ARG_MAX];
+
+ // start the activity that confirms the request
+ snprintf(request_command, sizeof(request_command),
+ "exec /system/bin/am " ACTION_REQUEST " --es socket '%s' %s",
+ ctx->sock_path, user);
+
+ return silent_run(request_command);
}
#include <stdio.h>
#include <limits.h>
#include <sqlite3.h>
+#include <time.h>
#include "su.h"
-static int database_callback(void *NotUsed, int argc, char **argv, char **azColName){
+struct callback_data_t {
+ struct su_context *ctx;
+ policy_t policy;
+};
+
+static int database_callback(void *v, int argc, char **argv, char **azColName){
+ struct callback_data_t *data = (struct callback_data_t *)v;
+ int command_match = 0;
+ policy_t policy = DENY;
int i;
- for(i=0; i<argc; i++){
- printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
+ time_t until = 0;
+ for(i = 0; i < argc; i++) {
+ if (strcmp(azColName[i], "policy") == 0) {
+ if (argv[i] == NULL) {
+ policy = DENY;
+ }
+ if (strcmp(argv[i], "allow") == 0) {
+ policy = ALLOW;
+ }
+ else if (strcmp(argv[i], "interactive") == 0) {
+ policy = INTERACTIVE;
+ }
+ else {
+ policy = DENY;
+ }
+ }
+ else if (strcmp(azColName[i], "command") == 0) {
+ // null command means to match all commands (whitelist all from uid)
+ command_match = argv[i] == NULL || strcmp(argv[i], get_command(&(data->ctx->to))) == 0;
+ }
+ else if (strcmp(azColName[i], "until") == 0) {
+ if (argv[i] != NULL) {
+ until = atoi(argv[i]);
+ }
+ }
+ }
+
+ // check for command match
+ if (command_match) {
+ // also make sure this policy has not expired
+ if (until == 0 || until < time(NULL)) {
+ if (policy == DENY) {
+ data->policy = DENY;
+ return -1;
+ }
+
+ data->policy = ALLOW;
+ // even though we allow, continue, so we can see if there's another policy
+ // that denies...
+ }
}
- printf("\n");
+
return 0;
}
-int database_check(struct su_context *ctx) {
+policy_t database_check(struct su_context *ctx) {
sqlite3 *db = NULL;
char query[512];
- snprintf(query, sizeof(query), "select allow_type, until, command from access where uid=%d", ctx->from.uid);
- int ret = sqlite3_open(query, &db);
+ snprintf(query, sizeof(query), "select policy, until, command from uid_policy where uid=%d", ctx->from.uid);
+ int ret = sqlite3_open_v2(ctx->user.database_path, &db, SQLITE_OPEN_READONLY, NULL);
if (ret) {
- LOGE("sqlite3 open failure");
- return ret;
+ LOGE("sqlite3 open failure: %d", ret);
+ sqlite3_close(db);
+ return DENY;
}
-
+
+ int result;
+ char *err = NULL;
+ struct callback_data_t data;
+ data.ctx = ctx;
+ data.policy = INTERACTIVE;
+ ret = sqlite3_exec(db, query, database_callback, &data, &err);
sqlite3_close(db);
+ if (err != NULL) {
+ LOGE("sqlite3_exec: %s", err);
+ return DENY;
+ }
- return -1;
+ return data.policy;
}
void exec_log(char *priority, char* logline) {
int pid;
if ((pid = fork()) == 0) {
- execl("/system/bin/log", "/system/bin/log", "-p", priority, "-t", LOG_TAG, logline);
- exit(0);
+ int zero = open("/dev/zero", O_RDONLY | O_CLOEXEC);
+ dup2(zero, 0);
+ int null = open("/dev/null", O_WRONLY | O_CLOEXEC);
+ dup2(null, 1);
+ dup2(null, 2);
+ execl("/system/bin/log", "/system/bin/log", "-p", priority, "-t", LOG_TAG, logline);
+ _exit(0);
}
}
void exec_loge(const char* fmt, ...) {
- va_list args;
+ va_list args;
- char logline[PATH_MAX];
- va_start(args, fmt);
- vsnprintf(logline, PATH_MAX, fmt, args);
- va_end(args);
- exec_log("e", logline);
+ char logline[PATH_MAX];
+ va_start(args, fmt);
+ vsnprintf(logline, PATH_MAX, fmt, args);
+ va_end(args);
+ exec_log("e", logline);
}
void exec_logw(const char* fmt, ...) {
- va_list args;
+ va_list args;
- char logline[PATH_MAX];
- va_start(args, fmt);
- vsnprintf(logline, PATH_MAX, fmt, args);
- va_end(args);
- exec_log("w", logline);
+ char logline[PATH_MAX];
+ va_start(args, fmt);
+ vsnprintf(logline, PATH_MAX, fmt, args);
+ va_end(args);
+ exec_log("w", logline);
}
void exec_logd(const char* fmt, ...) {
- va_list args;
+ va_list args;
- char logline[PATH_MAX];
- va_start(args, fmt);
- vsnprintf(logline, PATH_MAX, fmt, args);
- va_end(args);
- exec_log("d", logline);
+ char logline[PATH_MAX];
+ va_start(args, fmt);
+ vsnprintf(logline, PATH_MAX, fmt, args);
+ va_end(args);
+ exec_log("d", logline);
}
static int from_init(struct su_initiator *from) {
FILE *fp;
if ((fp = fopen(REQUESTOR_MULTIUSER_MODE, "r"))) {
fgets(mode, sizeof(mode), fp);
+ LOGD("multiuser mode: %s", mode);
if (strcmp(mode, "user\n") == 0) {
ctx->user.multiuser_mode = MULTIUSER_MODE_USER;
} else if (strcmp(mode, "owner\n") == 0) {
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(serv_fd, &fds);
+ LOGD("select");
do {
rc = select(serv_fd + 1, &fds, NULL, NULL, &tv);
} while (rc < 0 && errno == EINTR);
static int socket_receive_result(int fd, char *result, ssize_t result_len) {
ssize_t len;
+ LOGD("waiting for result");
len = read(fd, result, result_len-1);
if (len < 0) {
PLOGE("read(result)");
struct stat st;
int c, socket_serv_fd, fd;
char buf[64], *result;
- allow_t dballow;
+ policy_t dballow;
struct option long_opts[] = {
{ "command", required_argument, NULL, 'c' },
{ "help", no_argument, NULL, 'h' },
read_options(&ctx);
user_init(&ctx);
- if (ctx.user.multiuser_mode == MULTIUSER_MODE_OWNER_ONLY && ctx.user.android_user_id != 0)
+ if (ctx.user.multiuser_mode == MULTIUSER_MODE_OWNER_ONLY && ctx.user.android_user_id != 0) {
+ LOGD("multiuser mode: owner only");
deny(&ctx);
+ }
- if (access_disabled(&ctx.from))
+ if (access_disabled(&ctx.from)) {
+ LOGD("access_disabled");
deny(&ctx);
+ }
ctx.umask = umask(027);
allow(&ctx);
if (stat(ctx.user.database_path, &st) < 0) {
- PLOGE("stat");
+ PLOGE("stat %s", ctx.user.database_path);
deny(&ctx);
}
deny(&ctx);
}
- mkdir(REQUESTOR_CACHE_PATH, 0770);
+ int ret = mkdir(REQUESTOR_CACHE_PATH, 0770);
+ LOGD("mkdir: %d", ret);
if (chown(REQUESTOR_CACHE_PATH, st.st_uid, st.st_gid)) {
PLOGE("chown (%s, %ld, %ld)", REQUESTOR_CACHE_PATH, st.st_uid, st.st_gid);
deny(&ctx);
dballow = database_check(&ctx);
switch (dballow) {
- case INTERACTIVE: break;
- case ALLOW: allow(&ctx); /* never returns */
+ case INTERACTIVE:
+ break;
+ case ALLOW:
+ LOGD("db allowed");
+ allow(&ctx); /* never returns */
case DENY:
- default: deny(&ctx); /* never returns too */
+ default:
+ LOGD("db denied");
+ deny(&ctx); /* never returns too */
}
socket_serv_fd = socket_create_temp(ctx.sock_path, sizeof(ctx.sock_path));
#define REQUESTOR_MULTIUSER_MODE REQUESTOR_FILES_PATH "/multiuser_mode"
/* intent actions */
-#define ACTION_REQUEST "start -n " REQUESTOR "/.MainActivity"
+#define ACTION_REQUEST "start -n " REQUESTOR "/.RequestActivity"
#define ACTION_NOTIFY "start -n " REQUESTOR "/.NotifyActivity"
#define ACTION_RESULT "broadcast -n " REQUESTOR "/.SuReceiver"
} multiuser_mode_t;
typedef enum {
- INTERACTIVE = -1,
- DENY = 0,
- ALLOW = 1,
-} allow_t;
+ INTERACTIVE = 0,
+ DENY = 1,
+ ALLOW = 2,
+} policy_t;
-extern allow_t database_check(struct su_context *ctx);
+extern policy_t database_check(struct su_context *ctx);
extern void set_identity(unsigned int uid);
extern int send_request(struct su_context *ctx);
-extern int send_result(struct su_context *ctx, allow_t allow);
+extern int send_result(struct su_context *ctx, policy_t policy);
extern void sigchld_handler(int sig);
static inline char *get_command(const struct su_request *to)
--- /dev/null
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ >
+
+ <ListView
+ android:id="@+id/list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_weight="1"
+ android:divider="@null"
+ android:dividerHeight="0dp"
+ android:gravity="center"
+ android:paddingBottom="20dp"
+ android:paddingTop="20dp" />
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical" >
+
+ <include
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ layout="@layout/app_info" />
+ </LinearLayout>
+
+
+</LinearLayout>
\ No newline at end of file
+++ /dev/null
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- tools:context=".MainActivity" >
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:orientation="vertical"
- android:paddingBottom="@dimen/activity_vertical_margin"
- android:paddingLeft="@dimen/activity_horizontal_margin"
- android:paddingRight="@dimen/activity_horizontal_margin"
- android:paddingTop="@dimen/activity_vertical_margin" >
-
- <TextView
- android:id="@+id/incoming"
- style="@android:style/TextAppearance.Medium"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"
- android:layout_gravity="center"
- android:gravity="center"
- android:text="@string/status_incoming" />
-
- <LinearLayout
- android:id="@+id/ready"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical"
- android:visibility="gone" >
-
- <TextView
- android:id="@+id/request"
- style="@android:style/TextAppearance.Large"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:gravity="center"
- android:textStyle="bold" />
-
- <TextView
- style="@android:style/TextAppearance.Small"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:gravity="center"
- android:paddingBottom="10dp"
- android:paddingTop="10dp"
- android:text="@string/info" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="3"
- android:gravity="right"
- android:paddingRight="10dp"
- android:text="@string/app_header"
- android:textStyle="bold" />
-
- <TextView
- android:id="@+id/app_header"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="2"
- android:singleLine="true" />
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="3"
- android:gravity="right"
- android:paddingRight="10dp"
- android:text="@string/package_header"
- android:textStyle="bold" />
-
- <TextView
- android:id="@+id/package_header"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="2"
- android:singleLine="true" />
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="3"
- android:gravity="right"
- android:paddingRight="10dp"
- android:text="@string/uid_header"
- android:textStyle="bold" />
-
- <TextView
- android:id="@+id/uid_header"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="2"
- android:singleLine="true" />
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="3"
- android:gravity="right"
- android:paddingRight="10dp"
- android:text="@string/command_header"
- android:textStyle="bold" />
-
- <TextView
- android:id="@+id/command_header"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="2"
- android:ellipsize="middle"
- android:singleLine="true" />
- </LinearLayout>
-
- <ListView
- android:id="@+id/list"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:divider="@null"
- android:dividerHeight="0dp"
- android:gravity="center"
- android:paddingBottom="20dp"
- android:paddingTop="20dp" />
-
- <TextView
- android:id="@+id/unknown"
- style="@android:style/TextAppearance.Medium"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:gravity="center" />
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:orientation="vertical" >
-
- <CheckBox
- android:id="@+id/always_allow"
- style="@android:style/TextAppearance.Medium"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingBottom="10dp"
- android:text="@string/always_allow" />
-
- <CheckBox
- android:id="@+id/allow_temporarily"
- style="@android:style/TextAppearance.Medium"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingBottom="10dp"
- android:text="@string/allow_temporarily" />
- </LinearLayout>
- </LinearLayout>
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" >
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="@android:color/darker_gray"
- android:orientation="vertical" >
- </LinearLayout>
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content" >
-
- <Button
- android:id="@+id/deny"
- style="@style/FlatButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/deny" />
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:paddingBottom="10dp"
- android:paddingTop="10dp" >
-
- <LinearLayout
- android:layout_width="1dp"
- android:layout_height="match_parent"
- android:background="@android:color/darker_gray" >
- </LinearLayout>
- </LinearLayout>
-
- <Button
- android:id="@+id/allow"
- style="@style/FlatButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:enabled="false"
- android:text="@string/allow" />
- </LinearLayout>
-
-</LinearLayout>
\ No newline at end of file
--- /dev/null
+<merge xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="3"
+ android:gravity="right"
+ android:paddingRight="10dp"
+ android:text="@string/app_header"
+ android:textStyle="bold" />
+
+ <TextView
+ android:id="@+id/app_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="2"
+ android:singleLine="true" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="3"
+ android:gravity="right"
+ android:paddingRight="10dp"
+ android:text="@string/package_header"
+ android:textStyle="bold" />
+
+ <TextView
+ android:id="@+id/package_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="2"
+ android:singleLine="true" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="3"
+ android:gravity="right"
+ android:paddingRight="10dp"
+ android:text="@string/uid_header"
+ android:textStyle="bold" />
+
+ <TextView
+ android:id="@+id/uid_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="2"
+ android:singleLine="true" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="3"
+ android:gravity="right"
+ android:paddingRight="10dp"
+ android:text="@string/command_header"
+ android:textStyle="bold" />
+
+ <TextView
+ android:id="@+id/command_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="2"
+ android:ellipsize="middle"
+ android:singleLine="true" />
+ </LinearLayout>
+
+</merge>
\ No newline at end of file
--- /dev/null
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <include
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ layout="@layout/app_info" />
+
+ <ListView
+ android:id="@+id/list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:divider="@null"
+ android:dividerHeight="0dp"
+ android:gravity="center"
+ android:paddingBottom="20dp"
+ android:paddingTop="20dp" />
+
+</merge>
\ No newline at end of file
--- /dev/null
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:id="@+id/root"
+ tools:context=".RequestActivity" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin" >
+
+ <TextView
+ android:id="@+id/incoming"
+ style="@android:style/TextAppearance.Medium"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:text="@string/status_incoming" />
+
+ <LinearLayout
+ android:id="@+id/ready"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+ android:visibility="gone" >
+
+ <TextView
+ android:id="@+id/request"
+ style="@android:style/TextAppearance.Large"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:textStyle="bold" />
+
+ <TextView
+ style="@android:style/TextAppearance.Small"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:paddingBottom="10dp"
+ android:paddingTop="10dp"
+ android:text="@string/info" />
+
+ <include
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ layout="@layout/app_layout" />
+
+ <TextView
+ android:id="@+id/unknown"
+ style="@android:style/TextAppearance.Medium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center" />
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:orientation="vertical" >
+
+ <RadioGroup
+ android:id="@+id/remember"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" >
+
+ <RadioButton
+ android:checked="true"
+ android:id="@+id/this_time_only"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/this_time_only" />
+
+ <RadioButton
+ android:id="@+id/remember_for"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/remember_for" />
+
+ <RadioButton
+ android:id="@+id/remember_forever"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/remember_forever" />
+ </RadioGroup>
+ </LinearLayout>
+ </LinearLayout>
+ </LinearLayout>
+
+<!-- <include
+ android:background="@color/background_dark"
+ style="@style/AppBaseDarkTheme"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ layout="@layout/request_buttons" /> -->
+
+</LinearLayout>
\ No newline at end of file
--- /dev/null
+<merge xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="@android:color/darker_gray"
+ android:orientation="vertical" >
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:background="@color/background_dark"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" >
+
+ <Button
+ android:id="@+id/deny"
+ style="@style/FlatButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/deny" />
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingBottom="10dp"
+ android:paddingTop="10dp" >
+
+ <LinearLayout
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:background="@android:color/darker_gray" >
+ </LinearLayout>
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/allow"
+ style="@style/FlatButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:enabled="false"
+ android:text="@string/allow" />
+ </LinearLayout>
+
+</merge>
\ No newline at end of file
+++ /dev/null
-<resources>
-
- <!--
- Base application theme for API 11+. This theme completely replaces
- AppBaseTheme from res/values/styles.xml on API 11+ devices.
- -->
- <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
- <!-- API 11 theme customizations can go here. -->
- </style>
-
- <style name="AppBaseDarkTheme" parent="@android:style/Theme.Holo">
- <!-- API 14 theme customizations can go here. -->
- </style>
-
- <style name="FlatButton">
- <item name="android:background">?android:attr/selectableItemBackground</item>
- </style>
-
-</resources>
\ No newline at end of file
AppBaseTheme from BOTH res/values/styles.xml and
res/values-v11/styles.xml on API 14+ devices.
-->
- <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+ <style name="AppBaseTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
<!-- API 14 theme customizations can go here. -->
</style>
<string name="status_incoming">Incoming Superuser Request...</string>
<string name="unknown_uid">Unknown UID: %s</string>
<string name="application_request">%s is requesting Superuser access.</string>
- <string name="info">Warning: If you did not initiate this action or do not understand this request, you should Deny the request.</string>
+ <string name="info">Warning: If you do not understand this, you should Deny the request.</string>
<string name="request">Superuser Request</string>
- <string name="allow_temporarily">Temporarily allow future requests</string>
- <string name="always_allow">Allow all future requests</string>
<string name="package_header">Package:</string>
<string name="app_header">App:</string>
<string name="uid_header">Requested UID:</string>
<string name="command_header">Command:</string>
+
+ <string name="this_time_only">This time only</string>
+ <string name="remember_for">Remember choice for %s minutes</string>
+ <string name="remember_forever">Remember choice forever</string>
</resources>
\ No newline at end of file
Base application theme, dependent on API level. This theme is replaced
by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
-->
- <style name="AppBaseTheme" parent="android:Theme.Light">
+ <style name="AppBaseTheme" parent="@android:style/Theme.Light">
<!--
Theme customizations available in newer API levels can go in
res/values-vXX/styles.xml, while customizations related to
<style name="FlatButton">
</style>
- <style name="Dialog" parent="@android:style/Theme.Dialog">
-
- </style>
+
+ <color name="background_dark">#1F1F1F</color>
</resources>
\ No newline at end of file
package com.koushikdutta.superuser;
-import java.io.DataInputStream;
-import java.io.IOException;
-
import android.app.Activity;
-import android.content.Intent;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.net.LocalSocket;
-import android.net.LocalSocketAddress;
-import android.net.LocalSocketAddress.Namespace;
-import android.os.Bundle;
-import android.os.Handler;
-import android.util.Log;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.ListView;
-import android.widget.TextView;
public class MainActivity extends Activity {
- private static final String LOGTAG = "Superuser";
- int mCallerUid;
- int mDesiredUid;
- String mDesiredCmd;
-
- ArrayAdapter<PackageInfo> mAdapter;
-
- Handler mHandler = new Handler();
-
- int mTimeLeft = 3;
-
- Boolean mAction;
- Button mAllow;
- Button mDeny;
-
- void handleAction() {
- try {
- mSocket.getOutputStream().write((mAction ? "socket:ALLOW" : "socket:DENY").getBytes());
- }
- catch (Exception ex) {
- }
- finish();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- try {
- if (mSocket != null)
- mSocket.close();
- }
- catch (Exception ex) {
- }
- }
-
- void requestReady() {
- findViewById(R.id.incoming).setVisibility(View.GONE);
- findViewById(R.id.ready).setVisibility(View.VISIBLE);
-
- ListView list = (ListView)findViewById(R.id.list);
- list.setEnabled(false);
- list.setEmptyView(findViewById(R.id.unknown));
- final PackageManager pm = getPackageManager();
- String[] pkgs = pm.getPackagesForUid(mCallerUid);
- TextView unknown = (TextView)findViewById(R.id.unknown);
- unknown.setText(getString(R.string.unknown_uid, mCallerUid));
-
- ((TextView)findViewById(R.id.uid_header)).setText(Integer.toString(mDesiredUid));
- ((TextView)findViewById(R.id.command_header)).setText(mDesiredCmd);
-
- mAdapter = new ArrayAdapter<PackageInfo>(this, R.layout.packageinfo, R.id.title) {
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- convertView = super.getView(position, convertView, parent);
-
- PackageInfo pi = getItem(position);
- ImageView icon = (ImageView)convertView.findViewById(R.id.image);
- icon.setImageDrawable(pi.applicationInfo.loadIcon(pm));
- ((TextView)convertView.findViewById(R.id.title)).setText(pi.applicationInfo.loadLabel(pm));
-
- return convertView;
- }
- };
-
- list.setAdapter(mAdapter);
- for (String pkg: pkgs) {
- try {
- PackageInfo pi = pm.getPackageInfo(pkg, 0);
- ((TextView)findViewById(R.id.request)).setText(getString(R.string.application_request, pi.applicationInfo.loadLabel(pm)));
- mAdapter.add(pi);
- ((TextView)findViewById(R.id.app_header)).setText(pi.applicationInfo.loadLabel(pm));
- ((TextView)findViewById(R.id.package_header)).setText(pi.packageName);
- }
- catch (Exception ex) {
- }
- }
-
- new Runnable() {
- public void run() {
- mAllow.setText(getString(R.string.allow) + " (" + mTimeLeft + ")");
- if (mTimeLeft-- <= 0) {
- mAllow.setText(getString(R.string.allow));
- if (mAction == null)
- mAllow.setEnabled(true);
- return;
- }
- mHandler.postDelayed(this, 1000);
- };
- }.run();
- }
-
- void manageSocket(final String socket) {
- new Thread() {
- @Override
- public void run() {
- try {
- mSocket = new LocalSocket();
- mSocket.connect(new LocalSocketAddress(socket, Namespace.FILESYSTEM));
-
- DataInputStream is = new DataInputStream(mSocket.getInputStream());
-
- int protocolVersion = is.readInt();
- Log.d(LOGTAG, "INT32:PROTO VERSION = " + protocolVersion);
-
- int exeSizeMax = is.readInt();
- Log.d(LOGTAG, "UINT32:FIELD7MAX = " + exeSizeMax);
- int cmdSizeMax = is.readInt();
- Log.d(LOGTAG, "UINT32:FIELD9MAX = " + cmdSizeMax);
- mCallerUid = is.readInt();
- Log.d(LOGTAG, "UINT32:CALLER = " + mCallerUid);
- mDesiredUid = is.readInt();
- Log.d(LOGTAG, "UINT32:TO = " + mDesiredUid);
-
- int exeSize = is.readInt();
- Log.d(LOGTAG, "UINT32:EXESIZE = " + exeSize);
- if (exeSize > exeSizeMax) {
- throw new IOException("Incomming string bigger than allowed");
- }
- byte[] buf = new byte[exeSize];
- is.read(buf);
- String callerBin = new String(buf, 0, exeSize - 1);
- Log.d(LOGTAG, "STRING:EXE = " + callerBin);
-
- int cmdSize = is.readInt();
- Log.d(LOGTAG, "UINT32:CMDSIZE = " + cmdSize);
- if (cmdSize > cmdSizeMax) {
- throw new IOException("Incomming string bigger than allowed");
- }
- buf = new byte[cmdSize];
- is.read(buf);
- mDesiredCmd = new String(buf, 0, cmdSize - 1);
- Log.d(LOGTAG, "STRING:CMD = " + mDesiredCmd);
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- requestReady();
- }
- });
- }
- catch (Exception ex) {
- Log.i(LOGTAG, ex.getMessage(), ex);
- try {
- mSocket.close();
- }
- catch (Exception e) {
- }
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- finish();
- }
- });
- }
- }
- }.start();
- }
-
- LocalSocket mSocket;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
-
- Intent intent = getIntent();
- if (intent == null) {
- finish();
- return;
- }
-
- String socket = intent.getStringExtra("socket");
- if (socket == null) {
- finish();
- return;
- }
-
- if (getClass() == MainActivity.class) {
- // TODO: is this the right way to do this?
- // maybe queue requests to maintain the order?
- // fragile apps may have race conditions that occur
- // if the su requests are handled LIFO.
-
- // MainActivity is actually just a passthrough to a new task
- // stack.
- // Pretty much every superuser implementation i've seen craps out if there
- // is more than 1 su request at a time. Each subsequent su request
- // will get "lost" because there can only be one instance of the activity
- // at a time. Really annoying. This fixes that.
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.setClass(this, MultitaskSuRequestActivity.class);
- startActivity(intent);
- finish();
- return;
- }
- mAllow = (Button)findViewById(R.id.allow);
- mDeny = (Button)findViewById(R.id.deny);
-
- mAllow.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mAction = true;
- mAllow.setEnabled(false);
- mDeny.setEnabled(false);
- handleAction();
- }
- });
- mDeny.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mAction = false;
- mAllow.setEnabled(false);
- mDeny.setEnabled(false);
- handleAction();
- }
- });
- manageSocket(socket);
- }
}
import android.os.Bundle;
-public class MultitaskSuRequestActivity extends MainActivity {
+public class MultitaskSuRequestActivity extends RequestActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
--- /dev/null
+package com.koushikdutta.superuser;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+import junit.framework.Assert;
+import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
+import android.net.LocalSocketAddress.Namespace;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+public class RequestActivity extends Activity {
+ private static final String LOGTAG = "Superuser";
+ int mCallerUid;
+ int mDesiredUid;
+ String mDesiredCmd;
+
+ Spinner mSpinner;
+ ArrayAdapter<PackageInfo> mAdapter;
+
+ Handler mHandler = new Handler();
+
+ int mTimeLeft = 3;
+
+ Button mAllow;
+ Button mDeny;
+
+ boolean mHandled;
+ void handleAction(boolean action) {
+ Assert.assertTrue(!mHandled);
+ mHandled = true;
+ try {
+ mSocket.getOutputStream().write((action ? "socket:ALLOW" : "socket:DENY").getBytes());
+ }
+ catch (Exception ex) {
+ }
+ try {
+ SuDatabaseHelper.setPolicy(this, mCallerUid, mDesiredCmd, action ? SuDatabaseHelper.POLICY_ALLOW : SuDatabaseHelper.POLICY_DENY, 0);
+ }
+ catch (Exception ex) {
+ }
+ finish();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ try {
+ if (mSocket != null)
+ mSocket.close();
+ }
+ catch (Exception ex) {
+ }
+ }
+
+ boolean mRequestReady;
+ void requestReady() {
+ findViewById(R.id.incoming).setVisibility(View.GONE);
+ findViewById(R.id.ready).setVisibility(View.VISIBLE);
+
+ ListView list = (ListView)findViewById(R.id.list);
+ list.setEnabled(false);
+ list.setEmptyView(findViewById(R.id.unknown));
+ final PackageManager pm = getPackageManager();
+ String[] pkgs = pm.getPackagesForUid(mCallerUid);
+ TextView unknown = (TextView)findViewById(R.id.unknown);
+ unknown.setText(getString(R.string.unknown_uid, mCallerUid));
+
+ ((TextView)findViewById(R.id.uid_header)).setText(Integer.toString(mDesiredUid));
+ ((TextView)findViewById(R.id.command_header)).setText(mDesiredCmd);
+
+ mAdapter = new ArrayAdapter<PackageInfo>(this, R.layout.packageinfo, R.id.title) {
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ convertView = super.getView(position, convertView, parent);
+
+ PackageInfo pi = getItem(position);
+ ImageView icon = (ImageView)convertView.findViewById(R.id.image);
+ icon.setImageDrawable(pi.applicationInfo.loadIcon(pm));
+ ((TextView)convertView.findViewById(R.id.title)).setText(pi.applicationInfo.loadLabel(pm));
+
+ return convertView;
+ }
+ };
+
+ list.setAdapter(mAdapter);
+ if (pkgs != null) {
+ for (String pkg: pkgs) {
+ try {
+ PackageInfo pi = pm.getPackageInfo(pkg, 0);
+ ((TextView)findViewById(R.id.request)).setText(getString(R.string.application_request, pi.applicationInfo.loadLabel(pm)));
+ mAdapter.add(pi);
+ ((TextView)findViewById(R.id.app_header)).setText(pi.applicationInfo.loadLabel(pm));
+ ((TextView)findViewById(R.id.package_header)).setText(pi.packageName);
+
+ // could display them all, but screw it...
+ // maybe a better ux for this later
+ break;
+ }
+ catch (Exception ex) {
+ }
+ }
+ }
+
+ new Runnable() {
+ public void run() {
+ mAllow.setText(getString(R.string.allow) + " (" + mTimeLeft + ")");
+ if (mTimeLeft-- <= 0) {
+ mAllow.setText(getString(R.string.allow));
+ if (!mHandled)
+ mAllow.setEnabled(true);
+ return;
+ }
+ mHandler.postDelayed(this, 1000);
+ };
+ }.run();
+ }
+
+ void manageSocket(final String socket) {
+ new Thread() {
+ @Override
+ public void run() {
+ try {
+ mSocket = new LocalSocket();
+ mSocket.connect(new LocalSocketAddress(socket, Namespace.FILESYSTEM));
+ Log.i(LOGTAG, "connected to socket.");
+
+ DataInputStream is = new DataInputStream(mSocket.getInputStream());
+
+ int protocolVersion = is.readInt();
+ Log.d(LOGTAG, "INT32:PROTO VERSION = " + protocolVersion);
+
+ int exeSizeMax = is.readInt();
+ Log.d(LOGTAG, "UINT32:FIELD7MAX = " + exeSizeMax);
+ int cmdSizeMax = is.readInt();
+ Log.d(LOGTAG, "UINT32:FIELD9MAX = " + cmdSizeMax);
+ mCallerUid = is.readInt();
+ Log.d(LOGTAG, "UINT32:CALLER = " + mCallerUid);
+ mDesiredUid = is.readInt();
+ Log.d(LOGTAG, "UINT32:TO = " + mDesiredUid);
+
+ int exeSize = is.readInt();
+ Log.d(LOGTAG, "UINT32:EXESIZE = " + exeSize);
+ if (exeSize > exeSizeMax) {
+ throw new IOException("Incomming string bigger than allowed");
+ }
+ byte[] buf = new byte[exeSize];
+ is.read(buf);
+ String callerBin = new String(buf, 0, exeSize - 1);
+ Log.d(LOGTAG, "STRING:EXE = " + callerBin);
+
+ int cmdSize = is.readInt();
+ Log.d(LOGTAG, "UINT32:CMDSIZE = " + cmdSize);
+ if (cmdSize > cmdSizeMax) {
+ throw new IOException("Incomming string bigger than allowed");
+ }
+ buf = new byte[cmdSize];
+ is.read(buf);
+ mDesiredCmd = new String(buf, 0, cmdSize - 1);
+ Log.d(LOGTAG, "STRING:CMD = " + mDesiredCmd);
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mRequestReady = true;
+ requestReady();
+ }
+ });
+
+ // now, even though the request is ready, keep reading.
+ // if the su process dies for whatever, the socket will close.
+ // in that case, an exception will throw and this activity will finish.
+ is.read();
+ }
+ catch (Exception ex) {
+ Log.i(LOGTAG, ex.getMessage(), ex);
+ try {
+ mSocket.close();
+ }
+ catch (Exception e) {
+ }
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ finish();
+ }
+ });
+ }
+ }
+ }.start();
+ }
+
+
+ RadioGroup mRemember;
+
+ LocalSocket mSocket;
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Intent intent = getIntent();
+ if (intent == null) {
+ finish();
+ return;
+ }
+
+ String socket = intent.getStringExtra("socket");
+ if (socket == null) {
+ finish();
+ return;
+ }
+
+ if (getClass() == RequestActivity.class) {
+ // TODO: is this the right way to do this?
+ // maybe queue requests to maintain the order?
+ // fragile apps may have race conditions that occur
+ // if the su requests are handled LIFO.
+
+ // MainActivity is actually just a passthrough to a new task
+ // stack.
+ // Pretty much every superuser implementation i've seen craps out if there
+ // is more than 1 su request at a time. Each subsequent su request
+ // will get "lost" because there can only be one instance of the activity
+ // at a time. Really annoying. This fixes that.
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setClass(this, MultitaskSuRequestActivity.class);
+ startActivity(intent);
+ finish();
+ return;
+ }
+
+ setContentView();
+
+ manageSocket(socket);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ setContentView();
+ }
+
+ void setContentView() {
+ setContentView(R.layout.request);
+
+ ContextThemeWrapper wrapper = new ContextThemeWrapper(this, R.style.AppDarkTheme);
+ LayoutInflater darkInflater = (LayoutInflater)wrapper.getSystemService(LAYOUT_INFLATER_SERVICE);
+ ViewGroup root = (ViewGroup)findViewById(R.id.root);
+ darkInflater.inflate(R.layout.request_buttons, root);
+
+ mRemember = (RadioGroup)findViewById(R.id.remember);
+ RadioButton rememberFor = (RadioButton)findViewById(R.id.remember_for);
+ rememberFor.setText(getString(R.string.remember_for, 10));
+
+ mAllow = (Button)findViewById(R.id.allow);
+ mDeny = (Button)findViewById(R.id.deny);
+
+ mAllow.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mAllow.setEnabled(false);
+ mDeny.setEnabled(false);
+ handleAction(true);
+ }
+ });
+ mDeny.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mAllow.setEnabled(false);
+ mDeny.setEnabled(false);
+ handleAction(false);
+ }
+ });
+
+ if (mRequestReady)
+ requestReady();
+ }
+}
--- /dev/null
+package com.koushikdutta.superuser;
+
+import android.app.Application;
+
+public class SuApplication extends Application {
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ new SuDatabaseHelper(this).getWritableDatabase().close();
+ }
+}
--- /dev/null
+package com.koushikdutta.superuser;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+public class SuDatabaseHelper extends SQLiteOpenHelper {
+ public SuDatabaseHelper(Context context) {
+ super(context, "su.sqlite", null, 1);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ onUpgrade(db, 0, 1);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ if (oldVersion == 0) {
+ db.execSQL("create table if not exists uid_policy (policy text, until integer, command text, uid integer, primary key(uid, command))");
+ oldVersion = 1;
+ }
+ }
+
+ public static final String POLICY_ALLOW = "allow";
+ public static final String POLICY_DENY = "deny";
+ public static final String POLICY_INTERACTOVE = "interactive";
+
+
+ public static void setPolicy(Context context, int uid, String command, String policy, int until) {
+ SQLiteDatabase db = new SuDatabaseHelper(context).getWritableDatabase();
+
+ ContentValues values = new ContentValues();
+ values.put("uid", uid);
+ values.put("command", command);
+ values.put("policy", policy);
+ values.put("until", until);
+ db.replace("uid_policy", null, values);
+ }
+}
@Override
public void onReceive(Context context, Intent intent) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.setClass(context, MainActivity.class);
+ intent.setClass(context, RequestActivity.class);
context.startActivity(intent);
}
}