diff options
Diffstat (limited to 'cmds')
50 files changed, 3498 insertions, 2160 deletions
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index 38cacdd..293116c 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -28,11 +28,14 @@ import android.content.ComponentName; import android.content.Context; import android.content.IIntentReceiver; import android.content.Intent; +import android.content.pm.IPackageManager; +import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemProperties; import android.util.AndroidException; import android.view.IWindowManager; @@ -43,8 +46,8 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.URISyntaxException; -import java.util.Iterator; -import java.util.Set; +import java.util.HashSet; +import java.util.List; public class Am { @@ -55,6 +58,10 @@ public class Am { private boolean mDebugOption = false; private boolean mWaitOption = false; + private boolean mStopOption = false; + + private String mProfileFile; + private boolean mProfileAutoStop; // These are magic strings understood by the Eclipse plugin. private static final String FATAL_ERROR_CODE = "Error type 1"; @@ -73,7 +80,7 @@ public class Am { showUsage(); System.err.println("Error: " + e.getMessage()); } catch (Exception e) { - System.err.println(e.toString()); + e.printStackTrace(System.err); System.exit(1); } } @@ -98,6 +105,8 @@ public class Am { runStart(); } else if (op.equals("startservice")) { runStartService(); + } else if (op.equals("force-stop")) { + runForceStop(); } else if (op.equals("instrument")) { runInstrument(); } else if (op.equals("broadcast")) { @@ -123,6 +132,8 @@ public class Am { mDebugOption = false; mWaitOption = false; + mStopOption = false; + mProfileFile = null; Uri data = null; String type = null; @@ -154,6 +165,11 @@ public class Am { String value = nextArgRequired(); intent.putExtra(key, Integer.valueOf(value)); hasIntentInfo = true; + } else if (opt.equals("--eu")) { + String key = nextArgRequired(); + String value = nextArgRequired(); + intent.putExtra(key, Uri.parse(value)); + hasIntentInfo = true; } else if (opt.equals("--eia")) { String key = nextArgRequired(); String value = nextArgRequired(); @@ -197,6 +213,10 @@ public class Am { intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } else if (opt.equals("--grant-write-uri-permission")) { intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + } else if (opt.equals("--exclude-stopped-packages")) { + intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES); + } else if (opt.equals("--include-stopped-packages")) { + intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); } else if (opt.equals("--debug-log-resolution")) { intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION); } else if (opt.equals("--activity-brought-to-front")) { @@ -225,6 +245,10 @@ public class Am { intent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); } else if (opt.equals("--activity-single-top")) { intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + } else if (opt.equals("--activity-clear-task")) { + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + } else if (opt.equals("--activity-task-on-home")) { + intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME); } else if (opt.equals("--receiver-registered-only")) { intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); } else if (opt.equals("--receiver-replace-pending")) { @@ -233,6 +257,14 @@ public class Am { mDebugOption = true; } else if (opt.equals("-W")) { mWaitOption = true; + } else if (opt.equals("-P")) { + mProfileFile = nextArgRequired(); + mProfileAutoStop = true; + } else if (opt.equals("--start-profiler")) { + mProfileFile = nextArgRequired(); + mProfileAutoStop = false; + } else if (opt.equals("-S")) { + mStopOption = true; } else { System.err.println("Error: Unknown option: " + opt); showUsage(); @@ -241,23 +273,43 @@ public class Am { } intent.setDataAndType(data, type); - String uri = nextArg(); - if (uri != null) { - Intent oldIntent = intent; - intent = Intent.parseUri(uri, 0); - if (oldIntent.getAction() != null) { - intent.setAction(oldIntent.getAction()); - } - if (oldIntent.getData() != null || oldIntent.getType() != null) { - intent.setDataAndType(oldIntent.getData(), oldIntent.getType()); + String arg = nextArg(); + if (arg != null) { + Intent baseIntent; + if (arg.indexOf(':') >= 0) { + // The argument is a URI. Fully parse it, and use that result + // to fill in any data not specified so far. + baseIntent = Intent.parseUri(arg, Intent.URI_INTENT_SCHEME); + } else if (arg.indexOf('/') >= 0) { + // The argument is a component name. Build an Intent to launch + // it. + baseIntent = new Intent(Intent.ACTION_MAIN); + baseIntent.addCategory(Intent.CATEGORY_LAUNCHER); + baseIntent.setComponent(ComponentName.unflattenFromString(arg)); + } else { + // Assume the argument is a package name. + baseIntent = new Intent(Intent.ACTION_MAIN); + baseIntent.addCategory(Intent.CATEGORY_LAUNCHER); + baseIntent.setPackage(arg); } - Set cats = oldIntent.getCategories(); - if (cats != null) { - Iterator it = cats.iterator(); - while (it.hasNext()) { - intent.addCategory((String)it.next()); + Bundle extras = intent.getExtras(); + intent.replaceExtras((Bundle)null); + Bundle uriExtras = baseIntent.getExtras(); + baseIntent.replaceExtras((Bundle)null); + if (intent.getAction() != null && baseIntent.getCategories() != null) { + HashSet<String> cats = new HashSet<String>(baseIntent.getCategories()); + for (String c : cats) { + baseIntent.removeCategory(c); } } + intent.fillIn(baseIntent, Intent.FILL_IN_COMPONENT); + if (extras == null) { + extras = uriExtras; + } else if (uriExtras != null) { + uriExtras.putAll(extras); + extras = uriExtras; + } + intent.replaceExtras(extras); hasIntentInfo = true; } @@ -276,18 +328,70 @@ public class Am { private void runStart() throws Exception { Intent intent = makeIntent(); + + String mimeType = intent.getType(); + if (mimeType == null && intent.getData() != null + && "content".equals(intent.getData().getScheme())) { + mimeType = mAm.getProviderMimeType(intent.getData()); + } + + if (mStopOption) { + String packageName; + if (intent.getComponent() != null) { + packageName = intent.getComponent().getPackageName(); + } else { + IPackageManager pm = IPackageManager.Stub.asInterface( + ServiceManager.getService("package")); + if (pm == null) { + System.err.println("Error: Package manager not running; aborting"); + return; + } + List<ResolveInfo> activities = pm.queryIntentActivities(intent, mimeType, 0); + if (activities == null || activities.size() <= 0) { + System.err.println("Error: Intent does not match any activities: " + + intent); + return; + } else if (activities.size() > 1) { + System.err.println("Error: Intent matches multiple activities; can't stop: " + + intent); + return; + } + packageName = activities.get(0).activityInfo.packageName; + } + System.out.println("Stopping: " + packageName); + mAm.forceStopPackage(packageName); + Thread.sleep(250); + } + System.out.println("Starting: " + intent); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - // XXX should do something to determine the MIME type. + + ParcelFileDescriptor fd = null; + + if (mProfileFile != null) { + try { + fd = ParcelFileDescriptor.open( + new File(mProfileFile), + ParcelFileDescriptor.MODE_CREATE | + ParcelFileDescriptor.MODE_TRUNCATE | + ParcelFileDescriptor.MODE_READ_WRITE); + } catch (FileNotFoundException e) { + System.err.println("Error: Unable to open file: " + mProfileFile); + return; + } + } + IActivityManager.WaitResult result = null; int res; if (mWaitOption) { - result = mAm.startActivityAndWait(null, intent, intent.getType(), - null, 0, null, null, 0, false, mDebugOption); + result = mAm.startActivityAndWait(null, intent, mimeType, + null, 0, null, null, 0, false, mDebugOption, + mProfileFile, fd, mProfileAutoStop); res = result.result; } else { - res = mAm.startActivity(null, intent, intent.getType(), - null, 0, null, null, 0, false, mDebugOption); + res = mAm.startActivity(null, intent, mimeType, + null, 0, null, null, 0, false, mDebugOption, + mProfileFile, fd, mProfileAutoStop); } PrintStream out = mWaitOption ? System.out : System.err; boolean launched = false; @@ -365,6 +469,10 @@ public class Am { } } + private void runForceStop() throws Exception { + mAm.forceStopPackage(nextArgRequired()); + } + private void sendBroadcast() throws Exception { Intent intent = makeIntent(); IntentReceiver receiver = new IntentReceiver(); @@ -394,7 +502,8 @@ public class Am { argKey = nextArgRequired(); argValue = nextArgRequired(); args.putString(argKey, argValue); - } else if (opt.equals("--no_window_animation")) { + } else if (opt.equals("--no_window_animation") + || opt.equals("--no-window-animation")) { no_window_animation = true; } else { System.err.println("Error: Unknown option: " + opt); @@ -434,15 +543,49 @@ public class Am { } } + static void removeWallOption() { + String props = SystemProperties.get("dalvik.vm.extra-opts"); + if (props != null && props.contains("-Xprofile:wallclock")) { + props = props.replace("-Xprofile:wallclock", ""); + props = props.trim(); + SystemProperties.set("dalvik.vm.extra-opts", props); + } + } + private void runProfile() throws Exception { String profileFile = null; boolean start = false; - String process = nextArgRequired(); - ParcelFileDescriptor fd = null; - + boolean wall = false; + int profileType = 0; + + String process = null; + String cmd = nextArgRequired(); + if ("looper".equals(cmd)) { + cmd = nextArgRequired(); + profileType = 1; + } + if ("start".equals(cmd)) { start = true; + wall = "--wall".equals(nextOption()); + process = nextArgRequired(); + } else if ("stop".equals(cmd)) { + process = nextArg(); + } else { + // Compatibility with old syntax: process is specified first. + process = cmd; + cmd = nextArgRequired(); + if ("start".equals(cmd)) { + start = true; + } else if (!"stop".equals(cmd)) { + throw new IllegalArgumentException("Profile command " + process + " not valid"); + } + } + + ParcelFileDescriptor fd = null; + + if (start) { profileFile = nextArgRequired(); try { fd = ParcelFileDescriptor.open( @@ -454,12 +597,27 @@ public class Am { System.err.println("Error: Unable to open file: " + profileFile); return; } - } else if (!"stop".equals(cmd)) { - throw new IllegalArgumentException("Profile command " + cmd + " not valid"); } - if (!mAm.profileControl(process, start, profileFile, fd)) { - throw new AndroidException("PROFILE FAILED on process " + process); + try { + if (wall) { + // XXX doesn't work -- this needs to be set before booting. + String props = SystemProperties.get("dalvik.vm.extra-opts"); + if (props == null || !props.contains("-Xprofile:wallclock")) { + props = props + " -Xprofile:wallclock"; + //SystemProperties.set("dalvik.vm.extra-opts", props); + } + } else if (start) { + //removeWallOption(); + } + if (!mAm.profileControl(process, start, profileFile, fd, profileType)) { + wall = false; + throw new AndroidException("PROFILE FAILED on process " + process); + } + } finally { + if (!wall) { + //removeWallOption(); + } } } @@ -851,7 +1009,7 @@ public class Am { wm.clearForcedDisplaySize(); } } catch (RemoteException e) { - } + } } private class IntentReceiver extends IIntentReceiver.Stub { @@ -1006,60 +1164,80 @@ public class Am { private static void showUsage() { System.err.println( "usage: am [subcommand] [options]\n" + + "usage: am start [-D] [-W] [-P <FILE>] [--start-profiler <FILE>] [-S] <INTENT>\n" + + " am startservice <INTENT>\n" + + " am force-stop <PACKAGE>\n" + + " am broadcast <INTENT>\n" + + " am instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]\n" + + " [--no-window-animation] <COMPONENT>\n" + + " am profile [looper] start <PROCESS> <FILE>\n" + + " am profile [looper] stop [<PROCESS>]\n" + + " am dumpheap [flags] <PROCESS> <FILE>\n" + + " am monitor [--gdb <port>]\n" + + " am screen-compat [on|off] <PACKAGE>\n" + + " am display-size [reset|MxN]\n" + + "\n" + + "am start: start an Activity. Options are:\n" + + " -D: enable debugging\n" + + " -W: wait for launch to complete\n" + + " --start-profiler <FILE>: start profiler and send results to <FILE>\n" + + " -P <FILE>: like above, but profiling stops when app goes idle\n" + + " -S: force stop the target app before starting the activity\n" + "\n" + - " start an Activity: am start [-D] [-W] <INTENT>\n" + - " -D: enable debugging\n" + - " -W: wait for launch to complete\n" + + "am startservice: start a Service.\n" + "\n" + - " start a Service: am startservice <INTENT>\n" + + "am force-stop: force stop everything associated with <PACKAGE>.\n" + "\n" + - " send a broadcast Intent: am broadcast <INTENT>\n" + + "am broadcast: send a broadcast Intent.\n" + "\n" + - " start an Instrumentation: am instrument [flags] <COMPONENT>\n" + - " -r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT)\n" + - " -e <NAME> <VALUE>: set argument <NAME> to <VALUE>\n" + - " -p <FILE>: write profiling data to <FILE>\n" + - " -w: wait for instrumentation to finish before returning\n" + + "am instrument: start an Instrumentation. Typically this target <COMPONENT>\n" + + " is the form <TEST_PACKAGE>/<RUNNER_CLASS>. Options are:\n" + + " -r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT). Use with\n" + + " [-e perf true] to generate raw output for performance measurements.\n" + + " -e <NAME> <VALUE>: set argument <NAME> to <VALUE>. For test runners a\n" + + " common form is [-e <testrunner_flag> <value>[,<value>...]].\n" + + " -p <FILE>: write profiling data to <FILE>\n" + + " -w: wait for instrumentation to finish before returning. Required for\n" + + " test runners.\n" + + " --no-window-animation: turn off window animations will running.\n" + "\n" + - " run a test package against an application: am instrument [flags] <TEST_PACKAGE>/<RUNNER_CLASS>\n" + - " -e <testrunner_flag> <testrunner_value> [,<testrunner_value>]\n" + - " -w wait for the test to finish (required)\n" + - " -r use with -e perf true to generate raw output for performance measurements\n" + + "am profile: start and stop profiler on a process.\n" + "\n" + - " start profiling: am profile <PROCESS> start <FILE>\n" + - " stop profiling: am profile <PROCESS> stop\n" + - " dump heap: am dumpheap [flags] <PROCESS> <FILE>\n" + - " -n: dump native heap instead of managed heap\n" + + "am dumpheap: dump the heap of a process. Options are:\n" + + " -n: dump native heap instead of managed heap\n" + "\n" + - " start monitoring: am monitor [--gdb <port>]\n" + - " --gdb: start gdbserv on the given port at crash/ANR\n" + + "am monitor: start monitoring for crashes or ANRs.\n" + + " --gdb: start gdbserv on the given port at crash/ANR\n" + "\n" + - " control screen compatibility: am screen-compat [on|off] [package]\n" + + "am screen-compat: control screen compatibility mode of <PACKAGE>.\n" + "\n" + - " override display size: am display-size [reset|MxN]\n" + + "am display-size: override display size.\n" + "\n" + - " <INTENT> specifications include these flags:\n" + - " [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" + - " [-c <CATEGORY> [-c <CATEGORY>] ...]\n" + - " [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]\n" + - " [--esn <EXTRA_KEY> ...]\n" + - " [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]\n" + - " [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" + - " [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]\n" + - " [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]\n" + - " [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]\n" + - " [-n <COMPONENT>] [-f <FLAGS>]\n" + - " [--grant-read-uri-permission] [--grant-write-uri-permission]\n" + - " [--debug-log-resolution]\n" + - " [--activity-brought-to-front] [--activity-clear-top]\n" + - " [--activity-clear-when-task-reset] [--activity-exclude-from-recents]\n" + - " [--activity-launched-from-history] [--activity-multiple-task]\n" + - " [--activity-no-animation] [--activity-no-history]\n" + - " [--activity-no-user-action] [--activity-previous-is-top]\n" + - " [--activity-reorder-to-front] [--activity-reset-task-if-needed]\n" + - " [--activity-single-top]\n" + - " [--receiver-registered-only] [--receiver-replace-pending]\n" + - " [<URI>]\n" + "<INTENT> specifications include these flags and arguments:\n" + + " [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" + + " [-c <CATEGORY> [-c <CATEGORY>] ...]\n" + + " [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]\n" + + " [--esn <EXTRA_KEY> ...]\n" + + " [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]\n" + + " [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" + + " [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]\n" + + " [--eu <EXTRA_KEY> <EXTRA_URI_VALUE> ...]\n" + + " [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]\n" + + " [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]\n" + + " [-n <COMPONENT>] [-f <FLAGS>]\n" + + " [--grant-read-uri-permission] [--grant-write-uri-permission]\n" + + " [--debug-log-resolution] [--exclude-stopped-packages]\n" + + " [--include-stopped-packages]\n" + + " [--activity-brought-to-front] [--activity-clear-top]\n" + + " [--activity-clear-when-task-reset] [--activity-exclude-from-recents]\n" + + " [--activity-launched-from-history] [--activity-multiple-task]\n" + + " [--activity-no-animation] [--activity-no-history]\n" + + " [--activity-no-user-action] [--activity-previous-is-top]\n" + + " [--activity-reorder-to-front] [--activity-reset-task-if-needed]\n" + + " [--activity-single-top] [--activity-clear-task]\n" + + " [--activity-task-on-home]\n" + + " [--receiver-registered-only] [--receiver-replace-pending]\n" + + " [<URI> | <PACKAGE> | <COMPONENT>]\n" ); } } diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp index 0159edd..f2be29f 100644 --- a/cmds/app_process/app_main.cpp +++ b/cmds/app_process/app_main.cpp @@ -1,8 +1,8 @@ /* * Main entry of app process. - * + * * Starts the interpreted runtime, then starts up the application. - * + * */ #define LOG_TAG "appproc" @@ -25,23 +25,13 @@ void app_usage() "Usage: app_process [java-options] cmd-dir start-class-name [options]\n"); } -status_t app_init(const char* className, int argc, const char* const argv[]) -{ - LOGV("Entered app_init()!\n"); - - AndroidRuntime* jr = AndroidRuntime::getRuntime(); - jr->callMain(className, argc, argv); - - LOGV("Exiting app_init()!\n"); - return NO_ERROR; -} - class AppRuntime : public AndroidRuntime { public: AppRuntime() : mParentDir(NULL) , mClassName(NULL) + , mClass(NULL) , mArgC(0) , mArgV(NULL) { @@ -60,45 +50,68 @@ public: return mClassName; } - virtual void onStarted() + virtual void onVmCreated(JNIEnv* env) { - sp<ProcessState> proc = ProcessState::self(); - if (proc->supportsProcesses()) { - LOGV("App process: starting thread pool.\n"); - proc->startThreadPool(); + if (mClassName == NULL) { + return; // Zygote. Nothing to do here. } - - app_init(mClassName, mArgC, mArgV); - if (ProcessState::self()->supportsProcesses()) { - IPCThreadState::self()->stopProcess(); + /* + * This is a little awkward because the JNI FindClass call uses the + * class loader associated with the native method we're executing in. + * If called in onStarted (from RuntimeInit.finishInit because we're + * launching "am", for example), FindClass would see that we're calling + * from a boot class' native method, and so wouldn't look for the class + * we're trying to look up in CLASSPATH. Unfortunately it needs to, + * because the "am" classes are not boot classes. + * + * The easiest fix is to call FindClass here, early on before we start + * executing boot class Java code and thereby deny ourselves access to + * non-boot classes. + */ + char* slashClassName = toSlashClassName(mClassName); + mClass = env->FindClass(slashClassName); + if (mClass == NULL) { + LOGE("ERROR: could not find class '%s'\n", mClassName); } + free(slashClassName); + + mClass = reinterpret_cast<jclass>(env->NewGlobalRef(mClass)); + } + + virtual void onStarted() + { + sp<ProcessState> proc = ProcessState::self(); + LOGV("App process: starting thread pool.\n"); + proc->startThreadPool(); + + AndroidRuntime* ar = AndroidRuntime::getRuntime(); + ar->callMain(mClassName, mClass, mArgC, mArgV); + + IPCThreadState::self()->stopProcess(); } virtual void onZygoteInit() { sp<ProcessState> proc = ProcessState::self(); - if (proc->supportsProcesses()) { - LOGV("App process: starting thread pool.\n"); - proc->startThreadPool(); - } + LOGV("App process: starting thread pool.\n"); + proc->startThreadPool(); } virtual void onExit(int code) { if (mClassName == NULL) { // if zygote - if (ProcessState::self()->supportsProcesses()) { - IPCThreadState::self()->stopProcess(); - } + IPCThreadState::self()->stopProcess(); } AndroidRuntime::onExit(code); } - + const char* mParentDir; const char* mClassName; + jclass mClass; int mArgC; const char* const* mArgV; }; @@ -120,7 +133,7 @@ int main(int argc, const char* const argv[]) // These are global variables in ProcessState.cpp mArgC = argc; mArgV = argv; - + mArgLen = 0; for (int i=0; i<argc; i++) { mArgLen += strlen(argv[i]) + 1; @@ -128,10 +141,7 @@ int main(int argc, const char* const argv[]) mArgLen--; AppRuntime runtime; - const char *arg; - const char *argv0; - - argv0 = argv[0]; + const char* argv0 = argv[0]; // Process command line arguments // ignore argv[0] @@ -139,42 +149,56 @@ int main(int argc, const char* const argv[]) argv++; // Everything up to '--' or first non '-' arg goes to the vm - - int i = runtime.addVmArguments(argc, argv); - // Next arg is parent directory - if (i < argc) { - runtime.mParentDir = argv[i++]; - } + int i = runtime.addVmArguments(argc, argv); - // Next arg is startup classname or "--zygote" - if (i < argc) { - arg = argv[i++]; - if (0 == strcmp("--zygote", arg)) { - bool startSystemServer = (i < argc) ? - strcmp(argv[i], "--start-system-server") == 0 : false; - setArgv0(argv0, "zygote"); - set_process_name("zygote"); - runtime.start("com.android.internal.os.ZygoteInit", - startSystemServer); + // Parse runtime arguments. Stop at first unrecognized option. + bool zygote = false; + bool startSystemServer = false; + bool application = false; + const char* parentDir = NULL; + const char* niceName = NULL; + const char* className = NULL; + while (i < argc) { + const char* arg = argv[i++]; + if (!parentDir) { + parentDir = arg; + } else if (strcmp(arg, "--zygote") == 0) { + zygote = true; + niceName = "zygote"; + } else if (strcmp(arg, "--start-system-server") == 0) { + startSystemServer = true; + } else if (strcmp(arg, "--application") == 0) { + application = true; + } else if (strncmp(arg, "--nice-name=", 12) == 0) { + niceName = arg + 12; } else { - set_process_name(argv0); - - runtime.mClassName = arg; + className = arg; + break; + } + } - // Remainder of args get passed to startup class main() - runtime.mArgC = argc-i; - runtime.mArgV = argv+i; + if (niceName && *niceName) { + setArgv0(argv0, niceName); + set_process_name(niceName); + } - LOGV("App process is starting with pid=%d, class=%s.\n", - getpid(), runtime.getClassName()); - runtime.start(); - } + runtime.mParentDir = parentDir; + + if (zygote) { + runtime.start("com.android.internal.os.ZygoteInit", + startSystemServer ? "start-system-server" : ""); + } else if (className) { + // Remainder of args get passed to startup class main() + runtime.mClassName = className; + runtime.mArgC = argc - i; + runtime.mArgV = argv + i; + runtime.start("com.android.internal.os.RuntimeInit", + application ? "application" : "tool"); } else { fprintf(stderr, "Error: no class name or --zygote supplied.\n"); app_usage(); LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied."); return 10; } - } diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java index ac0e410..2666b41 100644 --- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java +++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java @@ -23,6 +23,8 @@ import android.app.backup.IRestoreSession; import android.os.RemoteException; import android.os.ServiceManager; +import java.util.HashSet; + public final class Bmgr { IBackupManager mBmgr; IRestoreSession mRestore; @@ -45,7 +47,6 @@ public final class Bmgr { } public void run(String[] args) { - boolean validCommand = false; if (args.length < 1) { showUsage(); return; @@ -150,20 +151,13 @@ public final class Bmgr { } private void doBackup() { - boolean isFull = false; String pkg = nextArg(); - if ("-f".equals(pkg)) { - isFull = true; - pkg = nextArg(); - } - - if (pkg == null || pkg.startsWith("-")) { + if (pkg == null) { showUsage(); return; } try { - // !!! TODO: handle full backup mBmgr.dataChanged(pkg); } catch (RemoteException e) { System.err.println(e.toString()); @@ -336,7 +330,13 @@ public final class Bmgr { } else { try { long token = Long.parseLong(arg, 16); - doRestoreAll(token); + HashSet<String> filter = null; + while ((arg = nextArg()) != null) { + if (filter == null) filter = new HashSet<String>(); + filter.add(arg); + } + + doRestoreAll(token, filter); } catch (NumberFormatException e) { showUsage(); return; @@ -371,7 +371,7 @@ public final class Bmgr { } } - private void doRestoreAll(long token) { + private void doRestoreAll(long token, HashSet<String> filter) { RestoreObserver observer = new RestoreObserver(); try { @@ -390,7 +390,13 @@ public final class Bmgr { for (RestoreSet s : sets) { if (s.token == token) { System.out.println("Scheduling restore: " + s.name); - didRestore = (mRestore.restoreAll(token, observer) == 0); + if (filter == null) { + didRestore = (mRestore.restoreAll(token, observer) == 0); + } else { + String[] names = new String[filter.size()]; + filter.toArray(names); + didRestore = (mRestore.restoreSome(token, observer, names) == 0); + } break; } } @@ -437,6 +443,7 @@ public final class Bmgr { System.err.println(" bmgr list sets"); System.err.println(" bmgr transport WHICH"); System.err.println(" bmgr restore TOKEN"); + System.err.println(" bmgr restore TOKEN PACKAGE..."); System.err.println(" bmgr restore PACKAGE"); System.err.println(" bmgr run"); System.err.println(" bmgr wipe PACKAGE"); @@ -464,12 +471,18 @@ public final class Bmgr { System.err.println("The 'transport' command designates the named transport as the currently"); System.err.println("active one. This setting is persistent across reboots."); System.err.println(""); - System.err.println("The 'restore' command when given a restore token initiates a full-system"); + System.err.println("The 'restore' command when given just a restore token initiates a full-system"); System.err.println("restore operation from the currently active transport. It will deliver"); System.err.println("the restore set designated by the TOKEN argument to each application"); System.err.println("that had contributed data to that restore set."); System.err.println(""); - System.err.println("The 'restore' command when given a package name intiates a restore of"); + System.err.println("The 'restore' command when given a token and one or more package names"); + System.err.println("initiates a restore operation of just those given packages from the restore"); + System.err.println("set designated by the TOKEN argument. It is effectively the same as the"); + System.err.println("'restore' operation supplying only a token, but applies a filter to the"); + System.err.println("set of applications to be restored."); + System.err.println(""); + System.err.println("The 'restore' command when given just a package name intiates a restore of"); System.err.println("just that one package according to the restore set selection algorithm"); System.err.println("used by the RestoreSession.restorePackage() method."); System.err.println(""); diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk index 2b89759..7d39912 100644 --- a/cmds/bootanimation/Android.mk +++ b/cmds/bootanimation/Android.mk @@ -5,13 +5,6 @@ LOCAL_SRC_FILES:= \ bootanimation_main.cpp \ BootAnimation.cpp -# need "-lrt" on Linux simulator to pick up clock_gettime -ifeq ($(TARGET_SIMULATOR),true) - ifeq ($(HOST_OS),linux) - LOCAL_LDLIBS += -lrt - endif -endif - LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES LOCAL_SHARED_LIBRARIES := \ @@ -22,7 +15,7 @@ LOCAL_SHARED_LIBRARIES := \ libskia \ libEGL \ libGLESv1_CM \ - libsurfaceflinger_client + libgui LOCAL_C_INCLUDES := \ $(call include-path-for, corecg graphics) diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index e07495d..d816e7c 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -37,7 +37,6 @@ #include <ui/Region.h> #include <ui/DisplayInfo.h> #include <ui/FramebufferNativeWindow.h> -#include <ui/EGLUtils.h> #include <surfaceflinger/ISurfaceComposer.h> #include <surfaceflinger/ISurfaceComposerClient.h> @@ -212,15 +211,19 @@ status_t BootAnimation::readyToRun() { // create the native surface sp<SurfaceControl> control = session()->createSurface( - getpid(), 0, dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565); - session()->openTransaction(); + 0, dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565); + + SurfaceComposerClient::openGlobalTransaction(); control->setLayer(0x40000000); - session()->closeTransaction(); + SurfaceComposerClient::closeGlobalTransaction(); sp<Surface> s = control->getSurface(); // initialize opengl and egl const EGLint attribs[] = { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, EGL_DEPTH_SIZE, 0, EGL_NONE }; @@ -233,7 +236,7 @@ status_t BootAnimation::readyToRun() { EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); eglInitialize(display, 0, 0); - EGLUtils::selectConfigForNativeWindow(display, attribs, s.get(), &config); + eglChooseConfig(display, attribs, &config, 1, &numConfigs); surface = eglCreateWindowSurface(display, config, s.get(), NULL); context = eglCreateContext(display, config, NULL, NULL); eglQuerySurface(display, surface, EGL_WIDTH, &w); @@ -302,6 +305,7 @@ bool BootAnimation::android() glShadeModel(GL_FLAT); glDisable(GL_DITHER); glDisable(GL_SCISSOR_TEST); + glClearColor(0,0,0,1); glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(mDisplay, mSurface); @@ -312,9 +316,6 @@ bool BootAnimation::android() const GLint yc = (mHeight - mAndroid[0].h) / 2; const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h); - // draw and update only what we need - mFlingerSurface->setSwapRectangle(updateRect); - glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(), updateRect.height()); @@ -439,6 +440,7 @@ bool BootAnimation::movie() glDisable(GL_DITHER); glDisable(GL_SCISSOR_TEST); glDisable(GL_BLEND); + glClearColor(0,0,0,1); glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(mDisplay, mSurface); diff --git a/cmds/bu/Android.mk b/cmds/bu/Android.mk new file mode 100644 index 0000000..4fd5fec --- /dev/null +++ b/cmds/bu/Android.mk @@ -0,0 +1,18 @@ +# Copyright 2011 The Android Open Source Project +# +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_MODULE := bu +LOCAL_MODULE_TAGS := optional +include $(BUILD_JAVA_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := bu +LOCAL_SRC_FILES := bu +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_MODULE_TAGS := optional +include $(BUILD_PREBUILT) + + diff --git a/cmds/runtime/NOTICE b/cmds/bu/NOTICE index c5b1efa..becc120 100644 --- a/cmds/runtime/NOTICE +++ b/cmds/bu/NOTICE @@ -1,5 +1,5 @@ - Copyright (c) 2005-2008, The Android Open Source Project + Copyright (c) 2011, The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/cmds/bu/bu b/cmds/bu/bu new file mode 100755 index 0000000..e8dbc31 --- /dev/null +++ b/cmds/bu/bu @@ -0,0 +1,6 @@ +# Script to start "bu" on the device +# +base=/system +export CLASSPATH=$base/framework/bu.jar +exec app_process $base/bin com.android.commands.bu.Backup "$@" + diff --git a/cmds/bu/src/com/android/commands/bu/Backup.java b/cmds/bu/src/com/android/commands/bu/Backup.java new file mode 100644 index 0000000..4c4bf98 --- /dev/null +++ b/cmds/bu/src/com/android/commands/bu/Backup.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2011 The Android Open Source 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. + */ + +package com.android.commands.bu; + +import android.app.backup.IBackupManager; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +import java.util.ArrayList; + +public final class Backup { + static final String TAG = "bu"; + + static String[] mArgs; + int mNextArg; + IBackupManager mBackupManager; + + public static void main(String[] args) { + Log.d(TAG, "Beginning: " + args[0]); + mArgs = args; + try { + new Backup().run(); + } catch (Exception e) { + Log.e(TAG, "Error running backup/restore", e); + } + Log.d(TAG, "Finished."); + } + + public void run() { + mBackupManager = IBackupManager.Stub.asInterface(ServiceManager.getService("backup")); + if (mBackupManager == null) { + Log.e(TAG, "Can't obtain Backup Manager binder"); + return; + } + + int socketFd = Integer.parseInt(nextArg()); + + String arg = nextArg(); + if (arg.equals("backup")) { + doFullBackup(socketFd); + } else if (arg.equals("restore")) { + doFullRestore(socketFd); + } else { + Log.e(TAG, "Invalid operation '" + arg + "'"); + } + } + + private void doFullBackup(int socketFd) { + ArrayList<String> packages = new ArrayList<String>(); + boolean saveApks = false; + boolean saveShared = false; + boolean doEverything = false; + + String arg; + while ((arg = nextArg()) != null) { + if (arg.startsWith("-")) { + if ("-apk".equals(arg)) { + saveApks = true; + } else if ("-noapk".equals(arg)) { + saveApks = false; + } else if ("-shared".equals(arg)) { + saveShared = true; + } else if ("-noshared".equals(arg)) { + saveShared = false; + } else if ("-all".equals(arg)) { + doEverything = true; + } else { + Log.w(TAG, "Unknown backup flag " + arg); + continue; + } + } else { + // Not a flag; treat as a package name + packages.add(arg); + } + } + + if (doEverything && packages.size() > 0) { + Log.w(TAG, "-all passed for backup along with specific package names"); + } + + if (!doEverything && !saveShared && packages.size() == 0) { + Log.e(TAG, "no backup packages supplied and neither -shared nor -all given"); + return; + } + + try { + ParcelFileDescriptor fd = ParcelFileDescriptor.adoptFd(socketFd); + String[] packArray = new String[packages.size()]; + mBackupManager.fullBackup(fd, saveApks, saveShared, doEverything, + packages.toArray(packArray)); + } catch (RemoteException e) { + Log.e(TAG, "Unable to invoke backup manager for backup"); + } + } + + private void doFullRestore(int socketFd) { + // No arguments to restore + try { + ParcelFileDescriptor fd = ParcelFileDescriptor.adoptFd(socketFd); + mBackupManager.fullRestore(fd); + } catch (RemoteException e) { + Log.e(TAG, "Unable to invoke backup manager for restore"); + } + } + + private String nextArg() { + if (mNextArg >= mArgs.length) { + return null; + } + String arg = mArgs[mNextArg]; + mNextArg++; + return arg; + } +}
\ No newline at end of file diff --git a/cmds/bugreport/Android.mk b/cmds/bugreport/Android.mk index 631c219..f476f5e 100644 --- a/cmds/bugreport/Android.mk +++ b/cmds/bugreport/Android.mk @@ -1,5 +1,3 @@ -ifneq ($(TARGET_SIMULATOR),true) - LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) @@ -10,5 +8,3 @@ LOCAL_MODULE:= bugreport LOCAL_SHARED_LIBRARIES := libcutils include $(BUILD_EXECUTABLE) - -endif diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk index 431a556..d602500 100644 --- a/cmds/dumpstate/Android.mk +++ b/cmds/dumpstate/Android.mk @@ -1,5 +1,3 @@ -ifneq ($(TARGET_SIMULATOR),true) - LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) @@ -13,6 +11,9 @@ LOCAL_MODULE := dumpstate LOCAL_SHARED_LIBRARIES := libcutils -include $(BUILD_EXECUTABLE) - +ifdef BOARD_LIB_DUMPSTATE +LOCAL_STATIC_LIBRARIES := $(BOARD_LIB_DUMPSTATE) +LOCAL_CFLAGS += -DBOARD_HAS_DUMPSTATE endif + +include $(BUILD_EXECUTABLE) diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c index 05e4044..0b08c5e 100644 --- a/cmds/dumpstate/dumpstate.c +++ b/cmds/dumpstate/dumpstate.c @@ -79,6 +79,10 @@ static void dumpstate() { dump_file("VMALLOC INFO", "/proc/vmallocinfo"); dump_file("SLAB INFO", "/proc/slabinfo"); dump_file("ZONEINFO", "/proc/zoneinfo"); + dump_file("PAGETYPEINFO", "/proc/pagetypeinfo"); + dump_file("BUDDYINFO", "/proc/buddyinfo"); + dump_file("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl"); + run_command("QTAGUID STATS INFO", 10, "su", "root", "cat", "/proc/net/xt_qtaguid/stats", NULL); if (screenshot_path[0]) { LOGI("taking screenshot\n"); @@ -113,12 +117,23 @@ static void dumpstate() { dump_file("NETWORK ROUTES", "/proc/net/route"); dump_file("NETWORK ROUTES IPV6", "/proc/net/ipv6_route"); dump_file("ARP CACHE", "/proc/net/arp"); - run_command("IPTABLES", 10, "su", "root", "iptables", "-L", "-n", NULL); + run_command("IPTABLES", 10, "su", "root", "iptables", "-L", "-nvx", NULL); + run_command("IP6TABLES", 10, "su", "root", "ip6tables", "-L", "-nvx", NULL); run_command("IPTABLE NAT", 10, "su", "root", "iptables", "-t", "nat", "-L", "-n", NULL); + run_command("IPT6ABLE NAT", 10, "su", "root", "ip6tables", "-t", "nat", "-L", "-n", NULL); run_command("WIFI NETWORKS", 20, "su", "root", "wpa_cli", "list_networks", NULL); + property_get("dhcp.wlan0.gateway", network, ""); + if (network[0]) + run_command("PING GATEWAY", 10, "su", "root", "ping", "-c", "3", "-i", ".5", network, NULL); + property_get("dhcp.wlan0.dns1", network, ""); + if (network[0]) + run_command("PING DNS1", 10, "su", "root", "ping", "-c", "3", "-i", ".5", network, NULL); + property_get("dhcp.wlan0.dns2", network, ""); + if (network[0]) + run_command("PING DNS2", 10, "su", "root", "ping", "-c", "3", "-i", ".5", network, NULL); #ifdef FWDUMP_bcm4329 run_command("DUMP WIFI STATUS", 20, "su", "root", "dhdutil", "-i", "wlan0", "dump", NULL); @@ -126,7 +141,8 @@ static void dumpstate() { "su", "root", "wlutil", "counters", NULL); #endif - char ril_dumpstate_timeout[PROPERTY_VALUE_MAX] = {0}; +#ifdef BROKEN_VRIL_IS_FIXED_B_4442803 + char ril_dumpstate_timeout[PROPERTY_VALUE_MAX] = {0}; property_get("ril.dumpstate.timeout", ril_dumpstate_timeout, "30"); if (strnlen(ril_dumpstate_timeout, PROPERTY_VALUE_MAX - 1) > 0) { if (0 == strncmp(build_type, "user", PROPERTY_VALUE_MAX - 1)) { @@ -140,6 +156,7 @@ static void dumpstate() { "su", "root", "vril-dump", NULL); } } +#endif print_properties(); @@ -186,6 +203,17 @@ static void dumpstate() { dump_file(NULL, "/sys/class/leds/lcd-backlight/registers"); printf("\n"); + run_command("LIST OF OPEN FILES", 10, "su", "root", "lsof", NULL); + +#ifdef BOARD_HAS_DUMPSTATE + printf("========================================================\n"); + printf("== Board\n"); + printf("========================================================\n"); + + dumpstate_board(); + printf("\n"); +#endif + printf("========================================================\n"); printf("== Android Framework Services\n"); printf("========================================================\n"); @@ -207,6 +235,9 @@ static void dumpstate() { run_command("APP SERVICES", 30, "dumpsys", "activity", "service", "all", NULL); + printf("========================================================\n"); + printf("== dumpstate: done\n"); + printf("========================================================\n"); } static void usage() { diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index 83b1d11..597ab1f 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -19,6 +19,7 @@ #include <time.h> #include <unistd.h> +#include <stdio.h> /* prints the contents of a file */ int dump_file(const char *title, const char* path); @@ -47,4 +48,7 @@ void show_wchan(int pid, const char *name); /* Play a sound via Stagefright */ void play_sound(const char* path); +/* Implemented by libdumpstate_board to dump board-specific info */ +void dumpstate_board(); + #endif /* _DUMPSTATE_H_ */ diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk index 8641c30..f277339 100644 --- a/cmds/installd/Android.mk +++ b/cmds/installd/Android.mk @@ -1,13 +1,32 @@ -ifneq ($(TARGET_SIMULATOR),true) - LOCAL_PATH := $(call my-dir) + +common_src_files := \ + commands.c utils.c + +# +# Static library used in testing and executable +# + include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - installd.c commands.c utils.c + $(common_src_files) + +LOCAL_MODULE := libinstalld + +LOCAL_MODULE_TAGS := eng tests -#LOCAL_C_INCLUDES := \ -# $(call include-path-for, system-core)/cutils +include $(BUILD_STATIC_LIBRARY) + +# +# Executable +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + installd.c \ + $(common_src_files) LOCAL_SHARED_LIBRARIES := \ libcutils @@ -20,5 +39,3 @@ LOCAL_MODULE := installd LOCAL_MODULE_TAGS := optional include $(BUILD_EXECUTABLE) - -endif # !simulator diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c index ada712d..26b9113 100644 --- a/cmds/installd/commands.c +++ b/cmds/installd/commands.c @@ -17,6 +17,13 @@ #include "installd.h" #include <diskusage/dirsize.h> +/* Directory records that are used in execution of commands. */ +dir_rec_t android_data_dir; +dir_rec_t android_asec_dir; +dir_rec_t android_app_dir; +dir_rec_t android_app_private_dir; +dir_rec_array_t android_system_dirs; + int install(const char *pkgname, uid_t uid, gid_t gid) { char pkgdir[PKG_PATH_MAX]; @@ -27,15 +34,25 @@ int install(const char *pkgname, uid_t uid, gid_t gid) return -1; } - if (create_pkg_path(pkgdir, PKG_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX)) + if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, 0)) { + LOGE("cannot create package path\n"); return -1; - if (create_pkg_path(libdir, PKG_LIB_PREFIX, pkgname, PKG_LIB_POSTFIX)) + } + + if (create_pkg_path(libdir, pkgname, PKG_LIB_POSTFIX, 0)) { + LOGE("cannot create package lib path\n"); return -1; + } if (mkdir(pkgdir, 0751) < 0) { LOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno)); return -errno; } + if (chmod(pkgdir, 0751) < 0) { + LOGE("cannot chmod dir '%s': %s\n", pkgdir, strerror(errno)); + unlink(pkgdir); + return -errno; + } if (chown(pkgdir, uid, gid) < 0) { LOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno)); unlink(pkgdir); @@ -46,6 +63,12 @@ int install(const char *pkgname, uid_t uid, gid_t gid) unlink(pkgdir); return -errno; } + if (chmod(libdir, 0755) < 0) { + LOGE("cannot chmod dir '%s': %s\n", libdir, strerror(errno)); + unlink(libdir); + unlink(pkgdir); + return -errno; + } if (chown(libdir, AID_SYSTEM, AID_SYSTEM) < 0) { LOGE("cannot chown dir '%s': %s\n", libdir, strerror(errno)); unlink(libdir); @@ -55,15 +78,15 @@ int install(const char *pkgname, uid_t uid, gid_t gid) return 0; } -int uninstall(const char *pkgname) +int uninstall(const char *pkgname, uid_t persona) { char pkgdir[PKG_PATH_MAX]; - if (create_pkg_path(pkgdir, PKG_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX)) + if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, persona)) return -1; - /* delete contents AND directory, no exceptions */ - return delete_dir_contents(pkgdir, 1, 0); + /* delete contents AND directory, no exceptions */ + return delete_dir_contents(pkgdir, 1, NULL); } int renamepkg(const char *oldpkgname, const char *newpkgname) @@ -71,9 +94,9 @@ int renamepkg(const char *oldpkgname, const char *newpkgname) char oldpkgdir[PKG_PATH_MAX]; char newpkgdir[PKG_PATH_MAX]; - if (create_pkg_path(oldpkgdir, PKG_DIR_PREFIX, oldpkgname, PKG_DIR_POSTFIX)) + if (create_pkg_path(oldpkgdir, oldpkgname, PKG_DIR_POSTFIX, 0)) return -1; - if (create_pkg_path(newpkgdir, PKG_DIR_PREFIX, newpkgname, PKG_DIR_POSTFIX)) + if (create_pkg_path(newpkgdir, newpkgname, PKG_DIR_POSTFIX, 0)) return -1; if (rename(oldpkgdir, newpkgdir) < 0) { @@ -83,22 +106,53 @@ int renamepkg(const char *oldpkgname, const char *newpkgname) return 0; } -int delete_user_data(const char *pkgname) +int delete_user_data(const char *pkgname, uid_t persona) { char pkgdir[PKG_PATH_MAX]; - if (create_pkg_path(pkgdir, PKG_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX)) + if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, persona)) return -1; - /* delete contents, excluding "lib", but not the directory itself */ + /* delete contents, excluding "lib", but not the directory itself */ return delete_dir_contents(pkgdir, 0, "lib"); } +int make_user_data(const char *pkgname, uid_t uid, uid_t persona) +{ + char pkgdir[PKG_PATH_MAX]; + char real_libdir[PKG_PATH_MAX]; + + // Create the data dir for the package + if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, persona)) { + return -1; + } + if (mkdir(pkgdir, 0751) < 0) { + LOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno)); + return -errno; + } + if (chown(pkgdir, uid, uid) < 0) { + LOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno)); + unlink(pkgdir); + return -errno; + } + return 0; +} + +int delete_persona(uid_t persona) +{ + char pkgdir[PKG_PATH_MAX]; + + if (create_persona_path(pkgdir, persona)) + return -1; + + return delete_dir_contents(pkgdir, 1, NULL); +} + int delete_cache(const char *pkgname) { char cachedir[PKG_PATH_MAX]; - if (create_pkg_path(cachedir, CACHE_DIR_PREFIX, pkgname, CACHE_DIR_POSTFIX)) + if (create_pkg_path(cachedir, pkgname, CACHE_DIR_POSTFIX, 0)) return -1; /* delete contents, not the directory, no exceptions */ @@ -108,10 +162,10 @@ int delete_cache(const char *pkgname) static int64_t disk_free() { struct statfs sfs; - if (statfs(PKG_DIR_PREFIX, &sfs) == 0) { + if (statfs(android_data_dir.path, &sfs) == 0) { return sfs.f_bavail * sfs.f_bsize; } else { - LOGE("Couldn't statfs " PKG_DIR_PREFIX ": %s\n", strerror(errno)); + LOGE("Couldn't statfs %s: %s\n", android_data_dir.path, strerror(errno)); return -1; } } @@ -137,9 +191,9 @@ int free_cache(int64_t free_size) LOGI("free_cache(%" PRId64 ") avail %" PRId64 "\n", free_size, avail); if (avail >= free_size) return 0; - d = opendir(PKG_DIR_PREFIX); + d = opendir(android_data_dir.path); if (d == NULL) { - LOGE("cannot open %s: %s\n", PKG_DIR_PREFIX, strerror(errno)); + LOGE("cannot open %s: %s\n", android_data_dir.path, strerror(errno)); return -1; } dfd = dirfd(d); @@ -172,43 +226,13 @@ int free_cache(int64_t free_size) return -1; } -/* used by move_dex, rm_dex, etc to ensure that the provided paths - * don't point anywhere other than at the APK_DIR_PREFIX - */ -static int is_valid_apk_path(const char *path) -{ - int len = strlen(APK_DIR_PREFIX); -int nosubdircheck = 0; - if (strncmp(path, APK_DIR_PREFIX, len)) { - len = strlen(PROTECTED_DIR_PREFIX); - if (strncmp(path, PROTECTED_DIR_PREFIX, len)) { - len = strlen(SDCARD_DIR_PREFIX); - if (strncmp(path, SDCARD_DIR_PREFIX, len)) { - LOGE("invalid apk path '%s' (bad prefix)\n", path); - return 0; - } else { - nosubdircheck = 1; - } - } - } - if ((nosubdircheck != 1) && strchr(path + len, '/')) { - LOGE("invalid apk path '%s' (subdir?)\n", path); - return 0; - } - if (path[len] == '.') { - LOGE("invalid apk path '%s' (trickery)\n", path); - return 0; - } - return 1; -} - int move_dex(const char *src, const char *dst) { char src_dex[PKG_PATH_MAX]; char dst_dex[PKG_PATH_MAX]; - if (!is_valid_apk_path(src)) return -1; - if (!is_valid_apk_path(dst)) return -1; + if (validate_apk_path(src)) return -1; + if (validate_apk_path(dst)) return -1; if (create_cache_path(src_dex, src)) return -1; if (create_cache_path(dst_dex, dst)) return -1; @@ -226,7 +250,7 @@ int rm_dex(const char *path) { char dex_path[PKG_PATH_MAX]; - if (!is_valid_apk_path(path)) return -1; + if (validate_apk_path(path)) return -1; if (create_cache_path(dex_path, path)) return -1; LOGV("unlink %s\n", dex_path); @@ -245,7 +269,7 @@ int protect(char *pkgname, gid_t gid) if (gid < AID_SYSTEM) return -1; - if (create_pkg_path(pkgpath, PROTECTED_DIR_PREFIX, pkgname, ".apk")) + if (create_pkg_path_in_dir(pkgpath, &android_app_private_dir, pkgname, ".apk")) return -1; if (stat(pkgpath, &s) < 0) return -1; @@ -264,8 +288,9 @@ int protect(char *pkgname, gid_t gid) } int get_size(const char *pkgname, const char *apkpath, - const char *fwdlock_apkpath, - int64_t *_codesize, int64_t *_datasize, int64_t *_cachesize) + const char *fwdlock_apkpath, const char *asecpath, + int64_t *_codesize, int64_t *_datasize, int64_t *_cachesize, + int64_t* _asecsize) { DIR *d; int dfd; @@ -276,12 +301,13 @@ int get_size(const char *pkgname, const char *apkpath, int64_t codesize = 0; int64_t datasize = 0; int64_t cachesize = 0; + int64_t asecsize = 0; /* count the source apk as code -- but only if it's not * on the /system partition and its not on the sdcard. */ - if (strncmp(apkpath, "/system", 7) != 0 && - strncmp(apkpath, SDCARD_DIR_PREFIX, 7) != 0) { + if (validate_system_app_path(apkpath) && + strncmp(apkpath, android_asec_dir.path, android_asec_dir.len) != 0) { if (stat(apkpath, &s) == 0) { codesize += stat_size(&s); } @@ -300,7 +326,15 @@ int get_size(const char *pkgname, const char *apkpath, } } - if (create_pkg_path(path, PKG_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX)) { + /* compute asec size if it is given + */ + if (asecpath != NULL && asecpath[0] != '!') { + if (stat(asecpath, &s) == 0) { + asecsize += stat_size(&s); + } + } + + if (create_pkg_path(path, pkgname, PKG_DIR_POSTFIX, 0)) { goto done; } @@ -310,10 +344,10 @@ int get_size(const char *pkgname, const char *apkpath, } dfd = dirfd(d); - /* most stuff in the pkgdir is data, except for the "cache" - * directory and below, which is cache, and the "lib" directory - * and below, which is code... - */ + /* most stuff in the pkgdir is data, except for the "cache" + * directory and below, which is cache, and the "lib" directory + * and below, which is code... + */ while ((de = readdir(d))) { const char *name = de->d_name; @@ -346,6 +380,7 @@ done: *_codesize = codesize; *_datasize = datasize; *_cachesize = cachesize; + *_asecsize = asecsize; return 0; } @@ -544,15 +579,15 @@ fail: } int create_move_path(char path[PKG_PATH_MAX], - const char* prefix, const char* pkgname, - const char* leaf) + const char* leaf, + uid_t persona) { - if ((strlen(prefix) + strlen(pkgname) + strlen(leaf) + 1) >= PKG_PATH_MAX) { + if ((android_data_dir.len + strlen(pkgname) + strlen(leaf) + 1) >= PKG_PATH_MAX) { return -1; } - sprintf(path, "%s%s/%s", prefix, pkgname, leaf); + sprintf(path, "%s%s%s/%s", android_data_dir.path, PRIMARY_USER_PREFIX, pkgname, leaf); return 0; } @@ -720,8 +755,8 @@ int movefiles() // Skip -- source package no longer exists. } else { LOGV("Move file: %s (from %s to %s)\n", buf+bufp, srcpkg, dstpkg); - if (!create_move_path(srcpath, PKG_DIR_PREFIX, srcpkg, buf+bufp) && - !create_move_path(dstpath, PKG_DIR_PREFIX, dstpkg, buf+bufp)) { + if (!create_move_path(srcpath, srcpkg, buf+bufp, 0) && + !create_move_path(dstpath, dstpkg, buf+bufp, 0)) { movefileordir(srcpath, dstpath, strlen(dstpath)-strlen(buf+bufp), dstuid, dstgid, &s); @@ -750,8 +785,7 @@ int movefiles() UPDATE_COMMANDS_DIR_PREFIX, name, div); } if (srcpkg[0] != 0) { - if (!create_pkg_path(srcpath, PKG_DIR_PREFIX, srcpkg, - PKG_DIR_POSTFIX)) { + if (!create_pkg_path(srcpath, srcpkg, PKG_DIR_POSTFIX, 0)) { if (lstat(srcpath, &s) < 0) { // Package no longer exists -- skip. srcpkg[0] = 0; @@ -762,8 +796,7 @@ int movefiles() div, UPDATE_COMMANDS_DIR_PREFIX, name); } if (srcpkg[0] != 0) { - if (!create_pkg_path(dstpath, PKG_DIR_PREFIX, dstpkg, - PKG_DIR_POSTFIX)) { + if (!create_pkg_path(dstpath, dstpkg, PKG_DIR_POSTFIX, 0)) { if (lstat(dstpath, &s) == 0) { dstuid = s.st_uid; dstgid = s.st_gid; diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c index d2b2f7f..feb6b92 100644 --- a/cmds/installd/installd.c +++ b/cmds/installd/installd.c @@ -21,7 +21,6 @@ #define TOKEN_MAX 8 /* max number of arguments in buffer */ #define REPLY_MAX 256 /* largest reply allowed */ - static int do_ping(char **arg, char reply[REPLY_MAX]) { return 0; @@ -50,7 +49,7 @@ static int do_rm_dex(char **arg, char reply[REPLY_MAX]) static int do_remove(char **arg, char reply[REPLY_MAX]) { - return uninstall(arg[0]); /* pkgname */ + return uninstall(arg[0], atoi(arg[1])); /* pkgname, userid */ } static int do_rename(char **arg, char reply[REPLY_MAX]) @@ -78,22 +77,34 @@ static int do_get_size(char **arg, char reply[REPLY_MAX]) int64_t codesize = 0; int64_t datasize = 0; int64_t cachesize = 0; + int64_t asecsize = 0; int res = 0; /* pkgdir, apkpath */ - res = get_size(arg[0], arg[1], arg[2], &codesize, &datasize, &cachesize); + res = get_size(arg[0], arg[1], arg[2], arg[3], &codesize, &datasize, &cachesize, &asecsize); /* * Each int64_t can take up 22 characters printed out. Make sure it * doesn't go over REPLY_MAX in the future. */ - snprintf(reply, REPLY_MAX, "%" PRId64 " %" PRId64 " %" PRId64, codesize, datasize, cachesize); + snprintf(reply, REPLY_MAX, "%" PRId64 " %" PRId64 " %" PRId64 " %" PRId64, + codesize, datasize, cachesize, asecsize); return res; } static int do_rm_user_data(char **arg, char reply[REPLY_MAX]) { - return delete_user_data(arg[0]); /* pkgname */ + return delete_user_data(arg[0], atoi(arg[1])); /* pkgname, userid */ +} + +static int do_mk_user_data(char **arg, char reply[REPLY_MAX]) +{ + return make_user_data(arg[0], atoi(arg[1]), atoi(arg[2])); /* pkgname, uid, userid */ +} + +static int do_rm_user(char **arg, char reply[REPLY_MAX]) +{ + return delete_persona(atoi(arg[0])); /* userid */ } static int do_movefiles(char **arg, char reply[REPLY_MAX]) @@ -123,16 +134,18 @@ struct cmdinfo cmds[] = { { "dexopt", 3, do_dexopt }, { "movedex", 2, do_move_dex }, { "rmdex", 1, do_rm_dex }, - { "remove", 1, do_remove }, + { "remove", 2, do_remove }, { "rename", 2, do_rename }, { "freecache", 1, do_free_cache }, { "rmcache", 1, do_rm_cache }, { "protect", 2, do_protect }, - { "getsize", 3, do_get_size }, - { "rmuserdata", 1, do_rm_user_data }, + { "getsize", 4, do_get_size }, + { "rmuserdata", 2, do_rm_user_data }, { "movefiles", 0, do_movefiles }, { "linklib", 2, do_linklib }, { "unlinklib", 1, do_unlinklib }, + { "mkuserdata", 3, do_mk_user_data }, + { "rmuser", 1, do_rm_user }, }; static int readx(int s, void *_buf, int count) @@ -235,12 +248,118 @@ done: return 0; } -int main(const int argc, const char *argv[]) { +/** + * Initialize all the global variables that are used elsewhere. Returns 0 upon + * success and -1 on error. + */ +void free_globals() { + size_t i; + + for (i = 0; i < android_system_dirs.count; i++) { + if (android_system_dirs.dirs[i].path != NULL) { + free(android_system_dirs.dirs[i].path); + } + } + + free(android_system_dirs.dirs); +} + +int initialize_globals() { + // Get the android data directory. + if (get_path_from_env(&android_data_dir, "ANDROID_DATA") < 0) { + return -1; + } + + // Get the android app directory. + if (copy_and_append(&android_app_dir, &android_data_dir, APP_SUBDIR) < 0) { + return -1; + } + + // Get the android protected app directory. + if (copy_and_append(&android_app_private_dir, &android_data_dir, PRIVATE_APP_SUBDIR) < 0) { + return -1; + } + + // Get the sd-card ASEC mount point. + if (get_path_from_env(&android_asec_dir, "ASEC_MOUNTPOINT") < 0) { + return -1; + } + + // Take note of the system and vendor directories. + android_system_dirs.count = 2; + + android_system_dirs.dirs = calloc(android_system_dirs.count, sizeof(dir_rec_t)); + if (android_system_dirs.dirs == NULL) { + LOGE("Couldn't allocate array for dirs; aborting\n"); + return -1; + } + + // system + if (get_path_from_env(&android_system_dirs.dirs[0], "ANDROID_ROOT") < 0) { + free_globals(); + return -1; + } + + // append "app/" to dirs[0] + char *system_app_path = build_string2(android_system_dirs.dirs[0].path, APP_SUBDIR); + android_system_dirs.dirs[0].path = system_app_path; + android_system_dirs.dirs[0].len = strlen(system_app_path); + + // vendor + // TODO replace this with an environment variable (doesn't exist yet) + android_system_dirs.dirs[1].path = "/vendor/app/"; + android_system_dirs.dirs[1].len = strlen(android_system_dirs.dirs[1].path); + + return 0; +} + +int initialize_directories() { + // /data/user + char *user_data_dir = build_string2(android_data_dir.path, SECONDARY_USER_PREFIX); + // /data/data + char *legacy_data_dir = build_string2(android_data_dir.path, PRIMARY_USER_PREFIX); + // /data/user/0 + char *primary_data_dir = build_string3(android_data_dir.path, SECONDARY_USER_PREFIX, + "0"); + int ret = -1; + if (user_data_dir != NULL && primary_data_dir != NULL && legacy_data_dir != NULL) { + ret = 0; + // Make the /data/user directory if necessary + if (access(user_data_dir, R_OK) < 0) { + if (mkdir(user_data_dir, 0755) < 0) { + return -1; + } + if (chown(user_data_dir, AID_SYSTEM, AID_SYSTEM) < 0) { + return -1; + } + } + // Make the /data/user/0 symlink to /data/data if necessary + if (access(primary_data_dir, R_OK) < 0) { + ret = symlink(legacy_data_dir, primary_data_dir); + } + free(user_data_dir); + free(legacy_data_dir); + free(primary_data_dir); + } + return ret; +} + +int main(const int argc, const char *argv[]) { char buf[BUFFER_MAX]; struct sockaddr addr; socklen_t alen; int lsocket, s, count; + if (initialize_globals() < 0) { + LOGE("Could not initialize globals; exiting.\n"); + exit(1); + } + + if (initialize_directories() < 0) { + LOGE("Could not create directories; exiting.\n"); + exit(1); + } + lsocket = android_get_control_socket(SOCKET_PATH); if (lsocket < 0) { LOGE("Failed to get socket from environment: %s\n", strerror(errno)); diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h index 77b58ec..c5872b8 100644 --- a/cmds/installd/installd.h +++ b/cmds/installd/installd.h @@ -49,37 +49,63 @@ /* elements combined with a valid package name to form paths */ -#define PKG_DIR_PREFIX "/data/data/" +#define PRIMARY_USER_PREFIX "data/" +#define SECONDARY_USER_PREFIX "user/" + #define PKG_DIR_POSTFIX "" -#define PKG_LIB_PREFIX "/data/data/" #define PKG_LIB_POSTFIX "/lib" -#define CACHE_DIR_PREFIX "/data/data/" #define CACHE_DIR_POSTFIX "/cache" -#define APK_DIR_PREFIX "/data/app/" +#define APP_SUBDIR "app/" // sub-directory under ANDROID_DATA /* other handy constants */ -#define PROTECTED_DIR_PREFIX "/data/app-private/" -#define SDCARD_DIR_PREFIX getenv("ASEC_MOUNTPOINT") +#define PRIVATE_APP_SUBDIR "app-private/" // sub-directory under ANDROID_DATA -#define DALVIK_CACHE_PREFIX "/data/dalvik-cache/" -#define DALVIK_CACHE_POSTFIX "/classes.dex" +#define DALVIK_CACHE_PREFIX "/data/dalvik-cache/" +#define DALVIK_CACHE_POSTFIX "/classes.dex" #define UPDATE_COMMANDS_DIR_PREFIX "/system/etc/updatecmds/" #define PKG_NAME_MAX 128 /* largest allowed package name */ #define PKG_PATH_MAX 256 /* max size of any path we use */ +/* data structures */ + +typedef struct { + char* path; + size_t len; +} dir_rec_t; + +typedef struct { + size_t count; + dir_rec_t* dirs; +} dir_rec_array_t; + +extern dir_rec_t android_app_dir; +extern dir_rec_t android_app_private_dir; +extern dir_rec_t android_data_dir; +extern dir_rec_t android_asec_dir; +extern dir_rec_array_t android_system_dirs; /* util.c */ +int create_pkg_path_in_dir(char path[PKG_PATH_MAX], + const dir_rec_t* dir, + const char* pkgname, + const char* postfix); + int create_pkg_path(char path[PKG_PATH_MAX], - const char *prefix, const char *pkgname, - const char *postfix); + const char *postfix, + uid_t persona); + +int create_persona_path(char path[PKG_PATH_MAX], + uid_t persona); + +int is_valid_package_name(const char* pkgname); int create_cache_path(char path[PKG_PATH_MAX], const char *src); @@ -89,18 +115,36 @@ int delete_dir_contents(const char *pathname, int delete_dir_contents_fd(int dfd, const char *name); +int validate_system_app_path(const char* path); + +int get_path_from_env(dir_rec_t* rec, const char* var); + +int get_path_from_string(dir_rec_t* rec, const char* path); + +int copy_and_append(dir_rec_t* dst, const dir_rec_t* src, const char* suffix); + +int validate_apk_path(const char *path); + +int append_and_increment(char** dst, const char* src, size_t* dst_size); + +char *build_string2(char *s1, char *s2); +char *build_string3(char *s1, char *s2, char *s3); + /* commands.c */ int install(const char *pkgname, uid_t uid, gid_t gid); -int uninstall(const char *pkgname); +int uninstall(const char *pkgname, uid_t persona); int renamepkg(const char *oldpkgname, const char *newpkgname); -int delete_user_data(const char *pkgname); +int delete_user_data(const char *pkgname, uid_t persona); +int make_user_data(const char *pkgname, uid_t uid, uid_t persona); +int delete_persona(uid_t persona); int delete_cache(const char *pkgname); int move_dex(const char *src, const char *dst); int rm_dex(const char *path); int protect(char *pkgname, gid_t gid); int get_size(const char *pkgname, const char *apkpath, const char *fwdlock_apkpath, - int64_t *codesize, int64_t *datasize, int64_t *cachesize); + const char *asecpath, int64_t *codesize, int64_t *datasize, int64_t *cachesize, + int64_t *asecsize); int free_cache(int64_t free_size); int dexopt(const char *apk_path, uid_t uid, int is_public); int movefiles(); diff --git a/cmds/installd/tests/Android.mk b/cmds/installd/tests/Android.mk new file mode 100644 index 0000000..315acdb --- /dev/null +++ b/cmds/installd/tests/Android.mk @@ -0,0 +1,38 @@ +# Build the unit tests for installd +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +# Build the unit tests. +test_src_files := \ + installd_utils_test.cpp + +shared_libraries := \ + libutils \ + libcutils \ + libstlport + +static_libraries := \ + libinstalld \ + libdiskusage \ + libgtest \ + libgtest_main + +c_includes := \ + frameworks/base/cmds/installd \ + bionic \ + bionic/libstdc++/include \ + external/gtest/include \ + external/stlport/stlport + +module_tags := eng tests + +$(foreach file,$(test_src_files), \ + $(eval include $(CLEAR_VARS)) \ + $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \ + $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \ + $(eval LOCAL_SRC_FILES := $(file)) \ + $(eval LOCAL_C_INCLUDES := $(c_includes)) \ + $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \ + $(eval LOCAL_MODULE_TAGS := $(module_tags)) \ + $(eval include $(BUILD_EXECUTABLE)) \ +) diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp new file mode 100644 index 0000000..1128fce --- /dev/null +++ b/cmds/installd/tests/installd_utils_test.cpp @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2011 The Android Open Source 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 <stdlib.h> +#include <string.h> + +#define LOG_TAG "utils_test" +#include <utils/Log.h> + +#include <gtest/gtest.h> + +extern "C" { +#include "installd.h" +} + +#define TEST_DATA_DIR "/data/" +#define TEST_APP_DIR "/data/app/" +#define TEST_APP_PRIVATE_DIR "/data/app-private/" +#define TEST_ASEC_DIR "/mnt/asec/" + +#define TEST_SYSTEM_DIR1 "/system/app/" +#define TEST_SYSTEM_DIR2 "/vendor/app/" + +namespace android { + +class UtilsTest : public testing::Test { +protected: + virtual void SetUp() { + android_app_dir.path = TEST_APP_DIR; + android_app_dir.len = strlen(TEST_APP_DIR); + + android_app_private_dir.path = TEST_APP_PRIVATE_DIR; + android_app_private_dir.len = strlen(TEST_APP_PRIVATE_DIR); + + android_data_dir.path = TEST_DATA_DIR; + android_data_dir.len = strlen(TEST_DATA_DIR); + + android_asec_dir.path = TEST_ASEC_DIR; + android_asec_dir.len = strlen(TEST_ASEC_DIR); + + android_system_dirs.count = 2; + + android_system_dirs.dirs = (dir_rec_t*) calloc(android_system_dirs.count, sizeof(dir_rec_t)); + android_system_dirs.dirs[0].path = TEST_SYSTEM_DIR1; + android_system_dirs.dirs[0].len = strlen(TEST_SYSTEM_DIR1); + + android_system_dirs.dirs[1].path = TEST_SYSTEM_DIR2; + android_system_dirs.dirs[1].len = strlen(TEST_SYSTEM_DIR2); + } + + virtual void TearDown() { + free(android_system_dirs.dirs); + } +}; + +TEST_F(UtilsTest, IsValidApkPath_BadPrefix) { + // Bad prefixes directories + const char *badprefix1 = "/etc/passwd"; + EXPECT_EQ(-1, validate_apk_path(badprefix1)) + << badprefix1 << " should be allowed as a valid path"; + + const char *badprefix2 = "../.." TEST_APP_DIR "../../../blah"; + EXPECT_EQ(-1, validate_apk_path(badprefix2)) + << badprefix2 << " should be allowed as a valid path"; + + const char *badprefix3 = "init.rc"; + EXPECT_EQ(-1, validate_apk_path(badprefix3)) + << badprefix3 << " should be allowed as a valid path"; + + const char *badprefix4 = "/init.rc"; + EXPECT_EQ(-1, validate_apk_path(badprefix4)) + << badprefix4 << " should be allowed as a valid path"; +} + +TEST_F(UtilsTest, IsValidApkPath_Internal) { + // Internal directories + const char *internal1 = TEST_APP_DIR "example.apk"; + EXPECT_EQ(0, validate_apk_path(internal1)) + << internal1 << " should be allowed as a valid path"; + + const char *badint1 = TEST_APP_DIR "../example.apk"; + EXPECT_EQ(-1, validate_apk_path(badint1)) + << badint1 << " should be rejected as a invalid path"; + + const char *badint2 = TEST_APP_DIR "/../example.apk"; + EXPECT_EQ(-1, validate_apk_path(badint2)) + << badint2 << " should be rejected as a invalid path"; + + const char *badint3 = TEST_APP_DIR "example.com/pkg.apk"; + EXPECT_EQ(-1, validate_apk_path(badint3)) + << badint3 << " should be rejected as a invalid path"; +} + +TEST_F(UtilsTest, IsValidApkPath_Private) { + // Internal directories + const char *private1 = TEST_APP_PRIVATE_DIR "example.apk"; + EXPECT_EQ(0, validate_apk_path(private1)) + << private1 << " should be allowed as a valid path"; + + const char *badpriv1 = TEST_APP_PRIVATE_DIR "../example.apk"; + EXPECT_EQ(-1, validate_apk_path(badpriv1)) + << badpriv1 << " should be rejected as a invalid path"; + + const char *badpriv2 = TEST_APP_PRIVATE_DIR "/../example.apk"; + EXPECT_EQ(-1, validate_apk_path(badpriv2)) + << badpriv2 << " should be rejected as a invalid path"; + + const char *badpriv3 = TEST_APP_PRIVATE_DIR "example.com/pkg.apk"; + EXPECT_EQ(-1, validate_apk_path(badpriv3)) + << badpriv3 << " should be rejected as a invalid path"; +} + + +TEST_F(UtilsTest, IsValidApkPath_AsecGood1) { + const char *asec1 = TEST_ASEC_DIR "example.apk"; + EXPECT_EQ(0, validate_apk_path(asec1)) + << asec1 << " should be allowed as a valid path"; +} + +TEST_F(UtilsTest, IsValidApkPath_AsecGood2) { + const char *asec2 = TEST_ASEC_DIR "com.example.asec/pkg.apk"; + EXPECT_EQ(0, validate_apk_path(asec2)) + << asec2 << " should be allowed as a valid path"; +} + +TEST_F(UtilsTest, IsValidApkPath_EscapeFail) { + const char *badasec1 = TEST_ASEC_DIR "../example.apk"; + EXPECT_EQ(-1, validate_apk_path(badasec1)) + << badasec1 << " should be rejected as a invalid path"; +} + +TEST_F(UtilsTest, IsValidApkPath_DoubleSlashFail) { + const char *badasec2 = TEST_ASEC_DIR "com.example.asec//pkg.apk"; + EXPECT_EQ(-1, validate_apk_path(badasec2)) + << badasec2 << " should be rejected as a invalid path"; +} + +TEST_F(UtilsTest, IsValidApkPath_SubdirEscapeFail) { + const char *badasec3 = TEST_ASEC_DIR "com.example.asec/../../../pkg.apk"; + EXPECT_EQ(-1, validate_apk_path(badasec3)) + << badasec3 << " should be rejected as a invalid path"; +} + +TEST_F(UtilsTest, IsValidApkPath_SlashEscapeFail) { + const char *badasec4 = TEST_ASEC_DIR "/../example.apk"; + EXPECT_EQ(-1, validate_apk_path(badasec4)) + << badasec4 << " should be rejected as a invalid path"; +} + +TEST_F(UtilsTest, IsValidApkPath_CrazyDirFail) { + const char *badasec5 = TEST_ASEC_DIR ".//../.."; + EXPECT_EQ(-1, validate_apk_path(badasec5)) + << badasec5 << " should be rejected as a invalid path"; +} + +TEST_F(UtilsTest, IsValidApkPath_SubdirEscapeSingleFail) { + const char *badasec6 = TEST_ASEC_DIR "com.example.asec/../pkg.apk"; + EXPECT_EQ(-1, validate_apk_path(badasec6)) + << badasec6 << " should be rejected as a invalid path"; +} + +TEST_F(UtilsTest, IsValidApkPath_TwoSubdirFail) { + const char *badasec7 = TEST_ASEC_DIR "com.example.asec/subdir1/pkg.apk"; + EXPECT_EQ(-1, validate_apk_path(badasec7)) + << badasec7 << " should be rejected as a invalid path"; +} + +TEST_F(UtilsTest, CheckSystemApp_Dir1) { + const char *sysapp1 = TEST_SYSTEM_DIR1 "Voice.apk"; + EXPECT_EQ(0, validate_system_app_path(sysapp1)) + << sysapp1 << " should be allowed as a system path"; +} + +TEST_F(UtilsTest, CheckSystemApp_Dir2) { + const char *sysapp2 = TEST_SYSTEM_DIR2 "com.example.myapp.apk"; + EXPECT_EQ(0, validate_system_app_path(sysapp2)) + << sysapp2 << " should be allowed as a system path"; +} + +TEST_F(UtilsTest, CheckSystemApp_EscapeFail) { + const char *badapp1 = TEST_SYSTEM_DIR1 "../com.example.apk"; + EXPECT_EQ(-1, validate_system_app_path(badapp1)) + << badapp1 << " should be rejected not a system path"; +} + +TEST_F(UtilsTest, CheckSystemApp_DoubleEscapeFail) { + const char *badapp2 = TEST_SYSTEM_DIR2 "/../../com.example.apk"; + EXPECT_EQ(-1, validate_system_app_path(badapp2)) + << badapp2 << " should be rejected not a system path"; +} + +TEST_F(UtilsTest, CheckSystemApp_BadPathEscapeFail) { + const char *badapp3 = TEST_APP_DIR "/../../com.example.apk"; + EXPECT_EQ(-1, validate_system_app_path(badapp3)) + << badapp3 << " should be rejected not a system path"; +} + +TEST_F(UtilsTest, GetPathFromString_NullPathFail) { + dir_rec_t test1; + EXPECT_EQ(-1, get_path_from_string(&test1, NULL)) + << "Should not allow NULL as a path."; +} + +TEST_F(UtilsTest, GetPathFromString_EmptyPathFail) { + dir_rec_t test1; + EXPECT_EQ(-1, get_path_from_string(&test1, "")) + << "Should not allow empty paths."; +} + +TEST_F(UtilsTest, GetPathFromString_RelativePathFail) { + dir_rec_t test1; + EXPECT_EQ(-1, get_path_from_string(&test1, "mnt/asec")) + << "Should not allow relative paths."; +} + +TEST_F(UtilsTest, GetPathFromString_NonCanonical) { + dir_rec_t test1; + + EXPECT_EQ(0, get_path_from_string(&test1, "/mnt/asec")) + << "Should be able to canonicalize directory /mnt/asec"; + EXPECT_STREQ("/mnt/asec/", test1.path) + << "/mnt/asec should be canonicalized to /mnt/asec/"; + EXPECT_EQ(10, (ssize_t) test1.len) + << "path len should be equal to the length of /mnt/asec/ (10)"; + free(test1.path); +} + +TEST_F(UtilsTest, GetPathFromString_CanonicalPath) { + dir_rec_t test3; + EXPECT_EQ(0, get_path_from_string(&test3, "/data/app/")) + << "Should be able to canonicalize directory /data/app/"; + EXPECT_STREQ("/data/app/", test3.path) + << "/data/app/ should be canonicalized to /data/app/"; + EXPECT_EQ(10, (ssize_t) test3.len) + << "path len should be equal to the length of /data/app/ (10)"; + free(test3.path); +} + +TEST_F(UtilsTest, CreatePkgPath_LongPkgNameSuccess) { + char path[PKG_PATH_MAX]; + + // Create long packagename of "aaaaa..." + size_t pkgnameSize = PKG_NAME_MAX; + char pkgname[pkgnameSize + 1]; + memset(pkgname, 'a', pkgnameSize); + pkgname[pkgnameSize] = '\0'; + + EXPECT_EQ(0, create_pkg_path(path, pkgname, "", 0)) + << "Should successfully be able to create package name."; + + const char *prefix = TEST_DATA_DIR PRIMARY_USER_PREFIX; + size_t offset = strlen(prefix); + EXPECT_STREQ(pkgname, path + offset) + << "Package path should be a really long string of a's"; +} + +TEST_F(UtilsTest, CreatePkgPath_LongPkgNameFail) { + char path[PKG_PATH_MAX]; + + // Create long packagename of "aaaaa..." + size_t pkgnameSize = PKG_NAME_MAX + 1; + char pkgname[pkgnameSize + 1]; + memset(pkgname, 'a', pkgnameSize); + pkgname[pkgnameSize] = '\0'; + + EXPECT_EQ(-1, create_pkg_path(path, pkgname, "", 0)) + << "Should return error because package name is too long."; +} + +TEST_F(UtilsTest, CreatePkgPath_LongPostfixFail) { + char path[PKG_PATH_MAX]; + + // Create long packagename of "aaaaa..." + size_t postfixSize = PKG_PATH_MAX; + char postfix[postfixSize + 1]; + memset(postfix, 'a', postfixSize); + postfix[postfixSize] = '\0'; + + EXPECT_EQ(-1, create_pkg_path(path, "com.example.package", postfix, 0)) + << "Should return error because postfix is too long."; +} + +TEST_F(UtilsTest, CreatePkgPath_PrimaryUser) { + char path[PKG_PATH_MAX]; + + EXPECT_EQ(0, create_pkg_path(path, "com.example.package", "", 0)) + << "Should return error because postfix is too long."; + + EXPECT_STREQ(TEST_DATA_DIR PRIMARY_USER_PREFIX "com.example.package", path) + << "Package path should be in /data/data/"; +} + +TEST_F(UtilsTest, CreatePkgPath_SecondaryUser) { + char path[PKG_PATH_MAX]; + + EXPECT_EQ(0, create_pkg_path(path, "com.example.package", "", 1)) + << "Should successfully create package path."; + + EXPECT_STREQ(TEST_DATA_DIR SECONDARY_USER_PREFIX "1/com.example.package", path) + << "Package path should be in /data/user/"; +} + +TEST_F(UtilsTest, CreatePkgPathInDir_ProtectedDir) { + char path[PKG_PATH_MAX]; + + dir_rec_t dir; + dir.path = "/data/app-private/"; + dir.len = strlen(dir.path); + + EXPECT_EQ(0, create_pkg_path_in_dir(path, &dir, "com.example.package", ".apk")) + << "Should successfully create package path."; + + EXPECT_STREQ("/data/app-private/com.example.package.apk", path) + << "Package path should be in /data/app-private/"; +} + +TEST_F(UtilsTest, CopyAndAppend_Normal) { + //int copy_and_append(dir_rec_t* dst, dir_rec_t* src, char* suffix) + dir_rec_t dst; + dir_rec_t src; + + src.path = "/data/"; + src.len = strlen(src.path); + + EXPECT_EQ(0, copy_and_append(&dst, &src, "app/")) + << "Should return error because postfix is too long."; + + EXPECT_STREQ("/data/app/", dst.path) + << "Appended path should be correct"; + + EXPECT_EQ(10, (ssize_t) dst.len) + << "Appended path should be length of '/data/app/' (10)"; +} + +TEST_F(UtilsTest, AppendAndIncrement_Normal) { + size_t dst_size = 10; + char dst[dst_size]; + char *dstp = dst; + const char* src = "FOO"; + + EXPECT_EQ(0, append_and_increment(&dstp, src, &dst_size)) + << "String should append successfully"; + + EXPECT_STREQ("FOO", dst) + << "String should append correctly"; + + EXPECT_EQ(0, append_and_increment(&dstp, src, &dst_size)) + << "String should append successfully again"; + + EXPECT_STREQ("FOOFOO", dst) + << "String should append correctly again"; +} + +TEST_F(UtilsTest, AppendAndIncrement_TooBig) { + size_t dst_size = 5; + char dst[dst_size]; + char *dstp = dst; + const char* src = "FOO"; + + EXPECT_EQ(0, append_and_increment(&dstp, src, &dst_size)) + << "String should append successfully"; + + EXPECT_STREQ("FOO", dst) + << "String should append correctly"; + + EXPECT_EQ(-1, append_and_increment(&dstp, src, &dst_size)) + << "String should fail because it's too large to fit"; +} + +} diff --git a/cmds/installd/utils.c b/cmds/installd/utils.c index a5e4b5a..3099b83 100644 --- a/cmds/installd/utils.c +++ b/cmds/installd/utils.c @@ -16,24 +16,133 @@ #include "installd.h" +int create_pkg_path_in_dir(char path[PKG_PATH_MAX], + const dir_rec_t* dir, + const char* pkgname, + const char* postfix) +{ + const size_t postfix_len = strlen(postfix); + + const size_t pkgname_len = strlen(pkgname); + if (pkgname_len > PKG_NAME_MAX) { + return -1; + } + + if (is_valid_package_name(pkgname) < 0) { + return -1; + } + + if ((pkgname_len + dir->len + postfix_len) >= PKG_PATH_MAX) { + return -1; + } + + char *dst = path; + size_t dst_size = PKG_PATH_MAX; + + if (append_and_increment(&dst, dir->path, &dst_size) < 0 + || append_and_increment(&dst, pkgname, &dst_size) < 0 + || append_and_increment(&dst, postfix, &dst_size) < 0) { + LOGE("Error building APK path"); + return -1; + } + + return 0; +} + +/** + * Create the package path name for a given package name with a postfix for + * a certain persona. Returns 0 on success, and -1 on failure. + */ int create_pkg_path(char path[PKG_PATH_MAX], - const char *prefix, const char *pkgname, - const char *postfix) + const char *postfix, + uid_t persona) { - int len; - const char *x; + size_t uid_len; + char* persona_prefix; + if (persona == 0) { + persona_prefix = PRIMARY_USER_PREFIX; + uid_len = 0; + } else { + persona_prefix = SECONDARY_USER_PREFIX; + uid_len = snprintf(NULL, 0, "%d", persona); + } + + const size_t prefix_len = android_data_dir.len + strlen(persona_prefix) + uid_len + 1 /*slash*/; + char prefix[prefix_len + 1]; + + char *dst = prefix; + size_t dst_size = sizeof(prefix); - len = strlen(pkgname); - if (len > PKG_NAME_MAX) { + if (append_and_increment(&dst, android_data_dir.path, &dst_size) < 0 + || append_and_increment(&dst, persona_prefix, &dst_size) < 0) { + LOGE("Error building prefix for APK path"); return -1; } - if ((len + strlen(prefix) + strlen(postfix)) >= PKG_PATH_MAX) { + + if (persona != 0) { + int ret = snprintf(dst, dst_size, "%d/", persona); + if (ret < 0 || (size_t) ret != uid_len + 1) { + LOGW("Error appending UID to APK path"); + return -1; + } + } + + dir_rec_t dir; + dir.path = prefix; + dir.len = prefix_len; + + return create_pkg_path_in_dir(path, &dir, pkgname, postfix); +} + +/** + * Create the path name for user data for a certain persona. + * Returns 0 on success, and -1 on failure. + */ +int create_persona_path(char path[PKG_PATH_MAX], + uid_t persona) +{ + size_t uid_len; + char* persona_prefix; + if (persona == 0) { + persona_prefix = PRIMARY_USER_PREFIX; + uid_len = 0; + } else { + persona_prefix = SECONDARY_USER_PREFIX; + uid_len = snprintf(NULL, 0, "%d", persona); + } + + char *dst = path; + size_t dst_size = PKG_PATH_MAX; + + if (append_and_increment(&dst, android_data_dir.path, &dst_size) < 0 + || append_and_increment(&dst, persona_prefix, &dst_size) < 0) { + LOGE("Error building prefix for user path"); return -1; } - x = pkgname; + if (persona != 0) { + if (dst_size < uid_len + 1) { + LOGE("Error building user path"); + return -1; + } + int ret = snprintf(dst, dst_size, "%d", persona); + if (ret < 0 || (size_t) ret != uid_len) { + LOGE("Error appending persona id to path"); + return -1; + } + } + return 0; +} + +/** + * Checks whether the package name is valid. Returns -1 on error and + * 0 on success. + */ +int is_valid_package_name(const char* pkgname) { + const char *x = pkgname; int alpha = -1; + while (*x) { if (isalnum(*x) || (*x == '_')) { /* alphanumeric or underscore are fine */ @@ -47,13 +156,15 @@ int create_pkg_path(char path[PKG_PATH_MAX], /* Suffix -X is fine to let versioning of packages. But whatever follows should be alphanumeric.*/ alpha = 1; - }else { + } else { /* anything not A-Z, a-z, 0-9, _, or . is invalid */ LOGE("invalid package name '%s'\n", pkgname); return -1; } + x++; } + if (alpha == 1) { // Skip current character x++; @@ -66,7 +177,6 @@ int create_pkg_path(char path[PKG_PATH_MAX], } } - sprintf(path, "%s%s%s", prefix, pkgname, postfix); return 0; } @@ -171,3 +281,202 @@ int delete_dir_contents_fd(int dfd, const char *name) closedir(d); return res; } + +/** + * Checks whether a path points to a system app (.apk file). Returns 0 + * if it is a system app or -1 if it is not. + */ +int validate_system_app_path(const char* path) { + size_t i; + + for (i = 0; i < android_system_dirs.count; i++) { + const size_t dir_len = android_system_dirs.dirs[i].len; + if (!strncmp(path, android_system_dirs.dirs[i].path, dir_len)) { + if (path[dir_len] == '.' || strchr(path + dir_len, '/') != NULL) { + LOGE("invalid system apk path '%s' (trickery)\n", path); + return -1; + } + return 0; + } + } + + return -1; +} + +/** + * Get the contents of a environment variable that contains a path. Caller + * owns the string that is inserted into the directory record. Returns + * 0 on success and -1 on error. + */ +int get_path_from_env(dir_rec_t* rec, const char* var) { + const char* path = getenv(var); + int ret = get_path_from_string(rec, path); + if (ret < 0) { + LOGW("Problem finding value for environment variable %s\n", var); + } + return ret; +} + +/** + * Puts the string into the record as a directory. Appends '/' to the end + * of all paths. Caller owns the string that is inserted into the directory + * record. A null value will result in an error. + * + * Returns 0 on success and -1 on error. + */ +int get_path_from_string(dir_rec_t* rec, const char* path) { + if (path == NULL) { + return -1; + } else { + const size_t path_len = strlen(path); + if (path_len <= 0) { + return -1; + } + + // Make sure path is absolute. + if (path[0] != '/') { + return -1; + } + + if (path[path_len - 1] == '/') { + // Path ends with a forward slash. Make our own copy. + + rec->path = strdup(path); + if (rec->path == NULL) { + return -1; + } + + rec->len = path_len; + } else { + // Path does not end with a slash. Generate a new string. + char *dst; + + // Add space for slash and terminating null. + size_t dst_size = path_len + 2; + + rec->path = malloc(dst_size); + if (rec->path == NULL) { + return -1; + } + + dst = rec->path; + + if (append_and_increment(&dst, path, &dst_size) < 0 + || append_and_increment(&dst, "/", &dst_size)) { + LOGE("Error canonicalizing path"); + return -1; + } + + rec->len = dst - rec->path; + } + } + return 0; +} + +int copy_and_append(dir_rec_t* dst, const dir_rec_t* src, const char* suffix) { + dst->len = src->len + strlen(suffix); + const size_t dstSize = dst->len + 1; + dst->path = (char*) malloc(dstSize); + + if (dst->path == NULL + || snprintf(dst->path, dstSize, "%s%s", src->path, suffix) + != (ssize_t) dst->len) { + LOGE("Could not allocate memory to hold appended path; aborting\n"); + return -1; + } + + return 0; +} + +/** + * Check whether path points to a valid path for an APK file. An ASEC + * directory is allowed to have one level of subdirectory names. Returns -1 + * when an invalid path is encountered and 0 when a valid path is encountered. + */ +int validate_apk_path(const char *path) +{ + int allowsubdir = 0; + char *subdir = NULL; + size_t dir_len; + size_t path_len; + + if (!strncmp(path, android_app_dir.path, android_app_dir.len)) { + dir_len = android_app_dir.len; + } else if (!strncmp(path, android_app_private_dir.path, android_app_private_dir.len)) { + dir_len = android_app_private_dir.len; + } else if (!strncmp(path, android_asec_dir.path, android_asec_dir.len)) { + dir_len = android_asec_dir.len; + allowsubdir = 1; + } else { + LOGE("invalid apk path '%s' (bad prefix)\n", path); + return -1; + } + + path_len = strlen(path); + + /* + * Only allow the path to have a subdirectory if it's been marked as being allowed. + */ + if ((subdir = strchr(path + dir_len, '/')) != NULL) { + ++subdir; + if (!allowsubdir + || (path_len > (size_t) (subdir - path) && (strchr(subdir, '/') != NULL))) { + LOGE("invalid apk path '%s' (subdir?)\n", path); + return -1; + } + } + + /* + * Directories can't have a period directly after the directory markers + * to prevent ".." + */ + if (path[dir_len] == '.' + || (subdir != NULL && ((*subdir == '.') || (strchr(subdir, '/') != NULL)))) { + LOGE("invalid apk path '%s' (trickery)\n", path); + return -1; + } + + return 0; +} + +int append_and_increment(char** dst, const char* src, size_t* dst_size) { + ssize_t ret = strlcpy(*dst, src, *dst_size); + if (ret < 0 || (size_t) ret >= *dst_size) { + return -1; + } + *dst += ret; + *dst_size -= ret; + return 0; +} + +char *build_string2(char *s1, char *s2) { + if (s1 == NULL || s2 == NULL) return NULL; + + int len_s1 = strlen(s1); + int len_s2 = strlen(s2); + int len = len_s1 + len_s2 + 1; + char *result = malloc(len); + if (result == NULL) return NULL; + + strcpy(result, s1); + strcpy(result + len_s1, s2); + + return result; +} + +char *build_string3(char *s1, char *s2, char *s3) { + if (s1 == NULL || s2 == NULL || s3 == NULL) return NULL; + + int len_s1 = strlen(s1); + int len_s2 = strlen(s2); + int len_s3 = strlen(s3); + int len = len_s1 + len_s2 + len_s3 + 1; + char *result = malloc(len); + if (result == NULL) return NULL; + + strcpy(result, s1); + strcpy(result + len_s1, s2); + strcpy(result + len_s1 + len_s2, s3); + + return result; +} diff --git a/cmds/ip-up-vpn/Android.mk b/cmds/ip-up-vpn/Android.mk new file mode 100644 index 0000000..de81889 --- /dev/null +++ b/cmds/ip-up-vpn/Android.mk @@ -0,0 +1,26 @@ +# +# Copyright (C) 2011 The Android Open Source 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. +# + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := ip-up-vpn.c +LOCAL_SHARED_LIBRARIES := libcutils +LOCAL_MODULE := ip-up-vpn +LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/ppp +LOCAL_MODULE_TAGS := optional + +include $(BUILD_EXECUTABLE) diff --git a/cmds/ip-up-vpn/ip-up-vpn.c b/cmds/ip-up-vpn/ip-up-vpn.c new file mode 100644 index 0000000..e9ee95d --- /dev/null +++ b/cmds/ip-up-vpn/ip-up-vpn.c @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2011 The Android Open Source 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <linux/if.h> +#include <linux/route.h> + +#define LOG_TAG "ip-up-vpn" +#include <cutils/log.h> + +#define DIR "/data/misc/vpn/" + +static const char *env(const char *name) { + const char *value = getenv(name); + return value ? value : ""; +} + +static int set_address(struct sockaddr *sa, const char *address) { + sa->sa_family = AF_INET; + return inet_pton(AF_INET, address, &((struct sockaddr_in *)sa)->sin_addr); +} + +/* + * The primary goal is to create a file with VPN parameters. Currently they + * are interface, addresses, routes, DNS servers, and search domains. Each + * parameter occupies one line in the file, and it can be an empty string or + * space-separated values. The order and the format must be consistent with + * com.android.server.connectivity.Vpn. Here is an example. + * + * ppp0 + * 192.168.1.100/24 + * 0.0.0.0/0 + * 192.168.1.1 192.168.1.2 + * example.org + * + * The secondary goal is to unify the outcome of VPN. The current baseline + * is to have an interface configured with the given address and netmask + * and maybe add a host route to protect the tunnel. PPP-based VPN already + * does this, but others might not. Routes, DNS servers, and search domains + * are handled by the framework since they can be overridden by the users. + */ +int main(int argc, char **argv) +{ + FILE *state = fopen(DIR ".tmp", "wb"); + if (!state) { + LOGE("Cannot create state: %s", strerror(errno)); + return 1; + } + + if (argc >= 6) { + /* Invoked by pppd. */ + fprintf(state, "%s\n", argv[1]); + fprintf(state, "%s/32\n", argv[4]); + fprintf(state, "0.0.0.0/0\n"); + fprintf(state, "%s %s\n", env("DNS1"), env("DNS2")); + fprintf(state, "\n"); + } else if (argc == 2) { + /* Invoked by racoon. */ + const char *interface = env("INTERFACE"); + const char *address = env("INTERNAL_ADDR4"); + const char *routes = env("SPLIT_INCLUDE_CIDR"); + + int s = socket(AF_INET, SOCK_DGRAM, 0); + struct rtentry rt; + struct ifreq ifr; + + memset(&rt, 0, sizeof(rt)); + memset(&ifr, 0, sizeof(ifr)); + + /* Remove the old host route. There could be more than one. */ + rt.rt_flags |= RTF_UP | RTF_HOST; + if (set_address(&rt.rt_dst, env("REMOTE_ADDR"))) { + while (!ioctl(s, SIOCDELRT, &rt)); + } + if (errno != ESRCH) { + LOGE("Cannot remove host route: %s", strerror(errno)); + return 1; + } + + /* Create a new host route. */ + rt.rt_flags |= RTF_GATEWAY; + if (!set_address(&rt.rt_gateway, argv[1]) || + (ioctl(s, SIOCADDRT, &rt) && errno != EEXIST)) { + LOGE("Cannot create host route: %s", strerror(errno)); + return 1; + } + + /* Bring up the interface. */ + ifr.ifr_flags = IFF_UP; + strncpy(ifr.ifr_name, interface, IFNAMSIZ); + if (ioctl(s, SIOCSIFFLAGS, &ifr)) { + LOGE("Cannot bring up %s: %s", interface, strerror(errno)); + return 1; + } + + /* Set the address. */ + if (!set_address(&ifr.ifr_addr, address) || + ioctl(s, SIOCSIFADDR, &ifr)) { + LOGE("Cannot set address: %s", strerror(errno)); + return 1; + } + + /* Set the netmask. */ + if (!set_address(&ifr.ifr_netmask, env("INTERNAL_NETMASK4")) || + ioctl(s, SIOCSIFNETMASK, &ifr)) { + LOGE("Cannot set netmask: %s", strerror(errno)); + return 1; + } + + /* TODO: Send few packets to trigger phase 2? */ + + fprintf(state, "%s\n", interface); + fprintf(state, "%s/%s\n", address, env("INTERNAL_CIDR4")); + fprintf(state, "%s\n", routes[0] ? routes : "0.0.0.0/0"); + fprintf(state, "%s\n", env("INTERNAL_DNS4_LIST")); + fprintf(state, "%s\n", env("DEFAULT_DOMAIN")); + } else { + LOGE("Cannot parse parameters"); + return 1; + } + + fclose(state); + if (chmod(DIR ".tmp", 0444) || rename(DIR ".tmp", DIR "state")) { + LOGE("Cannot write state: %s", strerror(errno)); + return 1; + } + return 0; +} diff --git a/cmds/keystore/Android.mk b/cmds/keystore/Android.mk index 15a199f..5a9b979 100644 --- a/cmds/keystore/Android.mk +++ b/cmds/keystore/Android.mk @@ -14,23 +14,19 @@ # limitations under the License. # -ifneq ($(TARGET_SIMULATOR),true) - LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES := keystore.c +LOCAL_SRC_FILES := keystore.cpp LOCAL_C_INCLUDES := external/openssl/include LOCAL_SHARED_LIBRARIES := libcutils libcrypto LOCAL_MODULE:= keystore include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) -LOCAL_SRC_FILES := keystore_cli.c +LOCAL_SRC_FILES := keystore_cli.cpp LOCAL_C_INCLUDES := external/openssl/include LOCAL_SHARED_LIBRARIES := libcutils libcrypto LOCAL_MODULE:= keystore_cli LOCAL_MODULE_TAGS := debug include $(BUILD_EXECUTABLE) - -endif diff --git a/cmds/keystore/keystore.c b/cmds/keystore/keystore.c deleted file mode 100644 index afa64f8..0000000 --- a/cmds/keystore/keystore.c +++ /dev/null @@ -1,589 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source 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 <stdio.h> -#include <stdint.h> -#include <string.h> -#include <unistd.h> -#include <signal.h> -#include <errno.h> -#include <dirent.h> -#include <fcntl.h> -#include <limits.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <arpa/inet.h> - -#include <openssl/aes.h> -#include <openssl/evp.h> -#include <openssl/md5.h> - -#define LOG_TAG "keystore" -#include <cutils/log.h> -#include <cutils/sockets.h> -#include <private/android_filesystem_config.h> - -#include "keystore.h" - -/* KeyStore is a secured storage for key-value pairs. In this implementation, - * each file stores one key-value pair. Keys are encoded in file names, and - * values are encrypted with checksums. The encryption key is protected by a - * user-defined password. To keep things simple, buffers are always larger than - * the maximum space we needed, so boundary checks on buffers are omitted. */ - -#define KEY_SIZE ((NAME_MAX - 15) / 2) -#define VALUE_SIZE 32768 -#define PASSWORD_SIZE VALUE_SIZE - -/* Here is the encoding of keys. This is necessary in order to allow arbitrary - * characters in keys. Characters in [0-~] are not encoded. Others are encoded - * into two bytes. The first byte is one of [+-.] which represents the first - * two bits of the character. The second byte encodes the rest of the bits into - * [0-o]. Therefore in the worst case the length of a key gets doubled. Note - * that Base64 cannot be used here due to the need of prefix match on keys. */ - -static int encode_key(char *out, uint8_t *in, int length) -{ - int i; - for (i = length; i > 0; --i, ++in, ++out) { - if (*in >= '0' && *in <= '~') { - *out = *in; - } else { - *out = '+' + (*in >> 6); - *++out = '0' + (*in & 0x3F); - ++length; - } - } - *out = 0; - return length; -} - -static int decode_key(uint8_t *out, char *in, int length) -{ - int i; - for (i = 0; i < length; ++i, ++in, ++out) { - if (*in >= '0' && *in <= '~') { - *out = *in; - } else { - *out = (*in - '+') << 6; - *out |= (*++in - '0') & 0x3F; - --length; - } - } - *out = 0; - return length; -} - -/* Here is the protocol used in both requests and responses: - * code [length_1 message_1 ... length_n message_n] end-of-file - * where code is one byte long and lengths are unsigned 16-bit integers in - * network order. Thus the maximum length of a message is 65535 bytes. */ - -static int the_socket = -1; - -static int recv_code(int8_t *code) -{ - return recv(the_socket, code, 1, 0) == 1; -} - -static int recv_message(uint8_t *message, int length) -{ - uint8_t bytes[2]; - if (recv(the_socket, &bytes[0], 1, 0) != 1 || - recv(the_socket, &bytes[1], 1, 0) != 1) { - return -1; - } else { - int offset = bytes[0] << 8 | bytes[1]; - if (length < offset) { - return -1; - } - length = offset; - offset = 0; - while (offset < length) { - int n = recv(the_socket, &message[offset], length - offset, 0); - if (n <= 0) { - return -1; - } - offset += n; - } - } - return length; -} - -static int recv_end_of_file() -{ - uint8_t byte; - return recv(the_socket, &byte, 1, 0) == 0; -} - -static void send_code(int8_t code) -{ - send(the_socket, &code, 1, 0); -} - -static void send_message(uint8_t *message, int length) -{ - uint16_t bytes = htons(length); - send(the_socket, &bytes, 2, 0); - send(the_socket, message, length, 0); -} - -/* Here is the file format. There are two parts in blob.value, the secret and - * the description. The secret is stored in ciphertext, and its original size - * can be found in blob.length. The description is stored after the secret in - * plaintext, and its size is specified in blob.info. The total size of the two - * parts must be no more than VALUE_SIZE bytes. The first three bytes of the - * file are reserved for future use and are always set to zero. Fields other - * than blob.info, blob.length, and blob.value are modified by encrypt_blob() - * and decrypt_blob(). Thus they should not be accessed from outside. */ - -static int the_entropy = -1; - -static struct __attribute__((packed)) { - uint8_t reserved[3]; - uint8_t info; - uint8_t vector[AES_BLOCK_SIZE]; - uint8_t encrypted[0]; - uint8_t digest[MD5_DIGEST_LENGTH]; - uint8_t digested[0]; - int32_t length; - uint8_t value[VALUE_SIZE + AES_BLOCK_SIZE]; -} blob; - -static int8_t encrypt_blob(char *name, AES_KEY *aes_key) -{ - uint8_t vector[AES_BLOCK_SIZE]; - int length; - int fd; - - if (read(the_entropy, blob.vector, AES_BLOCK_SIZE) != AES_BLOCK_SIZE) { - return SYSTEM_ERROR; - } - - length = blob.length + (blob.value - blob.encrypted); - length = (length + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE * AES_BLOCK_SIZE; - - if (blob.info != 0) { - memmove(&blob.encrypted[length], &blob.value[blob.length], blob.info); - } - - blob.length = htonl(blob.length); - MD5(blob.digested, length - (blob.digested - blob.encrypted), blob.digest); - - memcpy(vector, blob.vector, AES_BLOCK_SIZE); - AES_cbc_encrypt(blob.encrypted, blob.encrypted, length, aes_key, vector, - AES_ENCRYPT); - - memset(blob.reserved, 0, sizeof(blob.reserved)); - length += (blob.encrypted - (uint8_t *)&blob) + blob.info; - - fd = open(".tmp", O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR); - length -= write(fd, &blob, length); - close(fd); - return (length || rename(".tmp", name)) ? SYSTEM_ERROR : NO_ERROR; -} - -static int8_t decrypt_blob(char *name, AES_KEY *aes_key) -{ - int fd = open(name, O_RDONLY); - int length; - - if (fd == -1) { - return (errno == ENOENT) ? KEY_NOT_FOUND : SYSTEM_ERROR; - } - length = read(fd, &blob, sizeof(blob)); - close(fd); - - length -= (blob.encrypted - (uint8_t *)&blob) + blob.info; - if (length < blob.value - blob.encrypted || length % AES_BLOCK_SIZE != 0) { - return VALUE_CORRUPTED; - } - - AES_cbc_encrypt(blob.encrypted, blob.encrypted, length, aes_key, - blob.vector, AES_DECRYPT); - length -= blob.digested - blob.encrypted; - if (memcmp(blob.digest, MD5(blob.digested, length, NULL), - MD5_DIGEST_LENGTH)) { - return VALUE_CORRUPTED; - } - - length -= blob.value - blob.digested; - blob.length = ntohl(blob.length); - if (blob.length < 0 || blob.length > length) { - return VALUE_CORRUPTED; - } - if (blob.info != 0) { - memmove(&blob.value[blob.length], &blob.value[length], blob.info); - } - return NO_ERROR; -} - -/* Here are the actions. Each of them is a function without arguments. All - * information is defined in global variables, which are set properly before - * performing an action. The number of parameters required by each action is - * fixed and defined in a table. If the return value of an action is positive, - * it will be treated as a response code and transmitted to the client. Note - * that the lengths of parameters are checked when they are received, so - * boundary checks on parameters are omitted. */ - -#define MAX_PARAM 2 -#define MAX_RETRY 4 - -static uid_t uid = -1; -static int8_t state = UNINITIALIZED; -static int8_t retry = MAX_RETRY; - -static struct { - int length; - uint8_t value[VALUE_SIZE]; -} params[MAX_PARAM]; - -static AES_KEY encryption_key; -static AES_KEY decryption_key; - -static int8_t test() -{ - return state; -} - -static int8_t get() -{ - char name[NAME_MAX]; - int n = sprintf(name, "%u_", uid); - encode_key(&name[n], params[0].value, params[0].length); - n = decrypt_blob(name, &decryption_key); - if (n != NO_ERROR) { - return n; - } - send_code(NO_ERROR); - send_message(blob.value, blob.length); - return -NO_ERROR; -} - -static int8_t insert() -{ - char name[NAME_MAX]; - int n = sprintf(name, "%u_", uid); - encode_key(&name[n], params[0].value, params[0].length); - blob.info = 0; - blob.length = params[1].length; - memcpy(blob.value, params[1].value, params[1].length); - return encrypt_blob(name, &encryption_key); -} - -static int8_t delete() -{ - char name[NAME_MAX]; - int n = sprintf(name, "%u_", uid); - encode_key(&name[n], params[0].value, params[0].length); - return (unlink(name) && errno != ENOENT) ? SYSTEM_ERROR : NO_ERROR; -} - -static int8_t exist() -{ - char name[NAME_MAX]; - int n = sprintf(name, "%u_", uid); - encode_key(&name[n], params[0].value, params[0].length); - if (access(name, R_OK) == -1) { - return (errno != ENOENT) ? SYSTEM_ERROR : KEY_NOT_FOUND; - } - return NO_ERROR; -} - -static int8_t saw() -{ - DIR *dir = opendir("."); - struct dirent *file; - char name[NAME_MAX]; - int n; - - if (!dir) { - return SYSTEM_ERROR; - } - n = sprintf(name, "%u_", uid); - n += encode_key(&name[n], params[0].value, params[0].length); - send_code(NO_ERROR); - while ((file = readdir(dir)) != NULL) { - if (!strncmp(name, file->d_name, n)) { - char *p = &file->d_name[n]; - params[0].length = decode_key(params[0].value, p, strlen(p)); - send_message(params[0].value, params[0].length); - } - } - closedir(dir); - return -NO_ERROR; -} - -static int8_t reset() -{ - DIR *dir = opendir("."); - struct dirent *file; - - memset(&encryption_key, 0, sizeof(encryption_key)); - memset(&decryption_key, 0, sizeof(decryption_key)); - state = UNINITIALIZED; - retry = MAX_RETRY; - - if (!dir) { - return SYSTEM_ERROR; - } - while ((file = readdir(dir)) != NULL) { - unlink(file->d_name); - } - closedir(dir); - return NO_ERROR; -} - -#define MASTER_KEY_FILE ".masterkey" -#define MASTER_KEY_SIZE 16 -#define SALT_SIZE 16 - -static void set_key(uint8_t *key, uint8_t *password, int length, uint8_t *salt) -{ - if (salt) { - PKCS5_PBKDF2_HMAC_SHA1((char *)password, length, salt, SALT_SIZE, - 8192, MASTER_KEY_SIZE, key); - } else { - PKCS5_PBKDF2_HMAC_SHA1((char *)password, length, (uint8_t *)"keystore", - sizeof("keystore"), 1024, MASTER_KEY_SIZE, key); - } -} - -/* Here is the history. To improve the security, the parameters to generate the - * master key has been changed. To make a seamless transition, we update the - * file using the same password when the user unlock it for the first time. If - * any thing goes wrong during the transition, the new file will not overwrite - * the old one. This avoids permanent damages of the existing data. */ - -static int8_t password() -{ - uint8_t key[MASTER_KEY_SIZE]; - AES_KEY aes_key; - int8_t response = SYSTEM_ERROR; - - if (state == UNINITIALIZED) { - if (read(the_entropy, blob.value, MASTER_KEY_SIZE) != MASTER_KEY_SIZE) { - return SYSTEM_ERROR; - } - } else { - int fd = open(MASTER_KEY_FILE, O_RDONLY); - uint8_t *salt = NULL; - if (fd != -1) { - int length = read(fd, &blob, sizeof(blob)); - close(fd); - if (length > SALT_SIZE && blob.info == SALT_SIZE) { - salt = (uint8_t *)&blob + length - SALT_SIZE; - } - } - - set_key(key, params[0].value, params[0].length, salt); - AES_set_decrypt_key(key, MASTER_KEY_SIZE * 8, &aes_key); - response = decrypt_blob(MASTER_KEY_FILE, &aes_key); - if (response == SYSTEM_ERROR) { - return SYSTEM_ERROR; - } - if (response != NO_ERROR || blob.length != MASTER_KEY_SIZE) { - if (retry <= 0) { - reset(); - return UNINITIALIZED; - } - return WRONG_PASSWORD + --retry; - } - - if (!salt && params[1].length == -1) { - params[1] = params[0]; - } - } - - if (params[1].length == -1) { - memcpy(key, blob.value, MASTER_KEY_SIZE); - } else { - uint8_t *salt = &blob.value[MASTER_KEY_SIZE]; - if (read(the_entropy, salt, SALT_SIZE) != SALT_SIZE) { - return SYSTEM_ERROR; - } - - set_key(key, params[1].value, params[1].length, salt); - AES_set_encrypt_key(key, MASTER_KEY_SIZE * 8, &aes_key); - memcpy(key, blob.value, MASTER_KEY_SIZE); - blob.info = SALT_SIZE; - blob.length = MASTER_KEY_SIZE; - response = encrypt_blob(MASTER_KEY_FILE, &aes_key); - } - - if (response == NO_ERROR) { - AES_set_encrypt_key(key, MASTER_KEY_SIZE * 8, &encryption_key); - AES_set_decrypt_key(key, MASTER_KEY_SIZE * 8, &decryption_key); - state = NO_ERROR; - retry = MAX_RETRY; - } - return response; -} - -static int8_t lock() -{ - memset(&encryption_key, 0, sizeof(encryption_key)); - memset(&decryption_key, 0, sizeof(decryption_key)); - state = LOCKED; - return NO_ERROR; -} - -static int8_t unlock() -{ - params[1].length = -1; - return password(); -} - -/* Here are the permissions, actions, users, and the main function. */ - -enum perm { - TEST = 1, - GET = 2, - INSERT = 4, - DELETE = 8, - EXIST = 16, - SAW = 32, - RESET = 64, - PASSWORD = 128, - LOCK = 256, - UNLOCK = 512, -}; - -static struct action { - int8_t (*run)(); - int8_t code; - int8_t state; - uint32_t perm; - int lengths[MAX_PARAM]; -} actions[] = { - {test, 't', 0, TEST, {0}}, - {get, 'g', NO_ERROR, GET, {KEY_SIZE}}, - {insert, 'i', NO_ERROR, INSERT, {KEY_SIZE, VALUE_SIZE}}, - {delete, 'd', 0, DELETE, {KEY_SIZE}}, - {exist, 'e', 0, EXIST, {KEY_SIZE}}, - {saw, 's', 0, SAW, {KEY_SIZE}}, - {reset, 'r', 0, RESET, {0}}, - {password, 'p', 0, PASSWORD, {PASSWORD_SIZE, PASSWORD_SIZE}}, - {lock, 'l', NO_ERROR, LOCK, {0}}, - {unlock, 'u', LOCKED, UNLOCK, {PASSWORD_SIZE}}, - {NULL, 0 , 0, 0, {0}}, -}; - -static struct user { - uid_t uid; - uid_t euid; - uint32_t perms; -} users[] = { - {AID_SYSTEM, ~0, ~GET}, - {AID_VPN, AID_SYSTEM, GET}, - {AID_WIFI, AID_SYSTEM, GET}, - {AID_ROOT, AID_SYSTEM, GET}, - {~0, ~0, TEST | GET | INSERT | DELETE | EXIST | SAW}, -}; - -static int8_t process(int8_t code) { - struct user *user = users; - struct action *action = actions; - int i; - - while (~user->uid && user->uid != uid) { - ++user; - } - while (action->code && action->code != code) { - ++action; - } - if (!action->code) { - return UNDEFINED_ACTION; - } - if (!(action->perm & user->perms)) { - return PERMISSION_DENIED; - } - if (action->state && action->state != state) { - return state; - } - if (~user->euid) { - uid = user->euid; - } - for (i = 0; i < MAX_PARAM && action->lengths[i]; ++i) { - params[i].length = recv_message(params[i].value, action->lengths[i]); - if (params[i].length == -1) { - return PROTOCOL_ERROR; - } - } - if (!recv_end_of_file()) { - return PROTOCOL_ERROR; - } - return action->run(); -} - -#define RANDOM_DEVICE "/dev/urandom" - -int main(int argc, char **argv) -{ - int control_socket = android_get_control_socket("keystore"); - if (argc < 2) { - LOGE("A directory must be specified!"); - return 1; - } - if (chdir(argv[1]) == -1) { - LOGE("chdir: %s: %s", argv[1], strerror(errno)); - return 1; - } - if ((the_entropy = open(RANDOM_DEVICE, O_RDONLY)) == -1) { - LOGE("open: %s: %s", RANDOM_DEVICE, strerror(errno)); - return 1; - } - if (listen(control_socket, 3) == -1) { - LOGE("listen: %s", strerror(errno)); - return 1; - } - - signal(SIGPIPE, SIG_IGN); - if (access(MASTER_KEY_FILE, R_OK) == 0) { - state = LOCKED; - } - - while ((the_socket = accept(control_socket, NULL, 0)) != -1) { - struct timeval tv = {.tv_sec = 3}; - struct ucred cred; - socklen_t size = sizeof(cred); - int8_t request; - - setsockopt(the_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); - setsockopt(the_socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); - - if (getsockopt(the_socket, SOL_SOCKET, SO_PEERCRED, &cred, &size)) { - LOGW("getsockopt: %s", strerror(errno)); - } else if (recv_code(&request)) { - int8_t old_state = state; - int8_t response; - uid = cred.uid; - - if ((response = process(request)) > 0) { - send_code(response); - response = -response; - } - - LOGI("uid: %d action: %c -> %d state: %d -> %d retry: %d", - cred.uid, request, -response, old_state, state, retry); - } - close(the_socket); - } - LOGE("accept: %s", strerror(errno)); - return 1; -} diff --git a/cmds/keystore/keystore.cpp b/cmds/keystore/keystore.cpp new file mode 100644 index 0000000..4b4b9b9 --- /dev/null +++ b/cmds/keystore/keystore.cpp @@ -0,0 +1,810 @@ +/* + * Copyright (C) 2009 The Android Open Source 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 <stdio.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <errno.h> +#include <dirent.h> +#include <fcntl.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <arpa/inet.h> + +#include <openssl/aes.h> +#include <openssl/evp.h> +#include <openssl/md5.h> + +#define LOG_TAG "keystore" +#include <cutils/log.h> +#include <cutils/sockets.h> +#include <private/android_filesystem_config.h> + +#include "keystore.h" + +/* KeyStore is a secured storage for key-value pairs. In this implementation, + * each file stores one key-value pair. Keys are encoded in file names, and + * values are encrypted with checksums. The encryption key is protected by a + * user-defined password. To keep things simple, buffers are always larger than + * the maximum space we needed, so boundary checks on buffers are omitted. */ + +#define KEY_SIZE ((NAME_MAX - 15) / 2) +#define VALUE_SIZE 32768 +#define PASSWORD_SIZE VALUE_SIZE + +struct Value { + int length; + uint8_t value[VALUE_SIZE]; +}; + +/* Here is the encoding of keys. This is necessary in order to allow arbitrary + * characters in keys. Characters in [0-~] are not encoded. Others are encoded + * into two bytes. The first byte is one of [+-.] which represents the first + * two bits of the character. The second byte encodes the rest of the bits into + * [0-o]. Therefore in the worst case the length of a key gets doubled. Note + * that Base64 cannot be used here due to the need of prefix match on keys. */ + +static int encode_key(char* out, uid_t uid, const Value* key) { + int n = snprintf(out, NAME_MAX, "%u_", uid); + out += n; + const uint8_t* in = key->value; + int length = key->length; + for (int i = length; i > 0; --i, ++in, ++out) { + if (*in >= '0' && *in <= '~') { + *out = *in; + } else { + *out = '+' + (*in >> 6); + *++out = '0' + (*in & 0x3F); + ++length; + } + } + *out = '\0'; + return n + length; +} + +static int decode_key(uint8_t* out, char* in, int length) { + for (int i = 0; i < length; ++i, ++in, ++out) { + if (*in >= '0' && *in <= '~') { + *out = *in; + } else { + *out = (*in - '+') << 6; + *out |= (*++in - '0') & 0x3F; + --length; + } + } + *out = '\0'; + return length; +} + +static size_t readFully(int fd, uint8_t* data, size_t size) { + size_t remaining = size; + while (remaining > 0) { + ssize_t n = TEMP_FAILURE_RETRY(read(fd, data, size)); + if (n == -1 || n == 0) { + return size-remaining; + } + data += n; + remaining -= n; + } + return size; +} + +static size_t writeFully(int fd, uint8_t* data, size_t size) { + size_t remaining = size; + while (remaining > 0) { + ssize_t n = TEMP_FAILURE_RETRY(write(fd, data, size)); + if (n == -1 || n == 0) { + return size-remaining; + } + data += n; + remaining -= n; + } + return size; +} + +class Entropy { +public: + Entropy() : mRandom(-1) {} + ~Entropy() { + if (mRandom != -1) { + close(mRandom); + } + } + + bool open() { + const char* randomDevice = "/dev/urandom"; + mRandom = ::open(randomDevice, O_RDONLY); + if (mRandom == -1) { + LOGE("open: %s: %s", randomDevice, strerror(errno)); + return false; + } + return true; + } + + bool generate_random_data(uint8_t* data, size_t size) { + return (readFully(mRandom, data, size) == size); + } + +private: + int mRandom; +}; + +/* Here is the file format. There are two parts in blob.value, the secret and + * the description. The secret is stored in ciphertext, and its original size + * can be found in blob.length. The description is stored after the secret in + * plaintext, and its size is specified in blob.info. The total size of the two + * parts must be no more than VALUE_SIZE bytes. The first three bytes of the + * file are reserved for future use and are always set to zero. Fields other + * than blob.info, blob.length, and blob.value are modified by encryptBlob() + * and decryptBlob(). Thus they should not be accessed from outside. */ + +struct __attribute__((packed)) blob { + uint8_t reserved[3]; + uint8_t info; + uint8_t vector[AES_BLOCK_SIZE]; + uint8_t encrypted[0]; + uint8_t digest[MD5_DIGEST_LENGTH]; + uint8_t digested[0]; + int32_t length; // in network byte order when encrypted + uint8_t value[VALUE_SIZE + AES_BLOCK_SIZE]; +}; + +class Blob { +public: + Blob(uint8_t* value, int32_t valueLength, uint8_t* info, uint8_t infoLength) { + mBlob.length = valueLength; + memcpy(mBlob.value, value, valueLength); + + mBlob.info = infoLength; + memcpy(mBlob.value + valueLength, info, infoLength); + } + + Blob(blob b) { + mBlob = b; + } + + Blob() {} + + uint8_t* getValue() { + return mBlob.value; + } + + int32_t getLength() { + return mBlob.length; + } + + uint8_t getInfo() { + return mBlob.info; + } + + ResponseCode encryptBlob(const char* filename, AES_KEY *aes_key, Entropy* entropy) { + if (!entropy->generate_random_data(mBlob.vector, AES_BLOCK_SIZE)) { + return SYSTEM_ERROR; + } + + // data includes the value and the value's length + size_t dataLength = mBlob.length + sizeof(mBlob.length); + // pad data to the AES_BLOCK_SIZE + size_t digestedLength = ((dataLength + AES_BLOCK_SIZE - 1) + / AES_BLOCK_SIZE * AES_BLOCK_SIZE); + // encrypted data includes the digest value + size_t encryptedLength = digestedLength + MD5_DIGEST_LENGTH; + // move info after space for padding + memmove(&mBlob.encrypted[encryptedLength], &mBlob.value[mBlob.length], mBlob.info); + // zero padding area + memset(mBlob.value + mBlob.length, 0, digestedLength - dataLength); + + mBlob.length = htonl(mBlob.length); + MD5(mBlob.digested, digestedLength, mBlob.digest); + + uint8_t vector[AES_BLOCK_SIZE]; + memcpy(vector, mBlob.vector, AES_BLOCK_SIZE); + AES_cbc_encrypt(mBlob.encrypted, mBlob.encrypted, encryptedLength, + aes_key, vector, AES_ENCRYPT); + + memset(mBlob.reserved, 0, sizeof(mBlob.reserved)); + size_t headerLength = (mBlob.encrypted - (uint8_t*) &mBlob); + size_t fileLength = encryptedLength + headerLength + mBlob.info; + + const char* tmpFileName = ".tmp"; + int out = open(tmpFileName, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR); + if (out == -1) { + return SYSTEM_ERROR; + } + size_t writtenBytes = writeFully(out, (uint8_t*) &mBlob, fileLength); + if (close(out) != 0) { + return SYSTEM_ERROR; + } + if (writtenBytes != fileLength) { + unlink(tmpFileName); + return SYSTEM_ERROR; + } + return (rename(tmpFileName, filename) == 0) ? NO_ERROR : SYSTEM_ERROR; + } + + ResponseCode decryptBlob(const char* filename, AES_KEY *aes_key) { + int in = open(filename, O_RDONLY); + if (in == -1) { + return (errno == ENOENT) ? KEY_NOT_FOUND : SYSTEM_ERROR; + } + // fileLength may be less than sizeof(mBlob) since the in + // memory version has extra padding to tolerate rounding up to + // the AES_BLOCK_SIZE + size_t fileLength = readFully(in, (uint8_t*) &mBlob, sizeof(mBlob)); + if (close(in) != 0) { + return SYSTEM_ERROR; + } + size_t headerLength = (mBlob.encrypted - (uint8_t*) &mBlob); + if (fileLength < headerLength) { + return VALUE_CORRUPTED; + } + + ssize_t encryptedLength = fileLength - (headerLength + mBlob.info); + if (encryptedLength < 0 || encryptedLength % AES_BLOCK_SIZE != 0) { + return VALUE_CORRUPTED; + } + AES_cbc_encrypt(mBlob.encrypted, mBlob.encrypted, encryptedLength, aes_key, + mBlob.vector, AES_DECRYPT); + size_t digestedLength = encryptedLength - MD5_DIGEST_LENGTH; + uint8_t computedDigest[MD5_DIGEST_LENGTH]; + MD5(mBlob.digested, digestedLength, computedDigest); + if (memcmp(mBlob.digest, computedDigest, MD5_DIGEST_LENGTH) != 0) { + return VALUE_CORRUPTED; + } + + ssize_t maxValueLength = digestedLength - sizeof(mBlob.length); + mBlob.length = ntohl(mBlob.length); + if (mBlob.length < 0 || mBlob.length > maxValueLength) { + return VALUE_CORRUPTED; + } + if (mBlob.info != 0) { + // move info from after padding to after data + memmove(&mBlob.value[mBlob.length], &mBlob.value[maxValueLength], mBlob.info); + } + return NO_ERROR; + } + +private: + struct blob mBlob; +}; + +class KeyStore { +public: + KeyStore(Entropy* entropy) : mEntropy(entropy), mRetry(MAX_RETRY) { + if (access(MASTER_KEY_FILE, R_OK) == 0) { + setState(STATE_LOCKED); + } else { + setState(STATE_UNINITIALIZED); + } + } + + State getState() { + return mState; + } + + int8_t getRetry() { + return mRetry; + } + + ResponseCode initialize(Value* pw) { + if (!generateMasterKey()) { + return SYSTEM_ERROR; + } + ResponseCode response = writeMasterKey(pw); + if (response != NO_ERROR) { + return response; + } + setupMasterKeys(); + return NO_ERROR; + } + + ResponseCode writeMasterKey(Value* pw) { + uint8_t passwordKey[MASTER_KEY_SIZE_BYTES]; + generateKeyFromPassword(passwordKey, MASTER_KEY_SIZE_BYTES, pw, mSalt); + AES_KEY passwordAesKey; + AES_set_encrypt_key(passwordKey, MASTER_KEY_SIZE_BITS, &passwordAesKey); + Blob masterKeyBlob(mMasterKey, sizeof(mMasterKey), mSalt, sizeof(mSalt)); + return masterKeyBlob.encryptBlob(MASTER_KEY_FILE, &passwordAesKey, mEntropy); + } + + ResponseCode readMasterKey(Value* pw) { + int in = open(MASTER_KEY_FILE, O_RDONLY); + if (in == -1) { + return SYSTEM_ERROR; + } + + // we read the raw blob to just to get the salt to generate + // the AES key, then we create the Blob to use with decryptBlob + blob rawBlob; + size_t length = readFully(in, (uint8_t*) &rawBlob, sizeof(rawBlob)); + if (close(in) != 0) { + return SYSTEM_ERROR; + } + // find salt at EOF if present, otherwise we have an old file + uint8_t* salt; + if (length > SALT_SIZE && rawBlob.info == SALT_SIZE) { + salt = (uint8_t*) &rawBlob + length - SALT_SIZE; + } else { + salt = NULL; + } + uint8_t passwordKey[MASTER_KEY_SIZE_BYTES]; + generateKeyFromPassword(passwordKey, MASTER_KEY_SIZE_BYTES, pw, salt); + AES_KEY passwordAesKey; + AES_set_decrypt_key(passwordKey, MASTER_KEY_SIZE_BITS, &passwordAesKey); + Blob masterKeyBlob(rawBlob); + ResponseCode response = masterKeyBlob.decryptBlob(MASTER_KEY_FILE, &passwordAesKey); + if (response == SYSTEM_ERROR) { + return SYSTEM_ERROR; + } + if (response == NO_ERROR && masterKeyBlob.getLength() == MASTER_KEY_SIZE_BYTES) { + // if salt was missing, generate one and write a new master key file with the salt. + if (salt == NULL) { + if (!generateSalt()) { + return SYSTEM_ERROR; + } + response = writeMasterKey(pw); + } + if (response == NO_ERROR) { + memcpy(mMasterKey, masterKeyBlob.getValue(), MASTER_KEY_SIZE_BYTES); + setupMasterKeys(); + } + return response; + } + if (mRetry <= 0) { + reset(); + return UNINITIALIZED; + } + --mRetry; + switch (mRetry) { + case 0: return WRONG_PASSWORD_0; + case 1: return WRONG_PASSWORD_1; + case 2: return WRONG_PASSWORD_2; + case 3: return WRONG_PASSWORD_3; + default: return WRONG_PASSWORD_3; + } + } + + bool reset() { + clearMasterKeys(); + setState(STATE_UNINITIALIZED); + + DIR* dir = opendir("."); + struct dirent* file; + + if (!dir) { + return false; + } + while ((file = readdir(dir)) != NULL) { + unlink(file->d_name); + } + closedir(dir); + return true; + } + + bool isEmpty() { + DIR* dir = opendir("."); + struct dirent* file; + if (!dir) { + return true; + } + bool result = true; + while ((file = readdir(dir)) != NULL) { + if (isKeyFile(file->d_name)) { + result = false; + break; + } + } + closedir(dir); + return result; + } + + void lock() { + clearMasterKeys(); + setState(STATE_LOCKED); + } + + ResponseCode get(const char* filename, Blob* keyBlob) { + return keyBlob->decryptBlob(filename, &mMasterKeyDecryption); + } + + ResponseCode put(const char* filename, Blob* keyBlob) { + return keyBlob->encryptBlob(filename, &mMasterKeyEncryption, mEntropy); + } + +private: + static const char* MASTER_KEY_FILE; + static const int MASTER_KEY_SIZE_BYTES = 16; + static const int MASTER_KEY_SIZE_BITS = MASTER_KEY_SIZE_BYTES * 8; + + static const int MAX_RETRY = 4; + static const size_t SALT_SIZE = 16; + + Entropy* mEntropy; + + State mState; + int8_t mRetry; + + uint8_t mMasterKey[MASTER_KEY_SIZE_BYTES]; + uint8_t mSalt[SALT_SIZE]; + + AES_KEY mMasterKeyEncryption; + AES_KEY mMasterKeyDecryption; + + void setState(State state) { + mState = state; + if (mState == STATE_NO_ERROR || mState == STATE_UNINITIALIZED) { + mRetry = MAX_RETRY; + } + } + + bool generateSalt() { + return mEntropy->generate_random_data(mSalt, sizeof(mSalt)); + } + + bool generateMasterKey() { + if (!mEntropy->generate_random_data(mMasterKey, sizeof(mMasterKey))) { + return false; + } + if (!generateSalt()) { + return false; + } + return true; + } + + void setupMasterKeys() { + AES_set_encrypt_key(mMasterKey, MASTER_KEY_SIZE_BITS, &mMasterKeyEncryption); + AES_set_decrypt_key(mMasterKey, MASTER_KEY_SIZE_BITS, &mMasterKeyDecryption); + setState(STATE_NO_ERROR); + } + + void clearMasterKeys() { + memset(mMasterKey, 0, sizeof(mMasterKey)); + memset(mSalt, 0, sizeof(mSalt)); + memset(&mMasterKeyEncryption, 0, sizeof(mMasterKeyEncryption)); + memset(&mMasterKeyDecryption, 0, sizeof(mMasterKeyDecryption)); + } + + static void generateKeyFromPassword(uint8_t* key, ssize_t keySize, Value* pw, uint8_t* salt) { + size_t saltSize; + if (salt != NULL) { + saltSize = SALT_SIZE; + } else { + // pre-gingerbread used this hardwired salt, readMasterKey will rewrite these when found + salt = (uint8_t*) "keystore"; + // sizeof = 9, not strlen = 8 + saltSize = sizeof("keystore"); + } + PKCS5_PBKDF2_HMAC_SHA1((char*) pw->value, pw->length, salt, saltSize, 8192, keySize, key); + } + + static bool isKeyFile(const char* filename) { + return ((strcmp(filename, MASTER_KEY_FILE) != 0) + && (strcmp(filename, ".") != 0) + && (strcmp(filename, "..") != 0)); + } +}; + +const char* KeyStore::MASTER_KEY_FILE = ".masterkey"; + +/* Here is the protocol used in both requests and responses: + * code [length_1 message_1 ... length_n message_n] end-of-file + * where code is one byte long and lengths are unsigned 16-bit integers in + * network order. Thus the maximum length of a message is 65535 bytes. */ + +static int recv_code(int sock, int8_t* code) { + return recv(sock, code, 1, 0) == 1; +} + +static int recv_message(int sock, uint8_t* message, int length) { + uint8_t bytes[2]; + if (recv(sock, &bytes[0], 1, 0) != 1 || + recv(sock, &bytes[1], 1, 0) != 1) { + return -1; + } else { + int offset = bytes[0] << 8 | bytes[1]; + if (length < offset) { + return -1; + } + length = offset; + offset = 0; + while (offset < length) { + int n = recv(sock, &message[offset], length - offset, 0); + if (n <= 0) { + return -1; + } + offset += n; + } + } + return length; +} + +static int recv_end_of_file(int sock) { + uint8_t byte; + return recv(sock, &byte, 1, 0) == 0; +} + +static void send_code(int sock, int8_t code) { + send(sock, &code, 1, 0); +} + +static void send_message(int sock, uint8_t* message, int length) { + uint16_t bytes = htons(length); + send(sock, &bytes, 2, 0); + send(sock, message, length, 0); +} + +/* Here are the actions. Each of them is a function without arguments. All + * information is defined in global variables, which are set properly before + * performing an action. The number of parameters required by each action is + * fixed and defined in a table. If the return value of an action is positive, + * it will be treated as a response code and transmitted to the client. Note + * that the lengths of parameters are checked when they are received, so + * boundary checks on parameters are omitted. */ + +static const ResponseCode NO_ERROR_RESPONSE_CODE_SENT = (ResponseCode) 0; + +static ResponseCode test(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*) { + return (ResponseCode) keyStore->getState(); +} + +static ResponseCode get(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value*) { + char filename[NAME_MAX]; + encode_key(filename, uid, keyName); + Blob keyBlob; + ResponseCode responseCode = keyStore->get(filename, &keyBlob); + if (responseCode != NO_ERROR) { + return responseCode; + } + send_code(sock, NO_ERROR); + send_message(sock, keyBlob.getValue(), keyBlob.getLength()); + return NO_ERROR_RESPONSE_CODE_SENT; +} + +static ResponseCode insert(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value* val) { + char filename[NAME_MAX]; + encode_key(filename, uid, keyName); + Blob keyBlob(val->value, val->length, 0, NULL); + return keyStore->put(filename, &keyBlob); +} + +static ResponseCode del(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value*) { + char filename[NAME_MAX]; + encode_key(filename, uid, keyName); + return (unlink(filename) && errno != ENOENT) ? SYSTEM_ERROR : NO_ERROR; +} + +static ResponseCode exist(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value*) { + char filename[NAME_MAX]; + encode_key(filename, uid, keyName); + if (access(filename, R_OK) == -1) { + return (errno != ENOENT) ? SYSTEM_ERROR : KEY_NOT_FOUND; + } + return NO_ERROR; +} + +static ResponseCode saw(KeyStore* keyStore, int sock, uid_t uid, Value* keyPrefix, Value*) { + DIR* dir = opendir("."); + if (!dir) { + return SYSTEM_ERROR; + } + char filename[NAME_MAX]; + int n = encode_key(filename, uid, keyPrefix); + send_code(sock, NO_ERROR); + + struct dirent* file; + while ((file = readdir(dir)) != NULL) { + if (!strncmp(filename, file->d_name, n)) { + char* p = &file->d_name[n]; + keyPrefix->length = decode_key(keyPrefix->value, p, strlen(p)); + send_message(sock, keyPrefix->value, keyPrefix->length); + } + } + closedir(dir); + return NO_ERROR_RESPONSE_CODE_SENT; +} + +static ResponseCode reset(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*) { + return keyStore->reset() ? NO_ERROR : SYSTEM_ERROR; +} + +/* Here is the history. To improve the security, the parameters to generate the + * master key has been changed. To make a seamless transition, we update the + * file using the same password when the user unlock it for the first time. If + * any thing goes wrong during the transition, the new file will not overwrite + * the old one. This avoids permanent damages of the existing data. */ + +static ResponseCode password(KeyStore* keyStore, int sock, uid_t uid, Value* pw, Value*) { + switch (keyStore->getState()) { + case STATE_UNINITIALIZED: { + // generate master key, encrypt with password, write to file, initialize mMasterKey*. + return keyStore->initialize(pw); + } + case STATE_NO_ERROR: { + // rewrite master key with new password. + return keyStore->writeMasterKey(pw); + } + case STATE_LOCKED: { + // read master key, decrypt with password, initialize mMasterKey*. + return keyStore->readMasterKey(pw); + } + } + return SYSTEM_ERROR; +} + +static ResponseCode lock(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*) { + keyStore->lock(); + return NO_ERROR; +} + +static ResponseCode unlock(KeyStore* keyStore, int sock, uid_t uid, Value* pw, Value* unused) { + return password(keyStore, sock, uid, pw, unused); +} + +static ResponseCode zero(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*) { + return keyStore->isEmpty() ? KEY_NOT_FOUND : NO_ERROR; +} + +/* Here are the permissions, actions, users, and the main function. */ + +enum perm { + TEST = 1, + GET = 2, + INSERT = 4, + DELETE = 8, + EXIST = 16, + SAW = 32, + RESET = 64, + PASSWORD = 128, + LOCK = 256, + UNLOCK = 512, + ZERO = 1024, +}; + +static const int MAX_PARAM = 2; + +static const State STATE_ANY = (State) 0; + +static struct action { + ResponseCode (*run)(KeyStore* keyStore, int sock, uid_t uid, Value* param1, Value* param2); + int8_t code; + State state; + uint32_t perm; + int lengths[MAX_PARAM]; +} actions[] = { + {test, 't', STATE_ANY, TEST, {0, 0}}, + {get, 'g', STATE_NO_ERROR, GET, {KEY_SIZE, 0}}, + {insert, 'i', STATE_NO_ERROR, INSERT, {KEY_SIZE, VALUE_SIZE}}, + {del, 'd', STATE_ANY, DELETE, {KEY_SIZE, 0}}, + {exist, 'e', STATE_ANY, EXIST, {KEY_SIZE, 0}}, + {saw, 's', STATE_ANY, SAW, {KEY_SIZE, 0}}, + {reset, 'r', STATE_ANY, RESET, {0, 0}}, + {password, 'p', STATE_ANY, PASSWORD, {PASSWORD_SIZE, 0}}, + {lock, 'l', STATE_NO_ERROR, LOCK, {0, 0}}, + {unlock, 'u', STATE_LOCKED, UNLOCK, {PASSWORD_SIZE, 0}}, + {zero, 'z', STATE_ANY, ZERO, {0, 0}}, + {NULL, 0 , STATE_ANY, 0, {0, 0}}, +}; + +static struct user { + uid_t uid; + uid_t euid; + uint32_t perms; +} users[] = { + {AID_SYSTEM, ~0, ~0}, + {AID_VPN, AID_SYSTEM, GET}, + {AID_WIFI, AID_SYSTEM, GET}, + {AID_ROOT, AID_SYSTEM, GET}, + {~0, ~0, TEST | GET | INSERT | DELETE | EXIST | SAW}, +}; + +static ResponseCode process(KeyStore* keyStore, int sock, uid_t uid, int8_t code) { + struct user* user = users; + struct action* action = actions; + int i; + + while (~user->uid && user->uid != uid) { + ++user; + } + while (action->code && action->code != code) { + ++action; + } + if (!action->code) { + return UNDEFINED_ACTION; + } + if (!(action->perm & user->perms)) { + return PERMISSION_DENIED; + } + if (action->state != STATE_ANY && action->state != keyStore->getState()) { + return (ResponseCode) keyStore->getState(); + } + if (~user->euid) { + uid = user->euid; + } + Value params[MAX_PARAM]; + for (i = 0; i < MAX_PARAM && action->lengths[i] != 0; ++i) { + params[i].length = recv_message(sock, params[i].value, action->lengths[i]); + if (params[i].length < 0) { + return PROTOCOL_ERROR; + } + } + if (!recv_end_of_file(sock)) { + return PROTOCOL_ERROR; + } + return action->run(keyStore, sock, uid, ¶ms[0], ¶ms[1]); +} + +int main(int argc, char* argv[]) { + int controlSocket = android_get_control_socket("keystore"); + if (argc < 2) { + LOGE("A directory must be specified!"); + return 1; + } + if (chdir(argv[1]) == -1) { + LOGE("chdir: %s: %s", argv[1], strerror(errno)); + return 1; + } + + Entropy entropy; + if (!entropy.open()) { + return 1; + } + if (listen(controlSocket, 3) == -1) { + LOGE("listen: %s", strerror(errno)); + return 1; + } + + signal(SIGPIPE, SIG_IGN); + + KeyStore keyStore(&entropy); + int sock; + while ((sock = accept(controlSocket, NULL, 0)) != -1) { + struct timeval tv; + tv.tv_sec = 3; + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + + struct ucred cred; + socklen_t size = sizeof(cred); + int credResult = getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &cred, &size); + if (credResult != 0) { + LOGW("getsockopt: %s", strerror(errno)); + } else { + int8_t request; + if (recv_code(sock, &request)) { + State old_state = keyStore.getState(); + ResponseCode response = process(&keyStore, sock, cred.uid, request); + if (response == NO_ERROR_RESPONSE_CODE_SENT) { + response = NO_ERROR; + } else { + send_code(sock, response); + } + LOGI("uid: %d action: %c -> %d state: %d -> %d retry: %d", + cred.uid, + request, response, + old_state, keyStore.getState(), + keyStore.getRetry()); + } + } + close(sock); + } + LOGE("accept: %s", strerror(errno)); + return 1; +} diff --git a/cmds/keystore/keystore.h b/cmds/keystore/keystore.h index 5ef51e9..5ae3d24 100644 --- a/cmds/keystore/keystore.h +++ b/cmds/keystore/keystore.h @@ -17,17 +17,27 @@ #ifndef __KEYSTORE_H__ #define __KEYSTORE_H__ -enum response_code { - NO_ERROR = 1, - LOCKED = 2, - UNINITIALIZED = 3, +// note state values overlap with ResponseCode for the purposes of the state() API +enum State { + STATE_NO_ERROR = 1, + STATE_LOCKED = 2, + STATE_UNINITIALIZED = 3, +}; + +enum ResponseCode { + NO_ERROR = STATE_NO_ERROR, // 1 + LOCKED = STATE_LOCKED, // 2 + UNINITIALIZED = STATE_UNINITIALIZED, // 3 SYSTEM_ERROR = 4, PROTOCOL_ERROR = 5, PERMISSION_DENIED = 6, KEY_NOT_FOUND = 7, VALUE_CORRUPTED = 8, UNDEFINED_ACTION = 9, - WRONG_PASSWORD = 10, + WRONG_PASSWORD_0 = 10, + WRONG_PASSWORD_1 = 11, + WRONG_PASSWORD_2 = 12, + WRONG_PASSWORD_3 = 13, // MAX_RETRY = 4 }; #endif diff --git a/cmds/keystore/keystore_cli.c b/cmds/keystore/keystore_cli.cpp index e8afb5a..dcd3bcb 100644 --- a/cmds/keystore/keystore_cli.c +++ b/cmds/keystore/keystore_cli.cpp @@ -24,44 +24,40 @@ #include "keystore.h" -char *responses[256] = { - [NO_ERROR] = "No error", - [LOCKED] = "Locked", - [UNINITIALIZED] = "Uninitialized", - [SYSTEM_ERROR] = "System error", - [PROTOCOL_ERROR] = "Protocol error", - [PERMISSION_DENIED] = "Permission denied", - [KEY_NOT_FOUND] = "Key not found", - [VALUE_CORRUPTED] = "Value corrupted", - [UNDEFINED_ACTION] = "Undefined action", - [WRONG_PASSWORD] = "Wrong password (last chance)", - [WRONG_PASSWORD + 1] = "Wrong password (2 tries left)", - [WRONG_PASSWORD + 2] = "Wrong password (3 tries left)", - [WRONG_PASSWORD + 3] = "Wrong password (4 tries left)", +static const char* responses[] = { + NULL, + /* [NO_ERROR] = */ "No error", + /* [LOCKED] = */ "Locked", + /* [UNINITIALIZED] = */ "Uninitialized", + /* [SYSTEM_ERROR] = */ "System error", + /* [PROTOCOL_ERROR] = */ "Protocol error", + /* [PERMISSION_DENIED] = */ "Permission denied", + /* [KEY_NOT_FOUND] = */ "Key not found", + /* [VALUE_CORRUPTED] = */ "Value corrupted", + /* [UNDEFINED_ACTION] = */ "Undefined action", + /* [WRONG_PASSWORD] = */ "Wrong password (last chance)", + /* [WRONG_PASSWORD + 1] = */ "Wrong password (2 tries left)", + /* [WRONG_PASSWORD + 2] = */ "Wrong password (3 tries left)", + /* [WRONG_PASSWORD + 3] = */ "Wrong password (4 tries left)", }; -#define MAX_RESPONSE (WRONG_PASSWORD + 3) - -int main(int argc, char **argv) +int main(int argc, char* argv[]) { - uint8_t bytes[65536]; - uint8_t code; - int sock, i; - if (argc < 2) { printf("Usage: %s action [parameter ...]\n", argv[0]); return 0; } - sock = socket_local_client("keystore", ANDROID_SOCKET_NAMESPACE_RESERVED, - SOCK_STREAM); + int sock = socket_local_client("keystore", ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_STREAM); if (sock == -1) { puts("Failed to connect"); return 1; } send(sock, argv[1], 1, 0); - for (i = 2; i < argc; ++i) { + uint8_t bytes[65536]; + for (int i = 2; i < argc; ++i) { uint16_t length = strlen(argv[i]); bytes[0] = length >> 8; bytes[1] = length; @@ -70,11 +66,13 @@ int main(int argc, char **argv) } shutdown(sock, SHUT_WR); + uint8_t code; if (recv(sock, &code, 1, 0) != 1) { puts("Failed to receive"); return 1; } printf("%d %s\n", code , responses[code] ? responses[code] : "Unknown"); + int i; while ((i = recv(sock, &bytes[0], 1, 0)) == 1) { int length; int offset; diff --git a/cmds/keystore/test-keystore b/cmds/keystore/test-keystore new file mode 100755 index 0000000..3be51b3 --- /dev/null +++ b/cmds/keystore/test-keystore @@ -0,0 +1,273 @@ +#!/bin/bash +# +# Copyright 2011, The Android Open Source 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. + +set -e + +prefix=$0 +log_file=$prefix.log +baseline_file=$prefix.baseline + +function cleanup_output() { + rm -f $log_file + rm -f $baseline_file +} + +function log() { + echo "$@" + append $log_file \# "$@" + append $baseline_file \# "$@" +} + +function expect() { + append $baseline_file "$@" +} + +function append() { + declare -r file=$1 + shift + echo "$@" >> $file +} + +function run() { + # strip out carriage returns from adb + # strip out date/time from ls -l + "$@" | tr --delete '\r' | sed -E 's/[0-9]{4}-[0-9]{2}-[0-9]{2} +[0-9]{1,2}:[0-9]{2} //' >> $log_file +} + +function keystore() { + declare -r user=$1 + shift + run adb shell su $user keystore_cli "$@" +} + +function list_keystore_directory() { + run adb shell ls -al /data/misc/keystore +} + +function compare() { + log "comparing $baseline_file and $log_file" + diff $baseline_file $log_file || (log $tag FAILED && exit 1) +} + +function test_basic() { + + # + # reset + # + log "reset keystore as system user" + keystore system r + expect "1 No error" + list_keystore_directory + + # + # basic tests as system/root + # + log "root does not have permission to run test" + keystore root t + expect "6 Permission denied" + + log "but system user does" + keystore system t + expect "3 Uninitialized" + list_keystore_directory + + log "password is now bar" + keystore system p bar + expect "1 No error" + list_keystore_directory + expect "-rw------- keystore keystore 84 .masterkey" + + log "no error implies initialized and unlocked" + keystore system t + expect "1 No error" + + log "saw with no argument" + keystore system s + expect "5 Protocol error" + + log "saw nothing" + keystore system s "" + expect "1 No error" + + log "add key baz" + keystore system i baz quux + expect "1 No error" + + log "1000 is uid of system" + list_keystore_directory + expect "-rw------- keystore keystore 84 .masterkey" + expect "-rw------- keystore keystore 52 1000_baz" + + log "saw baz" + keystore system s "" + expect "1 No error" + expect "baz" + + log "get baz" + keystore system g baz + expect "1 No error" + expect "quux" + + log "root can read system user keys (as can wifi or vpn users)" + keystore root g baz + expect "1 No error" + expect "quux" + + # + # app user tests + # + + # app_0 has uid 10000, as seen below + log "other uses cannot see the system keys" + keystore app_0 g baz + expect "7 Key not found" + + log "app user cannot use reset, password, lock, unlock" + keystore app_0 r + expect "6 Permission denied" + keystore app_0 p + expect "6 Permission denied" + keystore app_0 l + expect "6 Permission denied" + keystore app_0 u + expect "6 Permission denied" + + log "install app_0 key" + keystore app_0 i 0x deadbeef + expect 1 No error + list_keystore_directory + expect "-rw------- keystore keystore 84 .masterkey" + expect "-rw------- keystore keystore 52 10000_0x" + expect "-rw------- keystore keystore 52 1000_baz" + + log "get with no argument" + keystore app_0 g + expect "5 Protocol error" + + keystore app_0 g 0x + expect "1 No error" + expect "deadbeef" + + keystore app_0 i fred barney + expect "1 No error" + + keystore app_0 s "" + expect "1 No error" + expect "0x" + expect "fred" + + log "note that saw returns the suffix of prefix matches" + keystore app_0 s fr # fred + expect "1 No error" + expect "ed" # fred + + # + # lock tests + # + log "lock the store as system" + keystore system l + expect "1 No error" + keystore system t + expect "2 Locked" + + log "saw works while locked" + keystore app_0 s "" + expect "1 No error" + expect "0x" + expect "fred" + + log "...but cannot read keys..." + keystore app_0 g 0x + expect "2 Locked" + + log "...but they can be deleted." + keystore app_0 e 0x + expect "1 No error" + keystore app_0 d 0x + expect "1 No error" + keystore app_0 e 0x + expect "7 Key not found" + + # + # password + # + log "wrong password" + keystore system u foo + expect "13 Wrong password (4 tries left)" + log "right password" + keystore system u bar + expect "1 No error" + + log "make the password foo" + keystore system p foo + expect "1 No error" + + # + # final reset + # + log "reset wipes everything for all users" + keystore system r + expect "1 No error" + list_keystore_directory + + keystore system t + expect "3 Uninitialized" + +} + +function test_4599735() { + # http://b/4599735 + log "start regression test for b/4599735" + keystore system r + expect "1 No error" + + keystore system p foo + expect "1 No error" + + keystore system i baz quux + expect "1 No error" + + keystore root g baz + expect "1 No error" + expect "quux" + + keystore system l + expect "1 No error" + + keystore system p foo + expect "1 No error" + + log "after unlock, regression led to result of '8 Value corrupted'" + keystore root g baz + expect "1 No error" + expect "quux" + + keystore system r + expect "1 No error" + log "end regression test for b/4599735" +} + +function main() { + cleanup_output + log $tag START + test_basic + test_4599735 + compare + log $tag PASSED + cleanup_output +} + +main diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index 1b2326a..0ec007c 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -37,6 +37,7 @@ import android.content.res.AssetManager; import android.content.res.Resources; import android.net.Uri; import android.os.Parcel; +import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; @@ -61,6 +62,7 @@ public final class Pm { private static final String PM_NOT_RUNNING_ERR = "Error: Could not access the Package Manager. Is the system running?"; + private static final int ROOT_UID = 0; public static void main(String[] args) { new Pm().run(args); @@ -118,16 +120,31 @@ public final class Pm { return; } - if ("setInstallLocation".equals(op)) { + if ("disable-user".equals(op)) { + runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER); + return; + } + + if ("set-install-location".equals(op)) { runSetInstallLocation(); return; } - if ("getInstallLocation".equals(op)) { + if ("get-install-location".equals(op)) { runGetInstallLocation(); return; } + if ("createUser".equals(op)) { + runCreateUser(); + return; + } + + if ("removeUser".equals(op)) { + runRemoveUser(); + return; + } + try { if (args.length == 1) { if (args[0].equalsIgnoreCase("-l")) { @@ -755,18 +772,33 @@ public final class Pm { } } - String apkFilePath = nextArg(); + final Uri apkURI; + final Uri verificationURI; + + // Populate apkURI, must be present + final String apkFilePath = nextArg(); System.err.println("\tpkg: " + apkFilePath); - if (apkFilePath == null) { + if (apkFilePath != null) { + apkURI = Uri.fromFile(new File(apkFilePath)); + } else { System.err.println("Error: no package specified"); showUsage(); return; } + // Populate verificationURI, optionally present + final String verificationFilePath = nextArg(); + if (verificationFilePath != null) { + System.err.println("\tver: " + verificationFilePath); + verificationURI = Uri.fromFile(new File(verificationFilePath)); + } else { + verificationURI = null; + } + PackageInstallObserver obs = new PackageInstallObserver(); try { - mPm.installPackage(Uri.fromFile(new File(apkFilePath)), obs, installFlags, - installerPackageName); + mPm.installPackageWithVerification(apkURI, obs, installFlags, installerPackageName, + verificationURI, null); synchronized (obs) { while (!obs.finished) { @@ -789,6 +821,63 @@ public final class Pm { } } + public void runCreateUser() { + // Need to be run as root + if (Process.myUid() != ROOT_UID) { + System.err.println("Error: createUser must be run as root"); + return; + } + String name; + String arg = nextArg(); + if (arg == null) { + System.err.println("Error: no user name specified."); + showUsage(); + return; + } + name = arg; + try { + if (mPm.createUser(name, 0) == null) { + System.err.println("Error: couldn't create user."); + showUsage(); + } + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(PM_NOT_RUNNING_ERR); + } + + } + + public void runRemoveUser() { + // Need to be run as root + if (Process.myUid() != ROOT_UID) { + System.err.println("Error: removeUser must be run as root"); + return; + } + int userId; + String arg = nextArg(); + if (arg == null) { + System.err.println("Error: no user id specified."); + showUsage(); + return; + } + try { + userId = Integer.parseInt(arg); + } catch (NumberFormatException e) { + System.err.println("Error: user id has to be a number."); + showUsage(); + return; + } + try { + if (!mPm.removeUser(userId)) { + System.err.println("Error: couldn't remove user."); + showUsage(); + } + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(PM_NOT_RUNNING_ERR); + } + } + class PackageDeleteObserver extends IPackageDeleteObserver.Stub { boolean finished; boolean result; @@ -901,6 +990,8 @@ public final class Pm { return "enabled"; case PackageManager.COMPONENT_ENABLED_STATE_DISABLED: return "disabled"; + case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER: + return "disabled-user"; } return "unknown"; } @@ -1018,8 +1109,7 @@ public final class Pm { } private static void showUsage() { - System.err.println("usage: pm [list|path|install|uninstall]"); - System.err.println(" pm list packages [-f] [-d] [-e] [-s] [-e] [-u] [FILTER]"); + System.err.println("usage: pm list packages [-f] [-d] [-e] [-s] [-e] [-u] [FILTER]"); System.err.println(" pm list permission-groups"); System.err.println(" pm list permissions [-g] [-f] [-d] [-u] [GROUP]"); System.err.println(" pm list instrumentation [-f] [TARGET-PACKAGE]"); @@ -1031,64 +1121,66 @@ public final class Pm { System.err.println(" pm clear PACKAGE"); System.err.println(" pm enable PACKAGE_OR_COMPONENT"); System.err.println(" pm disable PACKAGE_OR_COMPONENT"); - System.err.println(" pm setInstallLocation [0/auto] [1/internal] [2/external]"); + System.err.println(" pm disable-user PACKAGE_OR_COMPONENT"); + System.err.println(" pm set-install-location [0/auto] [1/internal] [2/external]"); + System.err.println(" pm get-install-location"); + System.err.println(" pm createUser USER_NAME"); + System.err.println(" pm removeUser USER_ID"); System.err.println(""); - System.err.println("The list packages command prints all packages, optionally only"); - System.err.println("those whose package name contains the text in FILTER. Options:"); - System.err.println(" -f: see their associated file."); - System.err.println(" -d: filter to only show disbled packages."); - System.err.println(" -e: filter to only show enabled packages."); - System.err.println(" -s: filter to only show system packages."); - System.err.println(" -3: filter to only show third party packages."); - System.err.println(" -u: also include uninstalled packages."); + System.err.println("pm list packages: prints all packages, optionally only"); + System.err.println(" those whose package name contains the text in FILTER. Options:"); + System.err.println(" -f: see their associated file."); + System.err.println(" -d: filter to only show disbled packages."); + System.err.println(" -e: filter to only show enabled packages."); + System.err.println(" -s: filter to only show system packages."); + System.err.println(" -3: filter to only show third party packages."); + System.err.println(" -u: also include uninstalled packages."); System.err.println(""); - System.err.println("The list permission-groups command prints all known"); - System.err.println("permission groups."); + System.err.println("pm list permission-groups: prints all known permission groups."); System.err.println(""); - System.err.println("The list permissions command prints all known"); - System.err.println("permissions, optionally only those in GROUP. Options:"); - System.err.println(" -g: organize by group."); - System.err.println(" -f: print all information."); - System.err.println(" -s: short summary."); - System.err.println(" -d: only list dangerous permissions."); - System.err.println(" -u: list only the permissions users will see."); + System.err.println("pm list permissions: prints all known permissions, optionally only"); + System.err.println(" those in GROUP. Options:"); + System.err.println(" -g: organize by group."); + System.err.println(" -f: print all information."); + System.err.println(" -s: short summary."); + System.err.println(" -d: only list dangerous permissions."); + System.err.println(" -u: list only the permissions users will see."); System.err.println(""); - System.err.println("The list instrumentation command prints all instrumentations,"); - System.err.println("or only those that target a specified package. Options:"); - System.err.println(" -f: see their associated file."); - System.err.println("(Use this command to list all test packages, or use <TARGET-PACKAGE> "); - System.err.println(" to list the test packages for a particular application. The -f "); - System.err.println(" option lists the .apk file for the test package.)"); + System.err.println("pm list instrumentation: use to list all test packages; optionally"); + System.err.println(" supply <TARGET-PACKAGE> to list the test packages for a particular"); + System.err.println(" application. Options:"); + System.err.println(" -f: list the .apk file for the test package."); System.err.println(""); - System.err.println("The list features command prints all features of the system."); + System.err.println("pm list features: prints all features of the system."); System.err.println(""); - System.err.println("The path command prints the path to the .apk of a package."); + System.err.println("pm path: print the path to the .apk of the given PACKAGE."); System.err.println(""); - System.err.println("The install command installs a package to the system. Options:"); - System.err.println(" -l: install the package with FORWARD_LOCK."); - System.err.println(" -r: reinstall an exisiting app, keeping its data."); - System.err.println(" -t: allow test .apks to be installed."); - System.err.println(" -i: specify the installer package name."); - System.err.println(" -s: install package on sdcard."); - System.err.println(" -f: install package on internal flash."); + System.err.println("pm install: installs a package to the system. Options:"); + System.err.println(" -l: install the package with FORWARD_LOCK."); + System.err.println(" -r: reinstall an exisiting app, keeping its data."); + System.err.println(" -t: allow test .apks to be installed."); + System.err.println(" -i: specify the installer package name."); + System.err.println(" -s: install package on sdcard."); + System.err.println(" -f: install package on internal flash."); System.err.println(""); - System.err.println("The uninstall command removes a package from the system. Options:"); - System.err.println(" -k: keep the data and cache directories around."); - System.err.println("after the package removal."); + System.err.println("pm uninstall: removes a package from the system. Options:"); + System.err.println(" -k: keep the data and cache directories around after package removal."); System.err.println(""); - System.err.println("The clear command deletes all data associated with a package."); + System.err.println("pm clear: deletes all data associated with a package."); System.err.println(""); - System.err.println("The enable and disable commands change the enabled state of"); - System.err.println("a given package or component (written as \"package/class\")."); + System.err.println("pm enable, disable, disable-user: these commands change the enabled state"); + System.err.println(" of a given package or component (written as \"package/class\")."); System.err.println(""); - System.err.println("The getInstallLocation command gets the current install location"); - System.err.println(" 0 [auto]: Let system decide the best location"); - System.err.println(" 1 [internal]: Install on internal device storage"); - System.err.println(" 2 [external]: Install on external media"); + System.err.println("pm get-install-location: returns the current install location."); + System.err.println(" 0 [auto]: Let system decide the best location"); + System.err.println(" 1 [internal]: Install on internal device storage"); + System.err.println(" 2 [external]: Install on external media"); System.err.println(""); - System.err.println("The setInstallLocation command changes the default install location"); - System.err.println(" 0 [auto]: Let system decide the best location"); - System.err.println(" 1 [internal]: Install on internal device storage"); - System.err.println(" 2 [external]: Install on external media"); + System.err.println("pm set-install-location: changes the default install location."); + System.err.println(" NOTE: this is only intended for debugging; using this can cause"); + System.err.println(" applications to break and other undersireable behavior."); + System.err.println(" 0 [auto]: Let system decide the best location"); + System.err.println(" 1 [internal]: Install on internal device storage"); + System.err.println(" 2 [external]: Install on external media"); } } diff --git a/cmds/rawbu/Android.mk b/cmds/rawbu/Android.mk index c1be8a4..b580390 100644 --- a/cmds/rawbu/Android.mk +++ b/cmds/rawbu/Android.mk @@ -1,7 +1,5 @@ # Copyright 2009 The Android Open Source Project -ifneq ($(TARGET_SIMULATOR),true) - LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) @@ -15,5 +13,3 @@ LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := debug include $(BUILD_EXECUTABLE) - -endif diff --git a/cmds/runtime/Android.mk b/cmds/runtime/Android.mk deleted file mode 100644 index 6a72d10..0000000 --- a/cmds/runtime/Android.mk +++ /dev/null @@ -1,30 +0,0 @@ -ifeq ($(TARGET_SIMULATOR),true) - -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - ServiceManager.cpp \ - SignalHandler.cpp \ - main_runtime.cpp - -LOCAL_SHARED_LIBRARIES := \ - libutils \ - libbinder \ - libandroid_runtime \ - libcutils \ - libui \ - libsystem_server \ - libhardware_legacy - -LOCAL_C_INCLUDES := \ - $(JNI_H_INCLUDE) - -ifeq ($(TARGET_OS),linux) - LOCAL_CFLAGS += -DXP_UNIX -endif - -LOCAL_MODULE:= runtime - -include $(BUILD_EXECUTABLE) -endif diff --git a/cmds/runtime/MODULE_LICENSE_APACHE2 b/cmds/runtime/MODULE_LICENSE_APACHE2 deleted file mode 100644 index e69de29..0000000 --- a/cmds/runtime/MODULE_LICENSE_APACHE2 +++ /dev/null diff --git a/cmds/runtime/ServiceManager.cpp b/cmds/runtime/ServiceManager.cpp deleted file mode 100644 index b2bef07..0000000 --- a/cmds/runtime/ServiceManager.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// -// Copyright 2005 The Android Open Source Project -// - -#define LOG_TAG "ServiceManager" - -#include "ServiceManager.h" -#include "SignalHandler.h" - -#include <utils/Debug.h> -#include <utils/Log.h> -#include <binder/Parcel.h> -#include <utils/String8.h> -#include <binder/ProcessState.h> - -#include <private/utils/Static.h> - -#include <ctype.h> -#include <errno.h> -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/stat.h> - -namespace android { - -BServiceManager::BServiceManager() -{ -} - -sp<IBinder> BServiceManager::getService(const String16& name) const -{ - AutoMutex _l(mLock); - ssize_t i = mServices.indexOfKey(name); - LOGV("ServiceManager: getService(%s) -> %d\n", String8(name).string(), i); - if (i >= 0) return mServices.valueAt(i); - return NULL; -} - -sp<IBinder> BServiceManager::checkService(const String16& name) const -{ - AutoMutex _l(mLock); - ssize_t i = mServices.indexOfKey(name); - LOGV("ServiceManager: getService(%s) -> %d\n", String8(name).string(), i); - if (i >= 0) return mServices.valueAt(i); - return NULL; -} - -status_t BServiceManager::addService(const String16& name, const sp<IBinder>& service) -{ - AutoMutex _l(mLock); - LOGI("ServiceManager: addService(%s, %p)\n", String8(name).string(), service.get()); - const ssize_t res = mServices.add(name, service); - if (res >= NO_ERROR) { - mChanged.broadcast(); - return NO_ERROR; - } - return res; -} - -Vector<String16> BServiceManager::listServices() -{ - Vector<String16> res; - - AutoMutex _l(mLock); - const size_t N = mServices.size(); - for (size_t i=0; i<N; i++) { - res.add(mServices.keyAt(i)); - } - - return res; -} - -}; // namespace android diff --git a/cmds/runtime/ServiceManager.h b/cmds/runtime/ServiceManager.h deleted file mode 100644 index 090ca6d..0000000 --- a/cmds/runtime/ServiceManager.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright 2005 The Android Open Source Project -// -#ifndef ANDROID_SERVICE_MANAGER_H -#define ANDROID_SERVICE_MANAGER_H - -#include <binder/IServiceManager.h> -#include <utils/KeyedVector.h> -#include <utils/threads.h> - -namespace android { - -// ---------------------------------------------------------------------- - -class BServiceManager : public BnServiceManager -{ -public: - BServiceManager(); - - virtual sp<IBinder> getService( const String16& name) const; - virtual sp<IBinder> checkService( const String16& name) const; - virtual status_t addService( const String16& name, - const sp<IBinder>& service); - virtual Vector<String16> listServices(); - - -private: - mutable Mutex mLock; - mutable Condition mChanged; - sp<IPermissionController> mPermissionController; - KeyedVector<String16, sp<IBinder> > mServices; -}; - -// ---------------------------------------------------------------------- - -}; // namespace android - -#endif // ANDROID_SERVICE_MANAGER_H diff --git a/cmds/runtime/SignalHandler.cpp b/cmds/runtime/SignalHandler.cpp deleted file mode 100644 index cccaabf..0000000 --- a/cmds/runtime/SignalHandler.cpp +++ /dev/null @@ -1,249 +0,0 @@ -// -// Copyright 2005 The Android Open Source Project -// - -#define LOG_TAG "SignalHandler" - -#include "SignalHandler.h" - -#include <utils/Atomic.h> -#include <utils/Debug.h> -#include <utils/Log.h> - -#include <errno.h> -#include <sys/wait.h> -#include <unistd.h> - -namespace android { - -class SignalHandler::ProcessThread : public Thread -{ -public: - ProcessThread(SignalHandler& sh) - : Thread(false) - , mOwner(sh) - { - } - - virtual bool threadLoop() - { - char buffer[32]; - read(mOwner.mAvailMsg[0], buffer, sizeof(buffer)); - - LOGV("Signal command processing thread woke up!"); - - if (mOwner.mLostCommands) { - LOGE("Lost %d signals!", mOwner.mLostCommands); - mOwner.mLostCommands = 0; - } - - int cur; - while ((cur=mOwner.mCommandBottom) != mOwner.mCommandTop) { - if (mOwner.mCommands[cur].filled == 0) { - LOGV("Command at %d is not yet filled", cur); - break; - } - - LOGV("Processing command at %d, top is %d", - cur, mOwner.mCommandTop); - processCommand(mOwner.mCommands[cur]); - mOwner.mCommands[cur].filled = 0; - - int next = mOwner.mCommandBottom+1; - if (next >= COMMAND_QUEUE_SIZE) { - next = 0; - } - - mOwner.mCommandBottom = next; - } - - return true; - } - - void processCommand(const CommandEntry& entry) - { - switch (entry.signum) { - case SIGCHLD: { - mOwner.mLock.lock(); - ssize_t i = mOwner.mChildHandlers.indexOfKey(entry.info.si_pid); - ChildHandler ch; - if (i >= 0) { - ch = mOwner.mChildHandlers.valueAt(i); - mOwner.mChildHandlers.removeItemsAt(i); - } - mOwner.mLock.unlock(); - - LOGD("SIGCHLD: pid=%d, handle index=%d", entry.info.si_pid, i); - - if (i >= 0) { - int res = waitpid(entry.info.si_pid, NULL, WNOHANG); - LOGW_IF(res == 0, - "Received SIGCHLD, but pid %d is not yet stopped", - entry.info.si_pid); - if (ch.handler) { - ch.handler(entry.info.si_pid, ch.userData); - } - } else { - LOGW("Unhandled SIGCHLD for pid %d", entry.info.si_pid); - } - } break; - } - } - - SignalHandler& mOwner; -}; - - -Mutex SignalHandler::mInstanceLock; -SignalHandler* SignalHandler::mInstance = NULL; - -status_t SignalHandler::setChildHandler(pid_t childPid, - int tag, - child_callback_t handler, - void* userData) -{ - SignalHandler* const self = getInstance(); - - self->mLock.lock(); - - // First make sure this child hasn't already exited. - pid_t res = waitpid(childPid, NULL, WNOHANG); - if (res != 0) { - if (res < 0) { - LOGW("setChildHandler waitpid of %d failed: %d (%s)", - childPid, res, strerror(errno)); - } else { - LOGW("setChildHandler waitpid of %d said %d already dead", - childPid, res); - } - - // Some kind of error... just handle the exit now. - self->mLock.unlock(); - - if (handler) { - handler(childPid, userData); - } - - // Return an error code -- 0 means it already exited. - return (status_t)res; - } - - ChildHandler entry; - entry.childPid = childPid; - entry.tag = tag; - entry.handler = handler; - entry.userData = userData; - - // Note: this replaces an existing entry for this pid, if there already - // is one. This is the required behavior. - LOGD("setChildHandler adding pid %d, tag %d, handler %p, data %p", - childPid, tag, handler, userData); - self->mChildHandlers.add(childPid, entry); - - self->mLock.unlock(); - - return NO_ERROR; -} - -void SignalHandler::killAllChildren(int tag) -{ - SignalHandler* const self = getInstance(); - - AutoMutex _l (self->mLock); - const size_t N = self->mChildHandlers.size(); - for (size_t i=0; i<N; i++) { - const ChildHandler& ch(self->mChildHandlers.valueAt(i)); - if (tag == 0 || ch.tag == tag) { - const pid_t pid = ch.childPid; - LOGI("Killing child %d (tag %d)\n", pid, ch.tag); - kill(pid, SIGKILL); - } - } -} - -SignalHandler::SignalHandler() - : mCommandTop(0) - , mCommandBottom(0) - , mLostCommands(0) -{ - memset(mCommands, 0, sizeof(mCommands)); - - int res = pipe(mAvailMsg); - LOGE_IF(res != 0, "Unable to create signal handler pipe: %s", strerror(errno)); - - mProcessThread = new ProcessThread(*this); - mProcessThread->run("SignalHandler", PRIORITY_HIGHEST); - - struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sa.sa_sigaction = sigAction; - sa.sa_flags = SA_NOCLDSTOP|SA_SIGINFO; - sigaction(SIGCHLD, &sa, NULL); -} - -SignalHandler::~SignalHandler() -{ -} - -SignalHandler* SignalHandler::getInstance() -{ - AutoMutex _l(mInstanceLock); - if (mInstance == NULL) { - mInstance = new SignalHandler(); - } - return mInstance; -} - -void SignalHandler::sigAction(int signum, siginfo_t* info, void*) -{ - static const char wakeupMsg[1] = { 0xff }; - - // If our signal handler is being called, then we know we have - // already initialized the SignalHandler class and thus mInstance - // is valid. - SignalHandler* const self = mInstance; - - // XXX This is not safe! - #if 0 - LOGV("Signal %d: signo=%d, errno=%d, code=%d, pid=%d\n", - signum, - info->si_signo, info->si_errno, info->si_code, - info->si_pid); - #endif - - int32_t oldTop, newTop; - - // Find the next command slot... - do { - oldTop = self->mCommandTop; - - newTop = oldTop + 1; - if (newTop >= COMMAND_QUEUE_SIZE) { - newTop = 0; - } - - if (newTop == self->mCommandBottom) { - // The buffer is filled up! Ouch! - // XXX This is not safe! - #if 0 - LOGE("Command buffer overflow! newTop=%d\n", newTop); - #endif - android_atomic_add(1, &self->mLostCommands); - write(self->mAvailMsg[1], wakeupMsg, sizeof(wakeupMsg)); - return; - } - } while(android_atomic_cmpxchg(oldTop, newTop, &(self->mCommandTop))); - - // Fill in the command data... - self->mCommands[oldTop].signum = signum; - self->mCommands[oldTop].info = *info; - - // And now make this command available. - self->mCommands[oldTop].filled = 1; - - // Wake up the processing thread. - write(self->mAvailMsg[1], wakeupMsg, sizeof(wakeupMsg)); -} - -}; // namespace android - diff --git a/cmds/runtime/SignalHandler.h b/cmds/runtime/SignalHandler.h deleted file mode 100644 index 7f4ef8e..0000000 --- a/cmds/runtime/SignalHandler.h +++ /dev/null @@ -1,137 +0,0 @@ -// -// Copyright 2005 The Android Open Source Project -// -#ifndef ANDROID_SIGNAL_HANDLER_H -#define ANDROID_SIGNAL_HANDLER_H - -#include <utils/KeyedVector.h> -#include <utils/threads.h> - -#include <signal.h> - -namespace android { - -// ---------------------------------------------------------------------- - -enum { - DEFAULT_PROCESS_TAG = 1 -}; - -class SignalHandler -{ -public: - typedef void (*child_callback_t)(pid_t child, void* userData); - - /** - * Set a handler for when a child process exits. By calling - * this, a waitpid() will be done when the child exits to remove - * it from the zombie state. You can also optionally specify a - * handler to be called when the child exits. - * - * If there is already a handler for this child process, it is - * replaced by this new handler. In this case the old handler's - * function is not called. - * - * @param childPid Process ID of child to watch. - * @param childTag User-defined tag for this child. Must be - * greater than zero. - * @param handler If non-NULL, this will be called when the - * child exits. It may be called in either a - * separate signal handling thread, or - * immediately if the child has already exited. - * @param userData Propageted as-is to handler. - * - * @return status_t NO_ERROR if all is well. - */ - static status_t setChildHandler(pid_t childPid, - int childTag = DEFAULT_PROCESS_TAG, - child_callback_t handler = NULL, - void* userData = NULL); - - /** - * Kill all of the child processes for which we have a waiting - * handler, whose tag is the given value. If tag is 0, all - * children are killed. - * - * @param tag - */ - static void killAllChildren(int tag = 0); - -private: - SignalHandler(); - ~SignalHandler(); - - static SignalHandler* getInstance(); - - static void sigAction(int, siginfo_t*, void*); - - // -------------------------------------------------- - // Shared state... all of this is protected by mLock. - // -------------------------------------------------- - - mutable Mutex mLock; - - struct ChildHandler - { - pid_t childPid; - int tag; - child_callback_t handler; - void* userData; - }; - KeyedVector<pid_t, ChildHandler> mChildHandlers; - - // -------------------------------------------------- - // Commmand queue... data is inserted by the signal - // handler using atomic ops, and retrieved by the - // signal processing thread. Because these are touched - // by the signal handler, no lock is used. - // -------------------------------------------------- - - enum { - COMMAND_QUEUE_SIZE = 64 - }; - struct CommandEntry - { - int filled; - int signum; - siginfo_t info; - }; - - // The top of the queue. This is incremented atomically by the - // signal handler before placing a command in the queue. - volatile int32_t mCommandTop; - - // The bottom of the queue. Only modified by the processing - // thread; the signal handler reads it only to determine if the - // queue is full. - int32_t mCommandBottom; - - // Incremented each time we receive a signal and don't have room - // for it on the command queue. - volatile int32_t mLostCommands; - - // The command processing thread. - class ProcessThread; - sp<Thread> mProcessThread; - - // Pipe used to tell command processing thread when new commands. - // are available. The thread blocks on the read end, the signal - // handler writes when it enqueues new commands. - int mAvailMsg[2]; - - // The commands. - CommandEntry mCommands[COMMAND_QUEUE_SIZE]; - - // -------------------------------------------------- - // Singleton. - // -------------------------------------------------- - - static Mutex mInstanceLock; - static SignalHandler* mInstance; -}; - -// ---------------------------------------------------------------------- - -}; // namespace android - -#endif // ANDROID_SIGNAL_HANDLER_H diff --git a/cmds/runtime/main_runtime.cpp b/cmds/runtime/main_runtime.cpp deleted file mode 100644 index 83cb533..0000000 --- a/cmds/runtime/main_runtime.cpp +++ /dev/null @@ -1,516 +0,0 @@ -// -// Copyright 2005 The Android Open Source Project -// -// Main entry point for runtime. -// - -#include "ServiceManager.h" -#include "SignalHandler.h" - -#include <utils/threads.h> -#include <utils/Errors.h> - -#include <binder/IPCThreadState.h> -#include <binder/ProcessState.h> -#include <utils/Log.h> -#include <cutils/zygote.h> - -#include <cutils/properties.h> - -#include <private/utils/Static.h> - -#include <surfaceflinger/ISurfaceComposer.h> - -#include <android_runtime/AndroidRuntime.h> - -#include <stdlib.h> -#include <unistd.h> -#include <fcntl.h> -#include <stdio.h> -#include <string.h> -#include <getopt.h> -#include <signal.h> -#include <errno.h> -#include <sys/stat.h> -#include <linux/capability.h> -#include <linux/ioctl.h> -#ifdef HAVE_ANDROID_OS -# include <linux/android_alarm.h> -#endif - -#undef LOG_TAG -#define LOG_TAG "runtime" - -static const char* ZYGOTE_ARGV[] = { - "--setuid=1000", - "--setgid=1000", - "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,3001,3002,3003", - /* CAP_SYS_TTY_CONFIG & CAP_SYS_RESOURCE & CAP_NET_BROADCAST & - * CAP_NET_ADMIN & CAP_NET_RAW & CAP_NET_BIND_SERVICE & CAP_KILL & - * CAP_SYS_BOOT CAP_SYS_NICE - */ - "--capabilities=96549920,96549920", - "--runtime-init", - "--nice-name=system_server", - "com.android.server.SystemServer" -}; - -using namespace android; - -extern "C" status_t system_init(); - -enum { - SYSTEM_PROCESS_TAG = DEFAULT_PROCESS_TAG+1 -}; - -extern Mutex gEventQMutex; -extern Condition gEventQCondition; - -namespace android { - -extern status_t app_init(const char* className); -extern void set_finish_init_func(void (*func)()); - - -/** - * This class is used to kill this process (runtime) when the system_server dies. - */ -class GrimReaper : public IBinder::DeathRecipient { -public: - GrimReaper() { } - - virtual void binderDied(const wp<IBinder>& who) - { - LOGI("Grim Reaper killing runtime..."); - kill(getpid(), SIGKILL); - } -}; - -extern void QuickTests(); - -/* - * Print usage info. - */ -static void usage(const char* argv0) -{ - fprintf(stderr, - "Usage: runtime [-g gamma] [-l logfile] [-n] [-s]\n" - " [-j app-component] [-v app-verb] [-d app-data]\n" - "\n" - "-l: File to send log messages to\n" - "-n: Don't print to stdout/stderr\n" - "-s: Force single-process mode\n" - "-j: Custom home app component name\n" - "-v: Custom home app intent verb\n" - "-d: Custom home app intent data\n" - ); - exit(1); -} - -// Selected application to run. -static const char* gInitialApplication = NULL; -static const char* gInitialVerb = NULL; -static const char* gInitialData = NULL; - -static void writeStringToParcel(Parcel& parcel, const char* str) -{ - if (str) { - parcel.writeString16(String16(str)); - } else { - parcel.writeString16(NULL, 0); - } -} - -/* - * Starting point for program logic. - * - * Returns with an exit status code (0 on success, nonzero on error). - */ -static int run(sp<ProcessState>& proc) -{ - // Temporary hack to call startRunning() on the activity manager. - sp<IServiceManager> sm = defaultServiceManager(); - sp<IBinder> am; - while ((am = sm->getService(String16("activity"))) == NULL) { - LOGI("Waiting for activity manager..."); - } - Parcel data, reply; - // XXX Need to also supply a package name for this to work again. - // IActivityManager::getInterfaceDescriptor() is the token for invoking on this interface; - // hardcoding it here avoids having to link with the full Activity Manager library - data.writeInterfaceToken(String16("android.app.IActivityManager")); - writeStringToParcel(data, NULL); - writeStringToParcel(data, gInitialApplication); - writeStringToParcel(data, gInitialVerb); - writeStringToParcel(data, gInitialData); -LOGI("run() sending FIRST_CALL_TRANSACTION to activity manager"); - am->transact(IBinder::FIRST_CALL_TRANSACTION, data, &reply); - - if (proc->supportsProcesses()) { - // Now we link to the Activity Manager waiting for it to die. If it does kill ourself. - // initd will restart this process and bring the system back up. - sp<GrimReaper> grim = new GrimReaper(); - am->linkToDeath(grim, grim.get(), 0); - - // Now join the thread pool. Note this is needed so that the message enqueued in the driver - // for the linkToDeath gets processed. - IPCThreadState::self()->joinThreadPool(); - } else { - // Keep this thread running forever... - while (1) { - usleep(100000); - } - } - return 1; -} - - -}; // namespace android - - -/* - * Post-system-process initialization. - * - * This function continues initialization after the system process - * has been initialized. It needs to be separate because the system - * initialization needs to care of starting the Android runtime if it is not - * running in its own process, which doesn't return until the runtime is - * being shut down. So it will call back to here from inside of Dalvik, - * to allow us to continue booting up. - */ -static void finish_system_init(sp<ProcessState>& proc) -{ - // If we are running multiprocess, we now need to have the - // thread pool started here. We don't do this in boot_init() - // because when running single process we need to start the - // thread pool after the Android runtime has been started (so - // the pool uses Dalvik threads). - if (proc->supportsProcesses()) { - proc->startThreadPool(); - } -} - - -// This function can be used to enforce security to different -// root contexts. For now, we just give every access. -static bool contextChecker( - const String16& name, const sp<IBinder>& caller, void* userData) -{ - return true; -} - -/* - * Initialization of boot services. - * - * This is where we perform initialization of all of our low-level - * boot services. Most importantly, here we become the context - * manager and use that to publish the service manager that will provide - * access to all other services. - */ -static void boot_init() -{ - LOGI("Entered boot_init()!\n"); - - sp<ProcessState> proc(ProcessState::self()); - LOGD("ProcessState: %p\n", proc.get()); - proc->becomeContextManager(contextChecker, NULL); - - if (proc->supportsProcesses()) { - LOGI("Binder driver opened. Multiprocess enabled.\n"); - } else { - LOGI("Binder driver not found. Processes not supported.\n"); - } - - sp<BServiceManager> sm = new BServiceManager; - proc->setContextObject(sm); -} - -/* - * Redirect stdin/stdout/stderr to /dev/null. - */ -static void redirectStdFds(void) -{ - int fd = open("/dev/null", O_RDWR, 0); - if (fd < 0) { - LOGW("Unable to open /dev/null: %s\n", strerror(errno)); - } else { - dup2(fd, 0); - dup2(fd, 1); - dup2(fd, 2); - close(fd); - } -} - -static int hasDir(const char* dir) -{ - struct stat s; - int res = stat(dir, &s); - if (res == 0) { - return S_ISDIR(s.st_mode); - } - return 0; -} - -static void validateTime() -{ -#if HAVE_ANDROID_OS - int fd; - int res; - time_t min_time = 1167652800; // jan 1 2007, type 'date -ud "1/1 12:00" +%s' to get value for current year - struct timespec ts; - - fd = open("/dev/alarm", O_RDWR); - if(fd < 0) { - LOGW("Unable to open alarm driver: %s\n", strerror(errno)); - return; - } - res = ioctl(fd, ANDROID_ALARM_GET_TIME(ANDROID_ALARM_RTC_WAKEUP), &ts); - if(res < 0) { - LOGW("Unable to read rtc, %s\n", strerror(errno)); - } - else if(ts.tv_sec >= min_time) { - goto done; - } - LOGW("Invalid time detected, %ld set to %ld\n", ts.tv_sec, min_time); - ts.tv_sec = min_time; - ts.tv_nsec = 0; - res = ioctl(fd, ANDROID_ALARM_SET_RTC, &ts); - if(res < 0) { - LOGW("Unable to set rtc to %ld: %s\n", ts.tv_sec, strerror(errno)); - } -done: - close(fd); -#endif -} - -#ifndef HAVE_ANDROID_OS -class QuickRuntime : public AndroidRuntime -{ -public: - QuickRuntime() {} - - virtual void onStarted() - { - printf("QuickRuntime: onStarted\n"); - } -}; -#endif - -static status_t start_process(const char* name); - -static void restart_me(pid_t child, void* userData) -{ - start_process((const char*)userData); -} - -static status_t start_process(const char* name) -{ - String8 path(name); - Vector<const char*> args; - String8 leaf(path.getPathLeaf()); - String8 parentDir(path.getPathDir()); - args.insertAt(leaf.string(), 0); - args.add(parentDir.string()); - args.add(NULL); - pid_t child = fork(); - if (child < 0) { - status_t err = errno; - LOGE("*** fork of child %s failed: %s", leaf.string(), strerror(err)); - return -errno; - } else if (child == 0) { - LOGI("Executing: %s", path.string()); - execv(path.string(), const_cast<char**>(args.array())); - int err = errno; - LOGE("Exec failed: %s\n", strerror(err)); - _exit(err); - } else { - SignalHandler::setChildHandler(child, DEFAULT_PROCESS_TAG, - restart_me, (void*)name); - } - return -errno; -} - -/* - * Application entry point. - * - * Parse arguments, set some values, and pass control off to Run(). - * - * This is redefined to "SDL_main" on SDL simulator builds, and - * "runtime_main" on wxWidgets builds. - */ -extern "C" -int main(int argc, char* const argv[]) -{ - bool singleProcess = false; - const char* logFile = NULL; - int ic; - int result = 1; - pid_t systemPid; - - sp<ProcessState> proc; - -#ifndef HAVE_ANDROID_OS - /* Set stdout/stderr to unbuffered for MinGW/MSYS. */ - //setvbuf(stdout, NULL, _IONBF, 0); - //setvbuf(stderr, NULL, _IONBF, 0); - - LOGI("commandline args:\n"); - for (int i = 0; i < argc; i++) - LOGI(" %2d: '%s'\n", i, argv[i]); -#endif - - while (1) { - ic = getopt(argc, argv, "g:j:v:d:l:ns"); - if (ic < 0) - break; - - switch (ic) { - case 'g': - break; - case 'j': - gInitialApplication = optarg; - break; - case 'v': - gInitialVerb = optarg; - break; - case 'd': - gInitialData = optarg; - break; - case 'l': - logFile = optarg; - break; - case 'n': - redirectStdFds(); - break; - case 's': - singleProcess = true; - break; - case '?': - default: - LOGE("runtime: unrecognized flag -%c\n", ic); - usage(argv[0]); - break; - } - } - if (optind < argc) { - LOGE("runtime: extra stuff: %s\n", argv[optind]); - usage(argv[0]); - } - - if (singleProcess) { - ProcessState::setSingleProcess(true); - } - - if (logFile != NULL) { - android_logToFile(NULL, logFile); - } - - /* - * Set up ANDROID_* environment variables. - * - * TODO: the use of $ANDROID_PRODUCT_OUT will go away soon. - */ - static const char* kSystemDir = "/system"; - static const char* kDataDir = "/data"; - static const char* kAppSubdir = "/app"; - const char* out = NULL; -#ifndef HAVE_ANDROID_OS - //out = getenv("ANDROID_PRODUCT_OUT"); -#endif - if (out == NULL) - out = ""; - - char* systemDir = (char*) malloc(strlen(out) + strlen(kSystemDir) +1); - char* dataDir = (char*) malloc(strlen(out) + strlen(kDataDir) +1); - - sprintf(systemDir, "%s%s", out, kSystemDir); - sprintf(dataDir, "%s%s", out, kDataDir); - setenv("ANDROID_ROOT", systemDir, 1); - setenv("ANDROID_DATA", dataDir, 1); - - char* assetDir = (char*) malloc(strlen(systemDir) + strlen(kAppSubdir) +1); - sprintf(assetDir, "%s%s", systemDir, kAppSubdir); - - LOGI("Startup: sys='%s' asset='%s' data='%s'\n", - systemDir, assetDir, dataDir); - free(systemDir); - free(dataDir); - -#ifdef HAVE_ANDROID_OS - /* set up a process group for easier killing on the device */ - setpgid(0, getpid()); -#endif - - // Change to asset dir. This is only necessary if we've changed to - // a different directory, but there's little harm in doing it regardless. - // - // Expecting assets to live in the current dir is not a great idea, - // because some of our code or one of our libraries could change the - // directory out from under us. Preserve the behavior for now. - if (chdir(assetDir) != 0) { - LOGW("WARNING: could not change dir to '%s': %s\n", - assetDir, strerror(errno)); - } - free(assetDir); - -#if 0 - // Hack to keep libc from beating the filesystem to death. It's - // hitting /etc/localtime frequently, - // - // This statement locks us into Pacific time. We could do better, - // but there's not much point until we're sure that the library - // can't be changed to do more along the lines of what we want. -#ifndef XP_WIN - setenv("TZ", "PST+8PDT,M4.1.0/2,M10.5.0/2", true); -#endif -#endif - - /* track our progress through the boot sequence */ - const int LOG_BOOT_PROGRESS_START = 3000; - LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, - ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); - - validateTime(); - - proc = ProcessState::self(); - - boot_init(); - - /* If we are in multiprocess mode, have zygote spawn the system - * server process and call system_init(). If we are running in - * single process mode just call system_init() directly. - */ - if (proc->supportsProcesses()) { - // If stdio logging is on, system_server should not inherit our stdio - // The dalvikvm instance will copy stdio to the log on its own - char propBuf[PROPERTY_VALUE_MAX]; - bool logStdio = false; - property_get("log.redirect-stdio", propBuf, ""); - logStdio = (strcmp(propBuf, "true") == 0); - - zygote_run_oneshot((int)(!logStdio), - sizeof(ZYGOTE_ARGV) / sizeof(ZYGOTE_ARGV[0]), - ZYGOTE_ARGV); - - //start_process("/system/bin/mediaserver"); - - } else { -#ifndef HAVE_ANDROID_OS - QuickRuntime* runt = new QuickRuntime(); - runt->start("com/android/server/SystemServer", - false /* spontaneously fork system server from zygote */); -#endif - } - - //printf("+++ post-zygote\n"); - - finish_system_init(proc); - run(proc); - -bail: - if (proc != NULL) { - proc->setContextObject(NULL); - } - - return 0; -} diff --git a/cmds/screencap/Android.mk b/cmds/screencap/Android.mk index 400a36b..ca8008b 100644 --- a/cmds/screencap/Android.mk +++ b/cmds/screencap/Android.mk @@ -10,7 +10,7 @@ LOCAL_SHARED_LIBRARIES := \ libbinder \ libskia \ libui \ - libsurfaceflinger_client + libgui LOCAL_MODULE:= screencap diff --git a/cmds/screenshot/Android.mk b/cmds/screenshot/Android.mk index 99c7aeb..73a8e22 100644 --- a/cmds/screenshot/Android.mk +++ b/cmds/screenshot/Android.mk @@ -1,5 +1,3 @@ -ifneq ($(TARGET_SIMULATOR),true) - LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) @@ -12,5 +10,3 @@ LOCAL_STATIC_LIBRARIES := libpng LOCAL_C_INCLUDES += external/zlib include $(BUILD_EXECUTABLE) - -endif diff --git a/cmds/sensorservice/Android.mk b/cmds/sensorservice/Android.mk new file mode 100644 index 0000000..0811be5 --- /dev/null +++ b/cmds/sensorservice/Android.mk @@ -0,0 +1,19 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + main_sensorservice.cpp + +LOCAL_SHARED_LIBRARIES := \ + libsensorservice \ + libbinder \ + libutils + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/../../services/sensorservice + +LOCAL_MODULE_TAGS := optional + +LOCAL_MODULE:= sensorservice + +include $(BUILD_EXECUTABLE) diff --git a/cmds/sensorservice/main_sensorservice.cpp b/cmds/sensorservice/main_sensorservice.cpp new file mode 100644 index 0000000..8610627 --- /dev/null +++ b/cmds/sensorservice/main_sensorservice.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2011 The Android Open Source 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 <binder/BinderService.h> +#include <SensorService.h> + +using namespace android; + +int main(int argc, char** argv) { + SensorService::publishAndJoinThreadPool(); + return 0; +} diff --git a/cmds/servicemanager/Android.mk b/cmds/servicemanager/Android.mk index fe5929b..ea80c7d 100644 --- a/cmds/servicemanager/Android.mk +++ b/cmds/servicemanager/Android.mk @@ -1,4 +1,3 @@ -ifneq ($(TARGET_SIMULATOR),true) LOCAL_PATH:= $(call my-dir) #include $(CLEAR_VARS) @@ -14,4 +13,3 @@ ifeq ($(BOARD_USE_LVMX),true) LOCAL_CFLAGS += -DLVMX endif include $(BUILD_EXECUTABLE) -endif diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk index 1b13dd9..e9642f7 100644 --- a/cmds/stagefright/Android.mk +++ b/cmds/stagefright/Android.mk @@ -8,7 +8,7 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES := \ libstagefright libmedia libutils libbinder libstagefright_foundation \ - libskia libsurfaceflinger_client libgui + libskia libgui LOCAL_C_INCLUDES:= \ $(JNI_H_INCLUDE) \ @@ -107,7 +107,7 @@ LOCAL_SRC_FILES:= \ stream.cpp \ LOCAL_SHARED_LIBRARIES := \ - libstagefright liblog libutils libbinder libsurfaceflinger_client \ + libstagefright liblog libutils libbinder libgui \ libstagefright_foundation libmedia LOCAL_C_INCLUDES:= \ @@ -132,7 +132,7 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES := \ libstagefright liblog libutils libbinder libstagefright_foundation \ - libmedia libsurfaceflinger_client libcutils libui + libmedia libgui libcutils libui LOCAL_C_INCLUDES:= \ $(JNI_H_INCLUDE) \ diff --git a/cmds/stagefright/audioloop.cpp b/cmds/stagefright/audioloop.cpp index 8733662..858681f 100644 --- a/cmds/stagefright/audioloop.cpp +++ b/cmds/stagefright/audioloop.cpp @@ -11,6 +11,8 @@ #include <media/stagefright/OMXClient.h> #include <media/stagefright/OMXCodec.h> +#include <system/audio.h> + using namespace android; int main() { @@ -31,8 +33,8 @@ int main() { AUDIO_SOURCE_DEFAULT, kSampleRate, kNumChannels == 1 - ? AudioSystem::CHANNEL_IN_MONO - : AudioSystem::CHANNEL_IN_STEREO); + ? AUDIO_CHANNEL_IN_MONO + : AUDIO_CHANNEL_IN_STEREO); #endif sp<MetaData> meta = new MetaData; diff --git a/cmds/stagefright/recordvideo.cpp b/cmds/stagefright/recordvideo.cpp index 1264215..c402286 100644 --- a/cmds/stagefright/recordvideo.cpp +++ b/cmds/stagefright/recordvideo.cpp @@ -34,7 +34,7 @@ static void usage(const char *me) { fprintf(stderr, "usage: %s\n", me); fprintf(stderr, " -h(elp)\n"); fprintf(stderr, " -b bit rate in bits per second (default: 300000)\n"); - fprintf(stderr, " -c YUV420 color format: [0] semi planar or [1] planar (default: 1)\n"); + fprintf(stderr, " -c YUV420 color format: [0] semi planar or [1] planar or other omx YUV420 color format (default: 1)\n"); fprintf(stderr, " -f frame rate in frames per second (default: 30)\n"); fprintf(stderr, " -i I frame interval in seconds (default: 1)\n"); fprintf(stderr, " -n number of frames to be recorded (default: 300)\n"); @@ -59,11 +59,6 @@ public: mSize((width * height * 3) / 2) { mGroup.add_buffer(new MediaBuffer(mSize)); - - // Check the color format to make sure - // that the buffer size mSize it set correctly above. - CHECK(colorFormat == OMX_COLOR_FormatYUV420SemiPlanar || - colorFormat == OMX_COLOR_FormatYUV420Planar); } virtual sp<MetaData> getFormat() { @@ -144,9 +139,13 @@ static int translateColorToOmxEnumValue(int color) { case kYUV420P: return OMX_COLOR_FormatYUV420Planar; default: - fprintf(stderr, "Unsupported color: %d\n", color); - return -1; + fprintf(stderr, "Custom OMX color format: %d\n", color); + if (color == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar || + color == OMX_QCOM_COLOR_FormatYVU420SemiPlanar) { + return color; + } } + return -1; } int main(int argc, char **argv) { diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp index c1d0803..6fa66cf 100644 --- a/cmds/stagefright/sf2.cpp +++ b/cmds/stagefright/sf2.cpp @@ -29,6 +29,7 @@ #include <media/stagefright/MediaExtractor.h> #include <media/stagefright/MediaSource.h> #include <media/stagefright/MetaData.h> +#include <media/stagefright/NativeWindowWrapper.h> #include <media/stagefright/Utils.h> #include <surfaceflinger/ISurfaceComposer.h> @@ -39,10 +40,12 @@ using namespace android; struct Controller : public AHandler { - Controller(const char *uri, bool decodeAudio, const sp<Surface> &surface) + Controller(const char *uri, bool decodeAudio, + const sp<Surface> &surface, bool renderToSurface) : mURI(uri), mDecodeAudio(decodeAudio), mSurface(surface), + mRenderToSurface(renderToSurface), mCodec(new ACodec) { CHECK(!mDecodeAudio || mSurface == NULL); } @@ -97,7 +100,8 @@ protected: sp<AMessage> format = makeFormat(mSource->getFormat()); if (mSurface != NULL) { - format->setObject("surface", mSurface); + format->setObject( + "native-window", new NativeWindowWrapper(mSurface)); } mCodec->initiateSetup(format); @@ -220,6 +224,7 @@ private: AString mURI; bool mDecodeAudio; sp<Surface> mSurface; + bool mRenderToSurface; sp<ACodec> mCodec; sp<MediaSource> mSource; @@ -451,7 +456,7 @@ private: inBuffer->release(); inBuffer = NULL; - // break; // Don't coalesce + break; // Don't coalesce } LOGV("coalesced %d input buffers", n); @@ -479,6 +484,10 @@ private: sp<AMessage> reply; CHECK(msg->findMessage("reply", &reply)); + if (mRenderToSurface) { + reply->setInt32("render", 1); + } + reply->post(); } @@ -491,7 +500,8 @@ static void usage(const char *me) { fprintf(stderr, " -a(udio)\n"); fprintf(stderr, - " -s(surface) Allocate output buffers on a surface.\n"); + " -S(urface) Allocate output buffers on a surface.\n" + " -R(ender) Render surface-allocated buffers.\n"); } int main(int argc, char **argv) { @@ -499,18 +509,23 @@ int main(int argc, char **argv) { bool decodeAudio = false; bool useSurface = false; + bool renderToSurface = false; int res; - while ((res = getopt(argc, argv, "has")) >= 0) { + while ((res = getopt(argc, argv, "haSR")) >= 0) { switch (res) { case 'a': decodeAudio = true; break; - case 's': + case 'S': useSurface = true; break; + case 'R': + renderToSurface = true; + break; + case '?': case 'h': default: @@ -543,7 +558,6 @@ int main(int argc, char **argv) { CHECK_EQ(composerClient->initCheck(), (status_t)OK); control = composerClient->createSurface( - getpid(), String8("A Surface"), 0, 1280, @@ -554,16 +568,18 @@ int main(int argc, char **argv) { CHECK(control != NULL); CHECK(control->isValid()); - CHECK_EQ(composerClient->openTransaction(), (status_t)OK); + SurfaceComposerClient::openGlobalTransaction(); CHECK_EQ(control->setLayer(30000), (status_t)OK); CHECK_EQ(control->show(), (status_t)OK); - CHECK_EQ(composerClient->closeTransaction(), (status_t)OK); + SurfaceComposerClient::closeGlobalTransaction(); surface = control->getSurface(); CHECK(surface != NULL); } - sp<Controller> controller = new Controller(argv[0], decodeAudio, surface); + sp<Controller> controller = + new Controller(argv[0], decodeAudio, surface, renderToSurface); + looper->registerHandler(controller); controller->startAsync(); diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index a875c3a..34f0a64 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -66,6 +66,7 @@ static long gNumRepetitions; static long gMaxNumFrames; // 0 means decode all available. static long gReproduceBug; // if not -1. static bool gPreferSoftwareCodec; +static bool gForceToUseHardwareCodec; static bool gPlaybackAudio; static bool gWriteMP4; static bool gDisplayHistogram; @@ -73,8 +74,6 @@ static String8 gWriteMP4Filename; static sp<ANativeWindow> gSurface; -#define USE_SURFACE_COMPOSER 0 - static int64_t getNowUs() { struct timeval tv; gettimeofday(&tv, NULL); @@ -144,10 +143,18 @@ static void playSource(OMXClient *client, sp<MediaSource> &source) { if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_RAW, mime)) { rawSource = source; } else { + int flags = 0; + if (gPreferSoftwareCodec) { + flags |= OMXCodec::kPreferSoftwareCodecs; + } + if (gForceToUseHardwareCodec) { + CHECK(!gPreferSoftwareCodec); + flags |= OMXCodec::kHardwareCodecsOnly; + } rawSource = OMXCodec::Create( client->interface(), meta, false /* createEncoder */, source, NULL /* matchComponentName */, - gPreferSoftwareCodec ? OMXCodec::kPreferSoftwareCodecs : 0, + flags, gSurface); if (rawSource == NULL) { @@ -379,13 +386,15 @@ private: sp<MediaSource> mSource; StreamType mStreamType; + bool mSawFirstIDRFrame; DISALLOW_EVIL_CONSTRUCTORS(DetectSyncSource); }; DetectSyncSource::DetectSyncSource(const sp<MediaSource> &source) : mSource(source), - mStreamType(OTHER) { + mStreamType(OTHER), + mSawFirstIDRFrame(false) { const char *mime; CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime)); @@ -401,6 +410,8 @@ DetectSyncSource::DetectSyncSource(const sp<MediaSource> &source) } status_t DetectSyncSource::start(MetaData *params) { + mSawFirstIDRFrame = false; + return mSource->start(params); } @@ -430,16 +441,30 @@ static bool isIDRFrame(MediaBuffer *buffer) { status_t DetectSyncSource::read( MediaBuffer **buffer, const ReadOptions *options) { - status_t err = mSource->read(buffer, options); + for (;;) { + status_t err = mSource->read(buffer, options); - if (err != OK) { - return err; - } + if (err != OK) { + return err; + } - if (mStreamType == AVC && isIDRFrame(*buffer)) { - (*buffer)->meta_data()->setInt32(kKeyIsSyncFrame, true); - } else { - (*buffer)->meta_data()->setInt32(kKeyIsSyncFrame, true); + if (mStreamType == AVC) { + bool isIDR = isIDRFrame(*buffer); + (*buffer)->meta_data()->setInt32(kKeyIsSyncFrame, isIDR); + if (isIDR) { + mSawFirstIDRFrame = true; + } + } else { + (*buffer)->meta_data()->setInt32(kKeyIsSyncFrame, true); + } + + if (mStreamType != AVC || mSawFirstIDRFrame) { + break; + } + + // Ignore everything up to the first IDR frame. + (*buffer)->release(); + *buffer = NULL; } return OK; @@ -545,12 +570,14 @@ static void usage(const char *me) { fprintf(stderr, " -p(rofiles) dump decoder profiles supported\n"); fprintf(stderr, " -t(humbnail) extract video thumbnail or album art\n"); fprintf(stderr, " -s(oftware) prefer software codec\n"); + fprintf(stderr, " -r(hardware) force to use hardware codec\n"); fprintf(stderr, " -o playback audio\n"); fprintf(stderr, " -w(rite) filename (write to .mp4 file)\n"); fprintf(stderr, " -k seek test\n"); fprintf(stderr, " -x display a histogram of decoding times/fps " "(video only)\n"); fprintf(stderr, " -S allocate buffers from a surface\n"); + fprintf(stderr, " -T allocate buffers from a surface texture\n"); } int main(int argc, char **argv) { @@ -562,10 +589,12 @@ int main(int argc, char **argv) { bool extractThumbnail = false; bool seekTest = false; bool useSurfaceAlloc = false; + bool useSurfaceTexAlloc = false; gNumRepetitions = 1; gMaxNumFrames = 0; gReproduceBug = -1; gPreferSoftwareCodec = false; + gForceToUseHardwareCodec = false; gPlaybackAudio = false; gWriteMP4 = false; gDisplayHistogram = false; @@ -575,7 +604,7 @@ int main(int argc, char **argv) { sp<LiveSession> liveSession; int res; - while ((res = getopt(argc, argv, "han:lm:b:ptsow:kxS")) >= 0) { + while ((res = getopt(argc, argv, "han:lm:b:ptsrow:kxST")) >= 0) { switch (res) { case 'a': { @@ -636,6 +665,12 @@ int main(int argc, char **argv) { break; } + case 'r': + { + gForceToUseHardwareCodec = true; + break; + } + case 'o': { gPlaybackAudio = true; @@ -660,6 +695,12 @@ int main(int argc, char **argv) { break; } + case 'T': + { + useSurfaceTexAlloc = true; + break; + } + case '?': case 'h': default: @@ -695,12 +736,14 @@ int main(int argc, char **argv) { for (int k = 0; k < argc; ++k) { const char *filename = argv[k]; + bool failed = true; CHECK_EQ(retriever->setDataSource(filename), (status_t)OK); sp<IMemory> mem = retriever->getFrameAtTime(-1, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); if (mem != NULL) { + failed = false; printf("getFrameAtTime(%s) => OK\n", filename); VideoFrame *frame = (VideoFrame *)mem->pointer(); @@ -715,16 +758,21 @@ int main(int argc, char **argv) { "/sdcard/out.jpg", bitmap, SkImageEncoder::kJPEG_Type, SkImageEncoder::kDefaultQuality)); - } else { + } + + { mem = retriever->extractAlbumArt(); if (mem != NULL) { + failed = false; printf("extractAlbumArt(%s) => OK\n", filename); - } else { - printf("both getFrameAtTime and extractAlbumArt " - "failed on file '%s'.\n", filename); } } + + if (failed) { + printf("both getFrameAtTime and extractAlbumArt " + "failed on file '%s'.\n", filename); + } } return 0; @@ -745,7 +793,9 @@ int main(int argc, char **argv) { MEDIA_MIMETYPE_VIDEO_AVC, MEDIA_MIMETYPE_VIDEO_MPEG4, MEDIA_MIMETYPE_VIDEO_H263, MEDIA_MIMETYPE_AUDIO_AAC, MEDIA_MIMETYPE_AUDIO_AMR_NB, MEDIA_MIMETYPE_AUDIO_AMR_WB, - MEDIA_MIMETYPE_AUDIO_MPEG + MEDIA_MIMETYPE_AUDIO_MPEG, MEDIA_MIMETYPE_AUDIO_G711_MLAW, + MEDIA_MIMETYPE_AUDIO_G711_ALAW, MEDIA_MIMETYPE_AUDIO_VORBIS, + MEDIA_MIMETYPE_VIDEO_VPX }; for (size_t k = 0; k < sizeof(kMimeTypes) / sizeof(kMimeTypes[0]); @@ -753,6 +803,7 @@ int main(int argc, char **argv) { printf("type '%s':\n", kMimeTypes[k]); Vector<CodecCapabilities> results; + // will retrieve hardware and software codecs CHECK_EQ(QueryCodecs(omx, kMimeTypes[k], true, // queryDecoders &results), (status_t)OK); @@ -794,41 +845,47 @@ int main(int argc, char **argv) { for (List<IOMX::ComponentInfo>::iterator it = list.begin(); it != list.end(); ++it) { - printf("%s\n", (*it).mName.string()); + printf("%s\t Roles: ", (*it).mName.string()); + for (List<String8>::iterator itRoles = (*it).mRoles.begin() ; + itRoles != (*it).mRoles.end() ; ++itRoles) { + printf("%s\t", (*itRoles).string()); + } + printf("\n"); } } sp<SurfaceComposerClient> composerClient; sp<SurfaceControl> control; - if (useSurfaceAlloc && !audioOnly) { -#if USE_SURFACE_COMPOSER - composerClient = new SurfaceComposerClient; - CHECK_EQ(composerClient->initCheck(), (status_t)OK); - - control = composerClient->createSurface( - getpid(), - String8("A Surface"), - 0, - 1280, - 800, - PIXEL_FORMAT_RGB_565, - 0); - - CHECK(control != NULL); - CHECK(control->isValid()); - - CHECK_EQ(composerClient->openTransaction(), (status_t)OK); - CHECK_EQ(control->setLayer(30000), (status_t)OK); - CHECK_EQ(control->show(), (status_t)OK); - CHECK_EQ(composerClient->closeTransaction(), (status_t)OK); - - gSurface = control->getSurface(); - CHECK(gSurface != NULL); -#else - sp<SurfaceTexture> texture = new SurfaceTexture(0 /* tex */); - gSurface = new SurfaceTextureClient(texture); -#endif + if ((useSurfaceAlloc || useSurfaceTexAlloc) && !audioOnly) { + if (useSurfaceAlloc) { + composerClient = new SurfaceComposerClient; + CHECK_EQ(composerClient->initCheck(), (status_t)OK); + + control = composerClient->createSurface( + String8("A Surface"), + 0, + 1280, + 800, + PIXEL_FORMAT_RGB_565, + 0); + + CHECK(control != NULL); + CHECK(control->isValid()); + + SurfaceComposerClient::openGlobalTransaction(); + CHECK_EQ(control->setLayer(30000), (status_t)OK); + CHECK_EQ(control->show(), (status_t)OK); + SurfaceComposerClient::closeGlobalTransaction(); + + gSurface = control->getSurface(); + CHECK(gSurface != NULL); + } else { + CHECK(useSurfaceTexAlloc); + + sp<SurfaceTexture> texture = new SurfaceTexture(0 /* tex */); + gSurface = new SurfaceTextureClient(texture); + } } DataSource::RegisterDefaultSniffers(); @@ -921,6 +978,17 @@ int main(int argc, char **argv) { fprintf(stderr, "could not create extractor.\n"); return -1; } + + sp<MetaData> meta = extractor->getMetaData(); + + if (meta != NULL) { + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + + if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) { + syncInfoPresent = false; + } + } } size_t numTracks = extractor->countTracks(); @@ -1008,12 +1076,12 @@ int main(int argc, char **argv) { } } - if (useSurfaceAlloc && !audioOnly) { + if ((useSurfaceAlloc || useSurfaceTexAlloc) && !audioOnly) { gSurface.clear(); -#if USE_SURFACE_COMPOSER - composerClient->dispose(); -#endif + if (useSurfaceAlloc) { + composerClient->dispose(); + } } client.disconnect(); diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp index bb84bd1..b13236a 100644 --- a/cmds/stagefright/stream.cpp +++ b/cmds/stagefright/stream.cpp @@ -14,12 +14,21 @@ * limitations under the License. */ +//#define LOG_NDEBUG 0 +#define LOG_TAG "stream" +#include "utils/Log.h" + #include <binder/ProcessState.h> #include <media/IStreamSource.h> #include <media/mediaplayer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/DataSource.h> +#include <media/stagefright/MPEG2TSWriter.h> +#include <media/stagefright/MediaExtractor.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MetaData.h> #include <binder/IServiceManager.h> #include <media/IMediaPlayerService.h> @@ -31,7 +40,7 @@ using namespace android; struct MyStreamSource : public BnStreamSource { - // Caller retains ownership of fd. + // Object assumes ownership of fd. MyStreamSource(int fd); virtual void setListener(const sp<IStreamListener> &listener); @@ -45,7 +54,7 @@ protected: private: int mFd; off64_t mFileSize; - int64_t mNextSeekTimeUs; + uint64_t mNumPacketsSent; sp<IStreamListener> mListener; Vector<sp<IMemory> > mBuffers; @@ -56,7 +65,7 @@ private: MyStreamSource::MyStreamSource(int fd) : mFd(fd), mFileSize(0), - mNextSeekTimeUs(-1) { // ALooper::GetNowUs() + 5000000ll) { + mNumPacketsSent(0) { CHECK_GE(fd, 0); mFileSize = lseek64(fd, 0, SEEK_END); @@ -64,6 +73,8 @@ MyStreamSource::MyStreamSource(int fd) } MyStreamSource::~MyStreamSource() { + close(mFd); + mFd = -1; } void MyStreamSource::setListener(const sp<IStreamListener> &listener) { @@ -77,18 +88,24 @@ void MyStreamSource::setBuffers(const Vector<sp<IMemory> > &buffers) { void MyStreamSource::onBufferAvailable(size_t index) { CHECK_LT(index, mBuffers.size()); - if (mNextSeekTimeUs >= 0 && mNextSeekTimeUs <= ALooper::GetNowUs()) { - off64_t offset = (off64_t)(((float)rand() / RAND_MAX) * mFileSize * 0.8); - offset = (offset / 188) * 188; +#if 0 + if (mNumPacketsSent >= 20000) { + LOGI("signalling discontinuity now"); + + off64_t offset = 0; + CHECK((offset % 188) == 0); lseek(mFd, offset, SEEK_SET); + sp<AMessage> extra = new AMessage; + extra->setInt32(IStreamListener::kKeyFormatChange, 0); + mListener->issueCommand( - IStreamListener::DISCONTINUITY, false /* synchronous */); + IStreamListener::DISCONTINUITY, false /* synchronous */, extra); - mNextSeekTimeUs = -1; - mNextSeekTimeUs = ALooper::GetNowUs() + 5000000ll; + mNumPacketsSent = 0; } +#endif sp<IMemory> mem = mBuffers.itemAt(index); @@ -97,6 +114,145 @@ void MyStreamSource::onBufferAvailable(size_t index) { mListener->issueCommand(IStreamListener::EOS, false /* synchronous */); } else { mListener->queueBuffer(index, n); + + mNumPacketsSent += n / 188; + } +} +//////////////////////////////////////////////////////////////////////////////// + +struct MyConvertingStreamSource : public BnStreamSource { + MyConvertingStreamSource(const char *filename); + + virtual void setListener(const sp<IStreamListener> &listener); + virtual void setBuffers(const Vector<sp<IMemory> > &buffers); + + virtual void onBufferAvailable(size_t index); + +protected: + virtual ~MyConvertingStreamSource(); + +private: + Mutex mLock; + Condition mCondition; + + sp<IStreamListener> mListener; + Vector<sp<IMemory> > mBuffers; + + sp<MPEG2TSWriter> mWriter; + + ssize_t mCurrentBufferIndex; + size_t mCurrentBufferOffset; + + List<size_t> mBufferQueue; + + static ssize_t WriteDataWrapper(void *me, const void *data, size_t size); + ssize_t writeData(const void *data, size_t size); + + DISALLOW_EVIL_CONSTRUCTORS(MyConvertingStreamSource); +}; + +//////////////////////////////////////////////////////////////////////////////// + +MyConvertingStreamSource::MyConvertingStreamSource(const char *filename) + : mCurrentBufferIndex(-1), + mCurrentBufferOffset(0) { + sp<DataSource> dataSource = DataSource::CreateFromURI(filename); + CHECK(dataSource != NULL); + + sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource); + CHECK(extractor != NULL); + + mWriter = new MPEG2TSWriter( + this, &MyConvertingStreamSource::WriteDataWrapper); + + for (size_t i = 0; i < extractor->countTracks(); ++i) { + const sp<MetaData> &meta = extractor->getTrackMetaData(i); + + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + + if (strncasecmp("video/", mime, 6) && strncasecmp("audio/", mime, 6)) { + continue; + } + + CHECK_EQ(mWriter->addSource(extractor->getTrack(i)), (status_t)OK); + } + + CHECK_EQ(mWriter->start(), (status_t)OK); +} + +MyConvertingStreamSource::~MyConvertingStreamSource() { +} + +void MyConvertingStreamSource::setListener( + const sp<IStreamListener> &listener) { + mListener = listener; +} + +void MyConvertingStreamSource::setBuffers( + const Vector<sp<IMemory> > &buffers) { + mBuffers = buffers; +} + +ssize_t MyConvertingStreamSource::WriteDataWrapper( + void *me, const void *data, size_t size) { + return static_cast<MyConvertingStreamSource *>(me)->writeData(data, size); +} + +ssize_t MyConvertingStreamSource::writeData(const void *data, size_t size) { + size_t totalWritten = 0; + + while (size > 0) { + Mutex::Autolock autoLock(mLock); + + if (mCurrentBufferIndex < 0) { + while (mBufferQueue.empty()) { + mCondition.wait(mLock); + } + + mCurrentBufferIndex = *mBufferQueue.begin(); + mCurrentBufferOffset = 0; + + mBufferQueue.erase(mBufferQueue.begin()); + } + + sp<IMemory> mem = mBuffers.itemAt(mCurrentBufferIndex); + + size_t copy = size; + if (copy + mCurrentBufferOffset > mem->size()) { + copy = mem->size() - mCurrentBufferOffset; + } + + memcpy((uint8_t *)mem->pointer() + mCurrentBufferOffset, data, copy); + mCurrentBufferOffset += copy; + + if (mCurrentBufferOffset == mem->size()) { + mListener->queueBuffer(mCurrentBufferIndex, mCurrentBufferOffset); + mCurrentBufferIndex = -1; + } + + data = (const uint8_t *)data + copy; + size -= copy; + + totalWritten += copy; + } + + return (ssize_t)totalWritten; +} + +void MyConvertingStreamSource::onBufferAvailable(size_t index) { + Mutex::Autolock autoLock(mLock); + + mBufferQueue.push_back(index); + mCondition.signal(); + + if (mWriter->reachedEOS()) { + if (mCurrentBufferIndex >= 0) { + mListener->queueBuffer(mCurrentBufferIndex, mCurrentBufferOffset); + mCurrentBufferIndex = -1; + } + + mListener->issueCommand(IStreamListener::EOS, false /* synchronous */); } } @@ -107,7 +263,7 @@ struct MyClient : public BnMediaPlayerClient { : mEOS(false) { } - virtual void notify(int msg, int ext1, int ext2) { + virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) { Mutex::Autolock autoLock(mLock); if (msg == MEDIA_ERROR || msg == MEDIA_PLAYBACK_COMPLETE) { @@ -139,6 +295,8 @@ private: int main(int argc, char **argv) { android::ProcessState::self()->startThreadPool(); + DataSource::RegisterDefaultSniffers(); + if (argc != 2) { fprintf(stderr, "Usage: %s filename\n", argv[0]); return 1; @@ -147,23 +305,27 @@ int main(int argc, char **argv) { sp<SurfaceComposerClient> composerClient = new SurfaceComposerClient; CHECK_EQ(composerClient->initCheck(), (status_t)OK); + ssize_t displayWidth = composerClient->getDisplayWidth(0); + ssize_t displayHeight = composerClient->getDisplayHeight(0); + + LOGV("display is %d x %d\n", displayWidth, displayHeight); + sp<SurfaceControl> control = composerClient->createSurface( - getpid(), String8("A Surface"), 0, - 1280, - 800, + displayWidth, + displayHeight, PIXEL_FORMAT_RGB_565, 0); CHECK(control != NULL); CHECK(control->isValid()); - CHECK_EQ(composerClient->openTransaction(), (status_t)OK); + SurfaceComposerClient::openGlobalTransaction(); CHECK_EQ(control->setLayer(30000), (status_t)OK); CHECK_EQ(control->show(), (status_t)OK); - CHECK_EQ(composerClient->closeTransaction(), (status_t)OK); + SurfaceComposerClient::closeGlobalTransaction(); sp<Surface> surface = control->getSurface(); CHECK(surface != NULL); @@ -174,19 +336,30 @@ int main(int argc, char **argv) { CHECK(service.get() != NULL); - int fd = open(argv[1], O_RDONLY); + sp<MyClient> client = new MyClient; - if (fd < 0) { - fprintf(stderr, "Failed to open file '%s'.", argv[1]); - return 1; - } + sp<IStreamSource> source; - sp<MyClient> client = new MyClient; + size_t len = strlen(argv[1]); + if (len >= 3 && !strcasecmp(".ts", &argv[1][len - 3])) { + int fd = open(argv[1], O_RDONLY); + + if (fd < 0) { + fprintf(stderr, "Failed to open file '%s'.", argv[1]); + return 1; + } + + source = new MyStreamSource(fd); + } else { + printf("Converting file to transport stream for streaming...\n"); + + source = new MyConvertingStreamSource(argv[1]); + } sp<IMediaPlayer> player = - service->create(getpid(), client, new MyStreamSource(fd), 0); + service->create(getpid(), client, 0); - if (player != NULL) { + if (player != NULL && player->setDataSource(source) == NO_ERROR) { player->setVideoSurface(surface); player->start(); @@ -197,9 +370,6 @@ int main(int argc, char **argv) { fprintf(stderr, "failed to instantiate player.\n"); } - close(fd); - fd = -1; - composerClient->dispose(); return 0; diff --git a/cmds/system_server/library/system_init.cpp b/cmds/system_server/library/system_init.cpp index a29ba73..59360d3 100644 --- a/cmds/system_server/library/system_init.cpp +++ b/cmds/system_server/library/system_init.cpp @@ -37,7 +37,7 @@ namespace android { * This class is used to kill this process when the runtime dies. */ class GrimReaper : public IBinder::DeathRecipient { -public: +public: GrimReaper() { } virtual void binderDied(const wp<IBinder>& who) @@ -54,15 +54,15 @@ public: extern "C" status_t system_init() { LOGI("Entered system_init()"); - + sp<ProcessState> proc(ProcessState::self()); - + sp<IServiceManager> sm = defaultServiceManager(); LOGI("ServiceManager: %p\n", sm.get()); - + sp<GrimReaper> grim = new GrimReaper(); sm->asBinder()->linkToDeath(grim, grim.get(), 0); - + char propBuf[PROPERTY_VALUE_MAX]; property_get("system_init.startsurfaceflinger", propBuf, "1"); if (strcmp(propBuf, "1") == 0) { @@ -70,24 +70,10 @@ extern "C" status_t system_init() SurfaceFlinger::instantiate(); } - // Start the sensor service - SensorService::instantiate(); - - // On the simulator, audioflinger et al don't get started the - // same way as on the device, and we need to start them here - if (!proc->supportsProcesses()) { - - // Start the AudioFlinger - AudioFlinger::instantiate(); - - // Start the media playback service - MediaPlayerService::instantiate(); - - // Start the camera service - CameraService::instantiate(); - - // Start the audio policy service - AudioPolicyService::instantiate(); + property_get("system_init.startsensorservice", propBuf, "1"); + if (strcmp(propBuf, "1") == 0) { + // Start the sensor service + SensorService::instantiate(); } // And now start the Android runtime. We have to do this bit @@ -97,21 +83,27 @@ extern "C" status_t system_init() // the beginning of their processes's main(), before calling // the init function. LOGI("System server: starting Android runtime.\n"); - AndroidRuntime* runtime = AndroidRuntime::getRuntime(); LOGI("System server: starting Android services.\n"); - runtime->callStatic("com/android/server/SystemServer", "init2"); - - // If running in our own process, just go into the thread - // pool. Otherwise, call the initialization finished - // func to let this process continue its initilization. - if (proc->supportsProcesses()) { - LOGI("System server: entering thread pool.\n"); - ProcessState::self()->startThreadPool(); - IPCThreadState::self()->joinThreadPool(); - LOGI("System server: exiting thread pool.\n"); + JNIEnv* env = runtime->getJNIEnv(); + if (env == NULL) { + return UNKNOWN_ERROR; + } + jclass clazz = env->FindClass("com/android/server/SystemServer"); + if (clazz == NULL) { + return UNKNOWN_ERROR; + } + jmethodID methodId = env->GetStaticMethodID(clazz, "init2", "()V"); + if (methodId == NULL) { + return UNKNOWN_ERROR; } + env->CallStaticVoidMethod(clazz, methodId); + + LOGI("System server: entering thread pool.\n"); + ProcessState::self()->startThreadPool(); + IPCThreadState::self()->joinThreadPool(); + LOGI("System server: exiting thread pool.\n"); + return NO_ERROR; } - diff --git a/cmds/system_server/system_main.cpp b/cmds/system_server/system_main.cpp index 543f650..d67329d 100644 --- a/cmds/system_server/system_main.cpp +++ b/cmds/system_server/system_main.cpp @@ -52,10 +52,5 @@ int main(int argc, const char* const argv[]) LOGW("*** Current priority: %d\n", getpriority(PRIO_PROCESS, 0)); setpriority(PRIO_PROCESS, 0, -1); - #if HAVE_ANDROID_OS - //setgid(GID_SYSTEM); - //setuid(UID_SYSTEM); - #endif - system_init(); } |