OSDN Git Service

intial commit
authorKoushik Dutta <koushd@gmail.com>
Sun, 17 Feb 2013 03:20:58 +0000 (19:20 -0800)
committerKoushik Dutta <koushd@gmail.com>
Sun, 17 Feb 2013 03:20:58 +0000 (19:20 -0800)
29 files changed:
.gitignore [new file with mode: 0644]
Superuser/.classpath [new file with mode: 0644]
Superuser/.project [new file with mode: 0644]
Superuser/AndroidManifest.xml [new file with mode: 0644]
Superuser/gen/com/koushikdutta/superuser/BuildConfig.java [new file with mode: 0644]
Superuser/gen/com/koushikdutta/superuser/R.java [new file with mode: 0644]
Superuser/jni/Android.mk [new file with mode: 0644]
Superuser/jni/su/activity.c [new file with mode: 0644]
Superuser/jni/su/db.c [new file with mode: 0644]
Superuser/jni/su/su.c [new file with mode: 0644]
Superuser/jni/su/su.h [new file with mode: 0644]
Superuser/jni/su/utils.c [new file with mode: 0644]
Superuser/jni/su/utils.h [new file with mode: 0644]
Superuser/proguard-project.txt [new file with mode: 0644]
Superuser/project.properties [new file with mode: 0644]
Superuser/res/drawable-hdpi/ic_launcher.png [new file with mode: 0644]
Superuser/res/drawable-mdpi/ic_launcher.png [new file with mode: 0644]
Superuser/res/drawable-xhdpi/ic_launcher.png [new file with mode: 0644]
Superuser/res/layout/activity_main.xml [new file with mode: 0644]
Superuser/res/menu/main.xml [new file with mode: 0644]
Superuser/res/values-sw600dp/dimens.xml [new file with mode: 0644]
Superuser/res/values-sw720dp-land/dimens.xml [new file with mode: 0644]
Superuser/res/values-v11/styles.xml [new file with mode: 0644]
Superuser/res/values-v14/styles.xml [new file with mode: 0644]
Superuser/res/values/dimens.xml [new file with mode: 0644]
Superuser/res/values/strings.xml [new file with mode: 0644]
Superuser/res/values/styles.xml [new file with mode: 0644]
Superuser/src/com/koushikdutta/superuser/MainActivity.java [new file with mode: 0644]
Superuser/src/com/koushikdutta/superuser/SuReceiver.java [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..8008f25
--- /dev/null
@@ -0,0 +1,3 @@
+bin
+libs
+obj
diff --git a/Superuser/.classpath b/Superuser/.classpath
new file mode 100644 (file)
index 0000000..a4763d1
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="src" path="gen"/>
+       <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+       <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+       <classpathentry kind="output" path="bin/classes"/>
+</classpath>
diff --git a/Superuser/.project b/Superuser/.project
new file mode 100644 (file)
index 0000000..5a828df
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>Superuser</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>com.android.ide.eclipse.adt.ApkBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
diff --git a/Superuser/AndroidManifest.xml b/Superuser/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..4101c52
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.koushikdutta.superuser"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-sdk
+        android:minSdkVersion="8"
+        android:targetSdkVersion="17" />
+
+    <application
+        android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/AppTheme" >
+        <activity
+            android:name="com.koushikdutta.superuser.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>
+        
+        <receiver android:name=".SuReceiver">
+            <intent-filter>
+                <action android:name="com.koushikdutta.superuser.REQUEST" />
+            </intent-filter>
+        </receiver>
+    </application>
+
+</manifest>
\ No newline at end of file
diff --git a/Superuser/gen/com/koushikdutta/superuser/BuildConfig.java b/Superuser/gen/com/koushikdutta/superuser/BuildConfig.java
new file mode 100644 (file)
index 0000000..46b1743
--- /dev/null
@@ -0,0 +1,6 @@
+/** Automatically generated file. DO NOT MODIFY */
+package com.koushikdutta.superuser;
+
+public final class BuildConfig {
+    public final static boolean DEBUG = true;
+}
\ No newline at end of file
diff --git a/Superuser/gen/com/koushikdutta/superuser/R.java b/Superuser/gen/com/koushikdutta/superuser/R.java
new file mode 100644 (file)
index 0000000..9fccbfc
--- /dev/null
@@ -0,0 +1,68 @@
+/* AUTO-GENERATED FILE.  DO NOT MODIFY.
+ *
+ * This class was automatically generated by the
+ * aapt tool from the resource data it found.  It
+ * should not be modified by hand.
+ */
+
+package com.koushikdutta.superuser;
+
+public final class R {
+    public static final class attr {
+    }
+    public static final class dimen {
+        /**  Default screen margins, per the Android Design guidelines. 
+
+         Customize dimensions originally defined in res/values/dimens.xml (such as
+         screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here.
+    
+         */
+        public static final int activity_horizontal_margin=0x7f040000;
+        public static final int activity_vertical_margin=0x7f040001;
+    }
+    public static final class drawable {
+        public static final int ic_launcher=0x7f020000;
+    }
+    public static final class id {
+        public static final int action_settings=0x7f080000;
+    }
+    public static final class layout {
+        public static final int activity_main=0x7f030000;
+    }
+    public static final class menu {
+        public static final int main=0x7f070000;
+    }
+    public static final class string {
+        public static final int action_settings=0x7f050001;
+        public static final int app_name=0x7f050000;
+        public static final int hello_world=0x7f050002;
+    }
+    public static final class style {
+        /** 
+        Base application theme, dependent on API level. This theme is replaced
+        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+    
+
+            Theme customizations available in newer API levels can go in
+            res/values-vXX/styles.xml, while customizations related to
+            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.
+    
+ API 14 theme customizations can go here. 
+         */
+        public static final int AppBaseTheme=0x7f060000;
+        /**  Application theme. 
+ All customizations that are NOT specific to a particular API-level can go here. 
+         */
+        public static final int AppTheme=0x7f060001;
+    }
+}
diff --git a/Superuser/jni/Android.mk b/Superuser/jni/Android.mk
new file mode 100644 (file)
index 0000000..52f56cd
--- /dev/null
@@ -0,0 +1,10 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_ARM_MODE := arm
+LOCAL_MODULE := su
+LOCAL_LDFLAGS := -static
+LOCAL_STATIC_LIBRARIES := log
+LOCAL_SRC_FILES := su/su.c su/activity.c su/db.c su/utils.c
+include $(BUILD_EXECUTABLE)
diff --git a/Superuser/jni/su/activity.c b/Superuser/jni/su/activity.c
new file mode 100644 (file)
index 0000000..c648333
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+** Copyright 2010, Adam Shanks (@ChainsDD)
+** Copyright 2008, Zinx Verituse (@zinxv)
+**
+** 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.
+*/
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "su.h"
+
+static void kill_child(pid_t pid)
+{
+    LOGD("killing child %d", pid);
+    if (pid) {
+        sigset_t set, old;
+
+        sigemptyset(&set);
+        sigaddset(&set, SIGCHLD);
+        if (sigprocmask(SIG_BLOCK, &set, &old)) {
+            PLOGE("sigprocmask(SIG_BLOCK)");
+            return;
+        }
+        if (kill(pid, SIGKILL))
+            PLOGE("kill (%d)", pid);
+        else if (sigsuspend(&old) && errno != EINTR)
+            PLOGE("sigsuspend");
+        if (sigprocmask(SIG_SETMASK, &old, NULL))
+            PLOGE("sigprocmask(SIG_BLOCK)");
+    }
+}
+
+int send_intent(struct su_context *ctx, allow_t allow, const char *action)
+{
+    const char *socket_path;
+    unsigned int uid = ctx->from.uid;
+    __sighandler_t handler;
+    pid_t pid;
+
+    if (allow == INTERACTIVE) {
+        socket_path = ctx->sock_path;
+        handler = sigchld_handler;
+    } else {
+        socket_path = "";
+        handler = SIG_IGN;
+    }
+
+    pid = fork();
+    /* Child */
+    if (!pid) {
+        char command[ARG_MAX];
+
+        snprintf(command, sizeof(command),
+            "exec /system/bin/am broadcast --user %d -a %s --es socket '%s' "
+            "--ei caller_uid %d --ei allow %d --es desired_cmd '%s' "
+            "--ei all %d --ei version_code %d > /dev/null 2> /dev/null",
+            ctx->user.userid, action, socket_path, uid, allow, get_command(&ctx->to),
+            ctx->to.all, VERSION_CODE);
+
+        char *args[] = { "sh", "-c", command, NULL, };
+
+        /*
+         * before sending the intent, make sure the effective uid/gid match
+         * the real uid/gid, otherwise LD_LIBRARY_PATH is wiped
+         * in Android 4.0+.
+         */
+        set_identity(uid);
+        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);
+        LOGD("Executing %s\n", command);
+        execv(_PATH_BSHELL, args);
+        PLOGE("exec am");
+        _exit(EXIT_FAILURE);
+    }
+    
+    /* Parent */
+    if (pid < 0) {
+        PLOGE("fork");
+        return -1;
+    }
+    if (allow != INTERACTIVE) {
+        waitpid(pid, NULL, 0);
+        signal(SIGCHLD, SIG_DFL);
+    }
+    return 0;
+}
diff --git a/Superuser/jni/su/db.c b/Superuser/jni/su/db.c
new file mode 100644 (file)
index 0000000..fad5371
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+** Copyright 2010, 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.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+
+#include "su.h"
+
+int database_check(struct su_context *ctx)
+{
+    FILE *fp;
+    char filename[PATH_MAX];
+    char allow[7];
+    int last = 0;
+    int from_uid = ctx->from.uid;
+    
+    if (ctx->user.owner_mode) {
+        from_uid = from_uid % 100000;
+    }
+
+    snprintf(filename, sizeof(filename),
+                "%s/%u-%u", ctx->user.store_path, from_uid, ctx->to.uid);
+    if ((fp = fopen(filename, "r"))) {
+        LOGD("Found file %s", filename);
+        
+        while (fgets(allow, sizeof(allow), fp)) {
+            last = strlen(allow) - 1;
+            if (last >= 0)
+                   allow[last] = 0;
+               
+            char cmd[ARG_MAX];
+            fgets(cmd, sizeof(cmd), fp);
+            /* skip trailing '\n' */
+            last = strlen(cmd) - 1;
+            if (last >= 0)
+                cmd[last] = 0;
+
+            LOGD("Comparing '%s' to '%s'", cmd, get_command(&ctx->to));
+            if (strcmp(cmd, get_command(&ctx->to)) == 0)
+                break;
+            else if (strcmp(cmd, "any") == 0) {
+                ctx->to.all = 1;
+                break;
+            }
+            else
+                strcpy(allow, "prompt");
+        }
+        fclose(fp);
+    } else if ((fp = fopen(ctx->user.store_default, "r"))) {
+        LOGD("Using default file %s", ctx->user.store_default);
+        fgets(allow, sizeof(allow), fp);
+        last = strlen(allow) - 1;
+        if (last >=0)
+            allow[last] = 0;
+        
+        fclose(fp);
+    }
+
+    if (strcmp(allow, "allow") == 0) {
+        return ALLOW;
+    } else if (strcmp(allow, "deny") == 0) {
+        return DENY;
+    } else {
+        if (ctx->user.userid != 0 && ctx->user.owner_mode) {
+            return DENY;
+        } else {
+            return INTERACTIVE;
+        }
+    }
+}
diff --git a/Superuser/jni/su/su.c b/Superuser/jni/su/su.c
new file mode 100644 (file)
index 0000000..8a6e14f
--- /dev/null
@@ -0,0 +1,699 @@
+/*
+** Copyright 2010, Adam Shanks (@ChainsDD)
+** Copyright 2008, Zinx Verituse (@zinxv)
+**
+** 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.
+*/
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <pwd.h>
+#include <sys/stat.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <pwd.h>
+
+#include "su.h"
+#include "utils.h"
+
+int get_shell_uid() {
+  struct passwd* ppwd = getpwnam("shell");
+  if (NULL == ppwd) {
+    return -1;
+  }
+  
+  return ppwd->pw_uid;
+}
+
+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);
+  }
+}
+
+void exec_loge(const char* fmt, ...) {
+  va_list args;
+
+  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;
+
+  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;
+
+  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)
+{
+    char path[PATH_MAX], exe[PATH_MAX];
+    char args[4096], *argv0, *argv_rest;
+    int fd;
+    ssize_t len;
+    int i;
+    int err;
+
+    from->uid = getuid();
+    from->pid = getppid();
+
+    /* Get the command line */
+    snprintf(path, sizeof(path), "/proc/%u/cmdline", from->pid);
+    fd = open(path, O_RDONLY);
+    if (fd < 0) {
+        PLOGE("Opening command line");
+        return -1;
+    }
+    len = read(fd, args, sizeof(args));
+    err = errno;
+    close(fd);
+    if (len < 0 || len == sizeof(args)) {
+        PLOGEV("Reading command line", err);
+        return -1;
+    }
+
+    argv0 = args;
+    argv_rest = NULL;
+    for (i = 0; i < len; i++) {
+        if (args[i] == '\0') {
+            if (!argv_rest) {
+                argv_rest = &args[i+1];
+            } else {
+                args[i] = ' ';
+            }
+        }
+    }
+    args[len] = '\0';
+
+    if (argv_rest) {
+        strncpy(from->args, argv_rest, sizeof(from->args));
+        from->args[sizeof(from->args)-1] = '\0';
+    } else {
+        from->args[0] = '\0';
+    }
+
+    /* If this isn't app_process, use the real path instead of argv[0] */
+    snprintf(path, sizeof(path), "/proc/%u/exe", from->pid);
+    len = readlink(path, exe, sizeof(exe));
+    if (len < 0) {
+        PLOGE("Getting exe path");
+        return -1;
+    }
+    exe[len] = '\0';
+    if (strcmp(exe, "/system/bin/app_process")) {
+        argv0 = exe;
+    }
+
+    strncpy(from->bin, argv0, sizeof(from->bin));
+    from->bin[sizeof(from->bin)-1] = '\0';
+
+    return 0;
+}
+
+static void read_options(struct su_context *ctx)
+{
+    char mode[12];
+    FILE *fp;
+    if ((fp = fopen(REQUESTOR_OPTIONS, "r"))) {
+        fgets(mode, sizeof(mode), fp);
+        if (strcmp(mode, "user\n") == 0) {
+            ctx->user.owner_mode = 0;
+        } else if (strcmp(mode, "owner\n") == 0) {
+            ctx->user.owner_mode = 1;
+        }
+    }
+}
+
+static void user_init(struct su_context *ctx)
+{
+    if (ctx->from.uid > 99999) {
+        ctx->user.userid = ctx->from.uid / 100000;
+        if (!ctx->user.owner_mode) {
+            snprintf(ctx->user.data_path, PATH_MAX, "/data/user/%d/%s",
+                    ctx->user.userid, REQUESTOR);
+            snprintf(ctx->user.store_path, PATH_MAX, "/data/user/%d/%s/files/stored",
+                    ctx->user.userid, REQUESTOR);
+            snprintf(ctx->user.store_default, PATH_MAX, "/data/user/%d/%s/files/stored/default",
+                    ctx->user.userid, REQUESTOR);
+        }
+    }
+}
+
+static void populate_environment(const struct su_context *ctx)
+{
+    struct passwd *pw;
+
+    if (ctx->to.keepenv)
+        return;
+
+    pw = getpwuid(ctx->to.uid);
+    if (pw) {
+        setenv("HOME", pw->pw_dir, 1);
+        setenv("SHELL", ctx->to.shell, 1);
+        if (ctx->to.login || ctx->to.uid) {
+            setenv("USER", pw->pw_name, 1);
+            setenv("LOGNAME", pw->pw_name, 1);
+        }
+    }
+}
+
+void set_identity(unsigned int uid)
+{
+    /*
+     * Set effective uid back to root, otherwise setres[ug]id will fail
+     * if uid isn't root.
+     */
+    if (seteuid(0)) {
+        PLOGE("seteuid (root)");
+        exit(EXIT_FAILURE);
+    }
+    if (setresgid(uid, uid, uid)) {
+        PLOGE("setresgid (%u)", uid);
+        exit(EXIT_FAILURE);
+    }
+    if (setresuid(uid, uid, uid)) {
+        PLOGE("setresuid (%u)", uid);
+        exit(EXIT_FAILURE);
+    }
+}
+
+/*
+ * For use in signal handlers/atexit-function
+ * NOTE: su_ctx points to main's local variable.
+ *       It's OK due to the program uses exit(3), not return from main()
+ */
+static struct su_context *su_ctx = NULL;
+
+static int socket_create_temp(char *path, size_t len)
+{
+    int fd;
+    struct sockaddr_un sun;
+
+    fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+    if (fd < 0) {
+        PLOGE("socket");
+        return -1;
+    }
+    if (fcntl(fd, F_SETFD, FD_CLOEXEC)) {
+        PLOGE("fcntl FD_CLOEXEC");
+        goto err;
+    }
+
+    memset(&sun, 0, sizeof(sun));
+    sun.sun_family = AF_UNIX;
+    snprintf(path, len, "%s/.socket%d", REQUESTOR_CACHE_PATH, getpid());
+    memset(sun.sun_path, 0, sizeof(sun.sun_path));
+    snprintf(sun.sun_path + 1, sizeof(sun.sun_path) - 1, "%s", path);
+
+    /*
+     * Delete the socket to protect from situations when
+     * something bad occured previously and the kernel reused pid from that process.
+     * Small probability, isn't it.
+     */
+    // unlink(sun.sun_path);
+
+    if (bind(fd, (struct sockaddr*)&sun, sizeof(sun)) < 0) {
+        PLOGE("bind");
+        goto err;
+    }
+
+    if (listen(fd, 1) < 0) {
+        PLOGE("listen");
+        goto err;
+    }
+
+    return fd;
+err:
+    close(fd);
+    return -1;
+}
+
+static int socket_accept(int serv_fd)
+{
+    struct timeval tv;
+    fd_set fds;
+    int fd, rc;
+
+    /* Wait 20 seconds for a connection, then give up. */
+    tv.tv_sec = 20;
+    tv.tv_usec = 0;
+    FD_ZERO(&fds);
+    FD_SET(serv_fd, &fds);
+    do {
+        rc = select(serv_fd + 1, &fds, NULL, NULL, &tv);
+    } while (rc < 0 && errno == EINTR);
+    if (rc < 1) {
+        PLOGE("select");
+        return -1;
+    }
+
+    fd = accept(serv_fd, NULL, NULL);
+    if (fd < 0) {
+        PLOGE("accept");
+        return -1;
+    }
+
+    return fd;
+}
+
+static int socket_send_request(int fd, const struct su_context *ctx)
+{
+    size_t len;
+    size_t bin_size, cmd_size;
+    char *cmd;
+
+#define write_token(fd, data)                \
+do {                            \
+    uint32_t __data = htonl(data);            \
+    size_t __count = sizeof(__data);        \
+    size_t __len = write((fd), &__data, __count);    \
+    if (__len != __count) {                \
+        PLOGE("write(" #data ")");        \
+        return -1;                \
+    }                        \
+} while (0)
+
+    write_token(fd, PROTO_VERSION);
+    write_token(fd, PATH_MAX);
+    write_token(fd, ARG_MAX);
+    write_token(fd, ctx->from.uid);
+    write_token(fd, ctx->to.uid);
+    bin_size = strlen(ctx->from.bin) + 1;
+    write_token(fd, bin_size);
+    len = write(fd, ctx->from.bin, bin_size);
+    if (len != bin_size) {
+        PLOGE("write(bin)");
+        return -1;
+    }
+    cmd = get_command(&ctx->to);
+    cmd_size = strlen(cmd) + 1;
+    write_token(fd, cmd_size);
+    len = write(fd, cmd, cmd_size);
+    if (len != cmd_size) {
+        PLOGE("write(cmd)");
+        return -1;
+    }
+    return 0;
+}
+
+static int socket_receive_result(int fd, char *result, ssize_t result_len)
+{
+    ssize_t len;
+    
+    len = read(fd, result, result_len-1);
+    if (len < 0) {
+        PLOGE("read(result)");
+        return -1;
+    }
+    result[len] = '\0';
+
+    return 0;
+}
+
+static void usage(int status)
+{
+    FILE *stream = (status == EXIT_SUCCESS) ? stdout : stderr;
+
+    fprintf(stream,
+    "Usage: su [options] [--] [-] [LOGIN] [--] [args...]\n\n"
+    "Options:\n"
+    "  -c, --command COMMAND         pass COMMAND to the invoked shell\n"
+    "  -h, --help                    display this help message and exit\n"
+    "  -, -l, --login                pretend the shell to be a login shell\n"
+    "  -m, -p,\n"
+    "  --preserve-environment        do not change environment variables\n"
+    "  -s, --shell SHELL             use SHELL instead of the default " DEFAULT_SHELL "\n"
+    "  -v, --version                 display version number and exit\n"
+    "  -V                            display version code and exit,\n"
+    "                                this is used almost exclusively by Superuser.apk\n");
+    exit(status);
+}
+
+static __attribute__ ((noreturn)) void deny(struct su_context *ctx)
+{
+    char *cmd = get_command(&ctx->to);
+
+    // No send to UI denied requests for shell and root users (they are in the log)
+    if( ctx->from.uid != AID_SHELL && ctx->from.uid != AID_ROOT ) {
+        send_intent(ctx, DENY, ACTION_RESULT);
+    }
+    LOGW("request rejected (%u->%u %s)", ctx->from.uid, ctx->to.uid, cmd);
+    fprintf(stderr, "%s\n", strerror(EACCES));
+    exit(EXIT_FAILURE);
+}
+
+static __attribute__ ((noreturn)) void allow(struct su_context *ctx)
+{
+    char *arg0;
+    int argc, err;
+
+    umask(ctx->umask);
+    // No send to UI accepted requests for shell and root users (they are in the log)
+    if( ctx->from.uid != AID_SHELL && ctx->from.uid != AID_ROOT ) {
+        send_intent(ctx, ALLOW, ACTION_RESULT);
+    }
+
+    arg0 = strrchr (ctx->to.shell, '/');
+    arg0 = (arg0) ? arg0 + 1 : ctx->to.shell;
+    if (ctx->to.login) {
+        int s = strlen(arg0) + 2;
+        char *p = malloc(s);
+
+        if (!p)
+            exit(EXIT_FAILURE);
+
+        *p = '-';
+        strcpy(p + 1, arg0);
+        arg0 = p;
+    }
+
+    populate_environment(ctx);
+    set_identity(ctx->to.uid);
+
+#define PARG(arg)                                    \
+    (ctx->to.optind + (arg) < ctx->to.argc) ? " " : "",                    \
+    (ctx->to.optind + (arg) < ctx->to.argc) ? ctx->to.argv[ctx->to.optind + (arg)] : ""
+
+    LOGD("%u %s executing %u %s using shell %s : %s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+            ctx->from.uid, ctx->from.bin,
+            ctx->to.uid, get_command(&ctx->to), ctx->to.shell,
+            arg0, PARG(0), PARG(1), PARG(2), PARG(3), PARG(4), PARG(5),
+            (ctx->to.optind + 6 < ctx->to.argc) ? " ..." : "");
+
+    argc = ctx->to.optind;
+    if (ctx->to.command) {
+        ctx->to.argv[--argc] = ctx->to.command;
+        ctx->to.argv[--argc] = "-c";
+    }
+    ctx->to.argv[--argc] = arg0;
+    execv(ctx->to.shell, ctx->to.argv + argc);
+    err = errno;
+    PLOGE("exec");
+    fprintf(stderr, "Cannot execute %s: %s\n", ctx->to.shell, strerror(err));
+    exit(EXIT_FAILURE);
+}
+
+/*
+ * CyanogenMod-specific behavior
+ *
+ * we can't simply use the property service, since we aren't launched from init
+ * and can't trust the location of the property workspace.
+ * Find the properties ourselves.
+ */
+int access_disabled(const struct su_initiator *from)
+{
+    char *data;
+    char build_type[PROPERTY_VALUE_MAX];
+    char debuggable[PROPERTY_VALUE_MAX], enabled[PROPERTY_VALUE_MAX];
+    size_t len;
+
+    data = read_file("/system/build.prop");
+    if (check_property(data, "ro.cm.version")) {
+        get_property(data, build_type, "ro.build.type", "");
+        free(data);
+
+        data = read_file("/default.prop");
+        get_property(data, debuggable, "ro.debuggable", "0");
+        free(data);
+        /* only allow su on debuggable builds */
+        if (strcmp("1", debuggable) != 0) {
+            LOGE("Root access is disabled on non-debug builds");
+            return 1;
+        }
+
+        data = read_file("/data/property/persist.sys.root_access");
+        if (data != NULL) {
+            len = strlen(data);
+            if (len >= PROPERTY_VALUE_MAX)
+                memcpy(enabled, "1", 2);
+            else
+                memcpy(enabled, data, len + 1);
+            free(data);
+        } else
+            memcpy(enabled, "1", 2);
+
+        /* enforce persist.sys.root_access on non-eng builds for apps */
+        if (strcmp("eng", build_type) != 0 &&
+                from->uid != AID_SHELL && from->uid != AID_ROOT &&
+                (atoi(enabled) & CM_ROOT_ACCESS_APPS_ONLY) != CM_ROOT_ACCESS_APPS_ONLY ) {
+            LOGE("Apps root access is disabled by system setting - "
+                 "enable it under settings -> developer options");
+            return 1;
+        }
+
+        /* disallow su in a shell if appropriate */
+        if (from->uid == AID_SHELL &&
+                (atoi(enabled) & CM_ROOT_ACCESS_ADB_ONLY) != CM_ROOT_ACCESS_ADB_ONLY ) {
+            LOGE("Shell root access is disabled by a system setting - "
+                 "enable it under settings -> developer options");
+            return 1;
+        }
+        
+    }
+    return 0;
+}
+
+int main(int argc, char *argv[])
+{
+    LOGD("su invoked.");
+
+    struct su_context ctx = {
+        .from = {
+            .pid = -1,
+            .uid = 0,
+            .bin = "",
+            .args = "",
+        },
+        .to = {
+            .uid = AID_ROOT,
+            .login = 0,
+            .keepenv = 0,
+            .shell = DEFAULT_SHELL,
+            .command = NULL,
+            .argv = argv,
+            .argc = argc,
+            .optind = 0,
+        },
+        .user = {
+            .userid = 0,
+            .owner_mode = -1,
+            .data_path = REQUESTOR_DATA_PATH,
+            .store_path = REQUESTOR_STORED_PATH,
+            .store_default = REQUESTOR_STORED_DEFAULT,
+        },
+    };
+    struct stat st;
+    int c, socket_serv_fd, fd;
+    char buf[64], *result;
+    allow_t dballow;
+    struct option long_opts[] = {
+        { "command",            required_argument,    NULL, 'c' },
+        { "help",            no_argument,        NULL, 'h' },
+        { "login",            no_argument,        NULL, 'l' },
+        { "preserve-environment",    no_argument,        NULL, 'p' },
+        { "shell",            required_argument,    NULL, 's' },
+        { "version",            no_argument,        NULL, 'v' },
+        { NULL, 0, NULL, 0 },
+    };
+
+    while ((c = getopt_long(argc, argv, "+c:hlmps:Vv", long_opts, NULL)) != -1) {
+        switch(c) {
+        case 'c':
+            ctx.to.command = optarg;
+            break;
+        case 'h':
+            usage(EXIT_SUCCESS);
+            break;
+        case 'l':
+            ctx.to.login = 1;
+            break;
+        case 'm':
+        case 'p':
+            ctx.to.keepenv = 1;
+            break;
+        case 's':
+            ctx.to.shell = optarg;
+            break;
+        case 'V':
+            printf("%d\n", VERSION_CODE);
+            exit(EXIT_SUCCESS);
+        case 'v':
+            printf("%s\n", VERSION);
+            exit(EXIT_SUCCESS);
+        default:
+            /* Bionic getopt_long doesn't terminate its error output by newline */
+            fprintf(stderr, "\n");
+            usage(2);
+        }
+    }
+    if (optind < argc && !strcmp(argv[optind], "-")) {
+        ctx.to.login = 1;
+        optind++;
+    }
+    /* username or uid */
+    if (optind < argc && strcmp(argv[optind], "--")) {
+        struct passwd *pw;
+        pw = getpwnam(argv[optind]);
+        if (!pw) {
+            char *endptr;
+
+            /* It seems we shouldn't do this at all */
+            errno = 0;
+            ctx.to.uid = strtoul(argv[optind], &endptr, 10);
+            if (errno || *endptr) {
+                LOGE("Unknown id: %s\n", argv[optind]);
+                fprintf(stderr, "Unknown id: %s\n", argv[optind]);
+                exit(EXIT_FAILURE);
+            }
+        } else {
+            ctx.to.uid = pw->pw_uid;
+        }
+        optind++;
+    }
+    if (optind < argc && !strcmp(argv[optind], "--")) {
+        optind++;
+    }
+    ctx.to.optind = optind;
+
+    su_ctx = &ctx;
+    if (from_init(&ctx.from) < 0) {
+        deny(&ctx);
+    }
+        
+    read_options(&ctx);
+    user_init(&ctx);
+    
+    if (ctx.user.owner_mode == -1 && ctx.user.userid != 0)
+        deny(&ctx);
+
+    if (access_disabled(&ctx.from))
+        deny(&ctx);
+
+    ctx.umask = umask(027);
+
+    /*
+     * set LD_LIBRARY_PATH if the linker has wiped out it due to we're suid.
+     * This occurs on Android 4.0+
+     */
+    setenv("LD_LIBRARY_PATH", "/vendor/lib:/system/lib", 0);
+    if (ctx.from.uid == AID_ROOT || ctx.from.uid == AID_SHELL)
+        allow(&ctx);
+
+    if (stat(ctx.user.data_path, &st) < 0) {
+        PLOGE("stat");
+        deny(&ctx);
+    }
+
+    if (st.st_gid != st.st_uid)
+    {
+        LOGE("Bad uid/gid %d/%d for Superuser Requestor application",
+                (int)st.st_uid, (int)st.st_gid);
+        deny(&ctx);
+    }
+
+    mkdir(REQUESTOR_CACHE_PATH, 0770);
+    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);
+    }
+
+    if (setgroups(0, NULL)) {
+        PLOGE("setgroups");
+        deny(&ctx);
+    }
+    if (setegid(st.st_gid)) {
+        PLOGE("setegid (%lu)", st.st_gid);
+        deny(&ctx);
+    }
+    if (seteuid(st.st_uid)) {
+        PLOGE("seteuid (%lu)", st.st_uid);
+        deny(&ctx);
+    }
+
+    dballow = database_check(&ctx);
+    switch (dballow) {
+        case INTERACTIVE: break;
+        case ALLOW: allow(&ctx);    /* never returns */
+        case DENY:
+        default: deny(&ctx);        /* never returns too */
+    }
+    
+    socket_serv_fd = socket_create_temp(ctx.sock_path, sizeof(ctx.sock_path));
+    LOGD(ctx.sock_path);
+    if (socket_serv_fd < 0) {
+        deny(&ctx);
+    }
+
+    if (send_intent(&ctx, INTERACTIVE, ACTION_REQUEST) < 0) {
+        deny(&ctx);
+    }
+
+    fd = socket_accept(socket_serv_fd);
+    LOGE("accepted");
+    if (fd < 0) {
+        deny(&ctx);
+    }
+    if (socket_send_request(fd, &ctx)) {
+        deny(&ctx);
+    }
+    if (socket_receive_result(fd, buf, sizeof(buf))) {
+        deny(&ctx);
+    }
+
+    close(fd);
+    close(socket_serv_fd);
+
+    result = buf;
+
+#define SOCKET_RESPONSE    "socket:"
+    if (strncmp(result, SOCKET_RESPONSE, sizeof(SOCKET_RESPONSE) - 1))
+        LOGW("SECURITY RISK: Requestor still receives credentials in intent");
+    else
+        result += sizeof(SOCKET_RESPONSE) - 1;
+
+    if (!strcmp(result, "DENY")) {
+        deny(&ctx);
+    } else if (!strcmp(result, "ALLOW")) {
+        allow(&ctx);
+    } else {
+        LOGE("unknown response from Superuser Requestor: %s", result);
+        deny(&ctx);
+    }
+}
diff --git a/Superuser/jni/su/su.h b/Superuser/jni/su/su.h
new file mode 100644 (file)
index 0000000..44535e1
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+** Copyright 2010, Adam Shanks (@ChainsDD)
+** Copyright 2008, Zinx Verituse (@zinxv)
+**
+** 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.
+*/
+
+#ifndef SU_h 
+#define SU_h 1
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "su"
+
+#ifndef AID_SHELL
+#define AID_SHELL (get_shell_uid())
+#endif
+
+#ifndef AID_ROOT
+#define AID_ROOT  0
+#endif
+
+// CyanogenMod-specific behavior
+#define CM_ROOT_ACCESS_DISABLED      0
+#define CM_ROOT_ACCESS_APPS_ONLY     1
+#define CM_ROOT_ACCESS_ADB_ONLY      2
+#define CM_ROOT_ACCESS_APPS_AND_ADB  3
+
+#define REQUESTOR "com.koushikdutta.superuser"
+#define REQUESTOR_DATA_PATH "/data/data/" REQUESTOR
+#define REQUESTOR_CACHE_PATH "/dev/" REQUESTOR
+
+#define REQUESTOR_STORED_PATH REQUESTOR_DATA_PATH "/files/stored"
+#define REQUESTOR_STORED_DEFAULT REQUESTOR_STORED_PATH "/default"
+#define REQUESTOR_OPTIONS REQUESTOR_STORED_PATH "/options"
+
+/* intent actions */
+#define ACTION_REQUEST REQUESTOR ".REQUEST"
+#define ACTION_RESULT  REQUESTOR ".RESULT"
+
+#define DEFAULT_SHELL "/system/bin/sh"
+
+#ifdef SU_LEGACY_BUILD
+#define VERSION_EXTRA  "l"
+#else
+#define VERSION_EXTRA  ""
+#endif
+
+#define VERSION "3.3" VERSION_EXTRA
+#define VERSION_CODE 19
+
+#define DATABASE_VERSION 6
+#define PROTO_VERSION 0
+
+struct su_initiator {
+    pid_t pid;
+    unsigned uid;
+    unsigned user;
+    char bin[PATH_MAX];
+    char args[4096];
+};
+
+struct su_request {
+    unsigned uid;
+    int login;
+    int keepenv;
+    char *shell;
+    char *command;
+    char **argv;
+    int argc;
+    int optind;
+    int appId;
+    int all;
+};
+
+struct su_user_info {
+    unsigned userid;
+    int owner_mode;
+    char data_path[PATH_MAX];
+    char store_path[PATH_MAX];
+    char store_default[PATH_MAX];
+};
+
+struct su_context {
+    struct su_initiator from;
+    struct su_request to;
+    struct su_user_info user;
+    mode_t umask;
+    char sock_path[PATH_MAX];
+};
+
+typedef enum {
+    INTERACTIVE = -1,
+    DENY = 0,
+    ALLOW = 1,
+} allow_t;
+
+extern allow_t database_check(struct su_context *ctx);
+extern void set_identity(unsigned int uid);
+extern int send_intent(struct su_context *ctx,
+                       allow_t allow, const char *action);
+extern void sigchld_handler(int sig);
+
+static inline char *get_command(const struct su_request *to)
+{
+       return (to->command) ? to->command : to->shell;
+}
+
+void exec_loge(const char* fmt, ...);
+void exec_logw(const char* fmt, ...);
+void exec_logd(const char* fmt, ...);
+#define LOGE exec_loge
+#define LOGD exec_logd
+#define LOGW exec_logw
+
+#if 0
+#undef LOGE
+#define LOGE(fmt,args...) fprintf(stderr, fmt, ##args)
+#undef LOGD
+#define LOGD(fmt,args...) fprintf(stderr, fmt, ##args)
+#undef LOGW
+#define LOGW(fmt,args...) fprintf(stderr, fmt, ##args)
+#endif
+
+#include <errno.h>
+#include <string.h>
+#define PLOGE(fmt,args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno))
+#define PLOGEV(fmt,err,args...) LOGE(fmt " failed with %d: %s", ##args, err, strerror(err))
+
+#endif
diff --git a/Superuser/jni/su/utils.c b/Superuser/jni/su/utils.c
new file mode 100644 (file)
index 0000000..09b9b60
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+** Copyright 2012, The CyanogenMod Project
+**
+** 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.
+*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "utils.h"
+
+/* reads a file, making sure it is terminated with \n \0 */
+char* read_file(const char *fn)
+{
+    struct stat st;
+    char *data = NULL;
+
+    int fd = open(fn, O_RDONLY);
+    if (fd < 0) return data;
+
+    if (fstat(fd, &st)) goto oops;
+
+    data = malloc(st.st_size + 2);
+    if (!data) goto oops;
+
+    if (read(fd, data, st.st_size) != st.st_size) goto oops;
+    close(fd);
+    data[st.st_size] = '\n';
+    data[st.st_size + 1] = 0;
+    return data;
+
+oops:
+    close(fd);
+    if (data) free(data);
+    return NULL;
+}
+
+int get_property(const char *data, char *found, const char *searchkey, const char *not_found)
+{
+    char *key, *value, *eol, *sol, *tmp;
+    if (data == NULL) goto defval;
+    int matched = 0;
+    sol = strdup(data);
+    while((eol = strchr(sol, '\n'))) {
+        key = sol;
+        *eol++ = 0;
+        sol = eol;
+
+        value = strchr(key, '=');
+        if(value == 0) continue;
+        *value++ = 0;
+
+        while(isspace(*key)) key++;
+        if(*key == '#') continue;
+        tmp = value - 2;
+        while((tmp > key) && isspace(*tmp)) *tmp-- = 0;
+
+        while(isspace(*value)) value++;
+        tmp = eol - 2;
+        while((tmp > value) && isspace(*tmp)) *tmp-- = 0;
+
+        if (strncmp(searchkey, key, strlen(searchkey)) == 0) {
+            matched = 1;
+            break;
+        }
+    }
+    int len;
+    if (matched) {
+        len = strlen(value);
+        if (len >= PROPERTY_VALUE_MAX)
+            return -1;
+        memcpy(found, value, len + 1);
+    } else goto defval;
+    return len;
+
+defval:
+    len = strlen(not_found);
+    memcpy(found, not_found, len + 1);
+    return len;
+}
+
+/*
+ * Fast version of get_property which purpose is to check
+ * whether the property with given prefix exists.
+ *
+ * Assume nobody is stupid enough to put a propery with prefix ro.cm.version
+ * in his build.prop on a non-CM ROM and comment it out.
+ */
+int check_property(const char *data, const char *prefix)
+{
+    if (!data)
+        return 0;
+    return strstr(data, prefix) != NULL;
+}
diff --git a/Superuser/jni/su/utils.h b/Superuser/jni/su/utils.h
new file mode 100644 (file)
index 0000000..a9d4a8f
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+** Copyright 2012, The CyanogenMod Project
+**
+** 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.
+*/
+
+#ifndef _UTILS_H_
+#define _UTILS_H_
+
+#ifndef PROPERTY_VALUE_MAX
+#define PROPERTY_VALUE_MAX  92
+#endif
+
+/* reads a file, making sure it is terminated with \n \0 */
+extern char* read_file(const char *fn);
+
+extern int get_property(const char *data, char *found, const char *searchkey,
+                        const char *not_found);
+extern int check_property(const char *data, const char *prefix);
+#endif
diff --git a/Superuser/proguard-project.txt b/Superuser/proguard-project.txt
new file mode 100644 (file)
index 0000000..f2fe155
--- /dev/null
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
diff --git a/Superuser/project.properties b/Superuser/project.properties
new file mode 100644 (file)
index 0000000..a3ee5ab
--- /dev/null
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-17
diff --git a/Superuser/res/drawable-hdpi/ic_launcher.png b/Superuser/res/drawable-hdpi/ic_launcher.png
new file mode 100644 (file)
index 0000000..96a442e
Binary files /dev/null and b/Superuser/res/drawable-hdpi/ic_launcher.png differ
diff --git a/Superuser/res/drawable-mdpi/ic_launcher.png b/Superuser/res/drawable-mdpi/ic_launcher.png
new file mode 100644 (file)
index 0000000..359047d
Binary files /dev/null and b/Superuser/res/drawable-mdpi/ic_launcher.png differ
diff --git a/Superuser/res/drawable-xhdpi/ic_launcher.png b/Superuser/res/drawable-xhdpi/ic_launcher.png
new file mode 100644 (file)
index 0000000..71c6d76
Binary files /dev/null and b/Superuser/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/Superuser/res/layout/activity_main.xml b/Superuser/res/layout/activity_main.xml
new file mode 100644 (file)
index 0000000..effccb9
--- /dev/null
@@ -0,0 +1,16 @@
+<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    tools:context=".MainActivity" >
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/hello_world" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/Superuser/res/menu/main.xml b/Superuser/res/menu/main.xml
new file mode 100644 (file)
index 0000000..d227c49
--- /dev/null
@@ -0,0 +1,9 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <item
+        android:id="@+id/action_settings"
+        android:orderInCategory="100"
+        android:showAsAction="never"
+        android:title="@string/action_settings"/>
+
+</menu>
\ No newline at end of file
diff --git a/Superuser/res/values-sw600dp/dimens.xml b/Superuser/res/values-sw600dp/dimens.xml
new file mode 100644 (file)
index 0000000..1ba777d
--- /dev/null
@@ -0,0 +1,8 @@
+<resources>
+
+    <!--
+         Customize dimensions originally defined in res/values/dimens.xml (such as
+         screen margins) for sw600dp devices (e.g. 7" tablets) here.
+    -->
+
+</resources>
\ No newline at end of file
diff --git a/Superuser/res/values-sw720dp-land/dimens.xml b/Superuser/res/values-sw720dp-land/dimens.xml
new file mode 100644 (file)
index 0000000..eee741a
--- /dev/null
@@ -0,0 +1,9 @@
+<resources>
+
+    <!--
+         Customize dimensions originally defined in res/values/dimens.xml (such as
+         screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here.
+    -->
+    <dimen name="activity_horizontal_margin">128dp</dimen>
+
+</resources>
\ No newline at end of file
diff --git a/Superuser/res/values-v11/styles.xml b/Superuser/res/values-v11/styles.xml
new file mode 100644 (file)
index 0000000..541752f
--- /dev/null
@@ -0,0 +1,11 @@
+<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>
+
+</resources>
\ No newline at end of file
diff --git a/Superuser/res/values-v14/styles.xml b/Superuser/res/values-v14/styles.xml
new file mode 100644 (file)
index 0000000..f20e015
--- /dev/null
@@ -0,0 +1,12 @@
+<resources>
+
+    <!--
+        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.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+        <!-- API 14 theme customizations can go here. -->
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/Superuser/res/values/dimens.xml b/Superuser/res/values/dimens.xml
new file mode 100644 (file)
index 0000000..a6dd140
--- /dev/null
@@ -0,0 +1,7 @@
+<resources>
+
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+
+</resources>
\ No newline at end of file
diff --git a/Superuser/res/values/strings.xml b/Superuser/res/values/strings.xml
new file mode 100644 (file)
index 0000000..a288577
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <string name="app_name">Superuser</string>
+    <string name="action_settings">Settings</string>
+    <string name="hello_world">Hello world!</string>
+
+</resources>
\ No newline at end of file
diff --git a/Superuser/res/values/styles.xml b/Superuser/res/values/styles.xml
new file mode 100644 (file)
index 0000000..4a10ca4
--- /dev/null
@@ -0,0 +1,20 @@
+<resources>
+
+    <!--
+        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">
+        <!--
+            Theme customizations available in newer API levels can go in
+            res/values-vXX/styles.xml, while customizations related to
+            backward-compatibility can go here.
+        -->
+    </style>
+
+    <!-- Application theme. -->
+    <style name="AppTheme" parent="AppBaseTheme">
+        <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/Superuser/src/com/koushikdutta/superuser/MainActivity.java b/Superuser/src/com/koushikdutta/superuser/MainActivity.java
new file mode 100644 (file)
index 0000000..6d3f925
--- /dev/null
@@ -0,0 +1,22 @@
+package com.koushikdutta.superuser;
+
+import android.os.Bundle;
+import android.app.Activity;
+import android.view.Menu;
+
+public class MainActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        // Inflate the menu; this adds items to the action bar if it is present.
+        getMenuInflater().inflate(R.menu.main, menu);
+        return true;
+    }
+
+}
diff --git a/Superuser/src/com/koushikdutta/superuser/SuReceiver.java b/Superuser/src/com/koushikdutta/superuser/SuReceiver.java
new file mode 100644 (file)
index 0000000..c655ed0
--- /dev/null
@@ -0,0 +1,20 @@
+package com.koushikdutta.superuser;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class SuReceiver extends BroadcastReceiver {
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Bundle b = intent.getExtras();
+        for (String key: b.keySet()) {
+            System.out.println(key);
+            System.out.println("" + b.getString(key));
+            
+        }
+    }
+
+}