summaryrefslogtreecommitdiffstats
path: root/cmds
diff options
context:
space:
mode:
Diffstat (limited to 'cmds')
-rw-r--r--cmds/am/src/com/android/commands/am/Am.java328
-rw-r--r--cmds/app_process/app_main.cpp148
-rw-r--r--cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java41
-rw-r--r--cmds/bootanimation/Android.mk9
-rw-r--r--cmds/bootanimation/BootAnimation.cpp18
-rw-r--r--cmds/bu/Android.mk18
-rw-r--r--cmds/bu/NOTICE (renamed from cmds/runtime/NOTICE)2
-rwxr-xr-xcmds/bu/bu6
-rw-r--r--cmds/bu/src/com/android/commands/bu/Backup.java130
-rw-r--r--cmds/bugreport/Android.mk4
-rw-r--r--cmds/dumpstate/Android.mk9
-rw-r--r--cmds/dumpstate/dumpstate.c35
-rw-r--r--cmds/dumpstate/dumpstate.h4
-rw-r--r--cmds/installd/Android.mk31
-rw-r--r--cmds/installd/commands.c171
-rw-r--r--cmds/installd/installd.c137
-rw-r--r--cmds/installd/installd.h70
-rw-r--r--cmds/installd/tests/Android.mk38
-rw-r--r--cmds/installd/tests/installd_utils_test.cpp383
-rw-r--r--cmds/installd/utils.c329
-rw-r--r--cmds/ip-up-vpn/Android.mk26
-rw-r--r--cmds/ip-up-vpn/ip-up-vpn.c151
-rw-r--r--cmds/keystore/Android.mk8
-rw-r--r--cmds/keystore/keystore.c589
-rw-r--r--cmds/keystore/keystore.cpp810
-rw-r--r--cmds/keystore/keystore.h20
-rw-r--r--cmds/keystore/keystore_cli.cpp (renamed from cmds/keystore/keystore_cli.c)46
-rwxr-xr-xcmds/keystore/test-keystore273
-rw-r--r--cmds/pm/src/com/android/commands/pm/Pm.java202
-rw-r--r--cmds/rawbu/Android.mk4
-rw-r--r--cmds/runtime/Android.mk30
-rw-r--r--cmds/runtime/MODULE_LICENSE_APACHE20
-rw-r--r--cmds/runtime/ServiceManager.cpp74
-rw-r--r--cmds/runtime/ServiceManager.h38
-rw-r--r--cmds/runtime/SignalHandler.cpp249
-rw-r--r--cmds/runtime/SignalHandler.h137
-rw-r--r--cmds/runtime/main_runtime.cpp516
-rw-r--r--cmds/screencap/Android.mk2
-rw-r--r--cmds/screenshot/Android.mk4
-rw-r--r--cmds/sensorservice/Android.mk19
-rw-r--r--cmds/sensorservice/main_sensorservice.cpp25
-rw-r--r--cmds/servicemanager/Android.mk2
-rw-r--r--cmds/stagefright/Android.mk6
-rw-r--r--cmds/stagefright/audioloop.cpp6
-rw-r--r--cmds/stagefright/recordvideo.cpp15
-rw-r--r--cmds/stagefright/sf2.cpp36
-rw-r--r--cmds/stagefright/stagefright.cpp170
-rw-r--r--cmds/stagefright/stream.cpp222
-rw-r--r--cmds/system_server/library/system_init.cpp62
-rw-r--r--cmds/system_server/system_main.cpp5
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, &params[0], &params[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();
}