summaryrefslogtreecommitdiff
path: root/cmds
diff options
context:
space:
mode:
Diffstat (limited to 'cmds')
-rw-r--r--cmds/am/src/com/android/commands/am/Am.java10
-rw-r--r--cmds/app_process/app_main.cpp130
-rw-r--r--cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java9
-rw-r--r--cmds/bootanimation/Android.mk2
-rw-r--r--cmds/bootanimation/BootAnimation.cpp4
-rw-r--r--cmds/bu/Android.mk18
-rw-r--r--cmds/bu/NOTICE190
-rwxr-xr-xcmds/bu/bu6
-rw-r--r--cmds/bu/src/com/android/commands/bu/Backup.java134
-rw-r--r--cmds/dumpstate/dumpstate.c13
-rw-r--r--cmds/installd/Android.mk27
-rw-r--r--cmds/installd/commands.c156
-rw-r--r--cmds/installd/installd.c129
-rw-r--r--cmds/installd/installd.h67
-rw-r--r--cmds/installd/tests/Android.mk42
-rw-r--r--cmds/installd/tests/installd_utils_test.cpp383
-rw-r--r--cmds/installd/utils.c329
-rw-r--r--cmds/keystore/Android.mk4
-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
-rw-r--r--cmds/pm/src/com/android/commands/pm/Pm.java71
-rw-r--r--cmds/runtime/main_runtime.cpp41
-rw-r--r--cmds/screencap/Android.mk2
-rw-r--r--cmds/sensorservice/Android.mk19
-rw-r--r--cmds/sensorservice/main_sensorservice.cpp25
-rw-r--r--cmds/stagefright/Android.mk6
-rw-r--r--cmds/stagefright/audioloop.cpp6
-rw-r--r--cmds/stagefright/sf2.cpp1
-rw-r--r--cmds/stagefright/stagefright.cpp15
-rw-r--r--cmds/stagefright/stream.cpp3
-rw-r--r--cmds/system_server/library/system_init.cpp35
33 files changed, 2518 insertions, 824 deletions
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 38cacdd09b7d..424b70ab8849 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -98,6 +98,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")) {
@@ -365,6 +367,10 @@ public class Am {
}
}
+ private void runForceStop() throws Exception {
+ mAm.forceStopPackage(nextArgRequired());
+ }
+
private void sendBroadcast() throws Exception {
Intent intent = makeIntent();
IntentReceiver receiver = new IntentReceiver();
@@ -851,7 +857,7 @@ public class Am {
wm.clearForcedDisplaySize();
}
} catch (RemoteException e) {
- }
+ }
}
private class IntentReceiver extends IIntentReceiver.Stub {
@@ -1013,6 +1019,8 @@ public class Am {
"\n" +
" start a Service: am startservice <INTENT>\n" +
"\n" +
+ " force stop everything associated with a package: force-stop <package>\n" +
+ "\n" +
" send a broadcast Intent: am broadcast <INTENT>\n" +
"\n" +
" start an Instrumentation: am instrument [flags] <COMPONENT>\n" +
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index 0159eddc0965..152a7cb7132b 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,6 +50,35 @@ public:
return mClassName;
}
+ virtual void onVmCreated(JNIEnv* env)
+ {
+ if (mClassName == NULL) {
+ return; // Zygote. Nothing to do here.
+ }
+
+ /*
+ * 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();
@@ -67,8 +86,9 @@ public:
LOGV("App process: starting thread pool.\n");
proc->startThreadPool();
}
-
- app_init(mClassName, mArgC, mArgV);
+
+ AndroidRuntime* ar = AndroidRuntime::getRuntime();
+ ar->callMain(mClassName, mClass, mArgC, mArgV);
if (ProcessState::self()->supportsProcesses()) {
IPCThreadState::self()->stopProcess();
@@ -81,7 +101,7 @@ public:
if (proc->supportsProcesses()) {
LOGV("App process: starting thread pool.\n");
proc->startThreadPool();
- }
+ }
}
virtual void onExit(int code)
@@ -96,9 +116,10 @@ public:
AndroidRuntime::onExit(code);
}
-
+
const char* mParentDir;
const char* mClassName;
+ jclass mClass;
int mArgC;
const char* const* mArgV;
};
@@ -120,7 +141,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 +149,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 +157,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 ac0e410fe301..38d0d2aecc56 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -150,20 +150,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());
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
index 2b8975943089..a00a212914a9 100644
--- a/cmds/bootanimation/Android.mk
+++ b/cmds/bootanimation/Android.mk
@@ -22,7 +22,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 e07495d28dcb..69c459705e2e 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -212,7 +212,7 @@ status_t BootAnimation::readyToRun() {
// create the native surface
sp<SurfaceControl> control = session()->createSurface(
- getpid(), 0, dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);
+ 0, dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);
session()->openTransaction();
control->setLayer(0x40000000);
session()->closeTransaction();
@@ -302,6 +302,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);
@@ -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 000000000000..4fd5fecdd8b9
--- /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/bu/NOTICE b/cmds/bu/NOTICE
new file mode 100644
index 000000000000..becc12079a43
--- /dev/null
+++ b/cmds/bu/NOTICE
@@ -0,0 +1,190 @@
+
+ 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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/cmds/bu/bu b/cmds/bu/bu
new file mode 100755
index 000000000000..e8dbc318818f
--- /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 000000000000..e81f799fda28
--- /dev/null
+++ b/cmds/bu/src/com/android/commands/bu/Backup.java
@@ -0,0 +1,134 @@
+/*
+ * 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.io.FileDescriptor;
+import java.io.IOException;
+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;
+ }
+
+ String arg = nextArg();
+ if (arg.equals("backup")) {
+ doFullBackup();
+ } else if (arg.equals("restore")) {
+ doFullRestore();
+ } else {
+ Log.e(TAG, "Invalid operation '" + arg + "'");
+ }
+ }
+
+ private void doFullBackup() {
+ 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.dup(FileDescriptor.out);
+ String[] packArray = new String[packages.size()];
+ mBackupManager.fullBackup(fd, saveApks, saveShared, doEverything,
+ packages.toArray(packArray));
+ } catch (IOException e) {
+ Log.e(TAG, "Can't dup out");
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to invoke backup manager for backup");
+ }
+ }
+
+ private void doFullRestore() {
+ // No arguments to restore
+ try {
+ ParcelFileDescriptor fd = ParcelFileDescriptor.dup(FileDescriptor.in);
+ mBackupManager.fullRestore(fd);
+ } catch (IOException e) {
+ Log.e(TAG, "Can't dup System.in");
+ } 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/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index ccc4fd78c1dd..21bb62eeeced 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -117,6 +117,15 @@ static void dumpstate() {
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);
@@ -124,12 +133,14 @@ 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 (strlen(ril_dumpstate_timeout) > 0) {
run_command("DUMP VENDOR RIL LOGS", atoi(ril_dumpstate_timeout),
"su", "root", "vril-dump", NULL);
}
+#endif
print_properties();
diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk
index 8641c3090a6c..d7a9ef67df05 100644
--- a/cmds/installd/Android.mk
+++ b/cmds/installd/Android.mk
@@ -1,13 +1,34 @@
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
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index ada712de547d..d45ac1925a24 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;
@@ -280,8 +304,8 @@ int get_size(const char *pkgname, const char *apkpath,
/* 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 +324,7 @@ int get_size(const char *pkgname, const char *apkpath,
}
}
- if (create_pkg_path(path, PKG_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX)) {
+ if (create_pkg_path(path, pkgname, PKG_DIR_POSTFIX, 0)) {
goto done;
}
@@ -310,10 +334,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;
@@ -544,15 +568,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 +744,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 +774,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 +785,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 d2b2f7f9fc60..c062d36ec93b 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])
@@ -93,7 +92,17 @@ static int do_get_size(char **arg, char reply[REPLY_MAX])
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 +132,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 },
+ { "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 +246,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 77b58ec10692..e5f6739d9081 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,12 +115,29 @@ 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);
diff --git a/cmds/installd/tests/Android.mk b/cmds/installd/tests/Android.mk
new file mode 100644
index 000000000000..e53378d9f724
--- /dev/null
+++ b/cmds/installd/tests/Android.mk
@@ -0,0 +1,42 @@
+# Build the unit tests for installd
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+ifneq ($(TARGET_SIMULATOR),true)
+
+# 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)) \
+)
+
+endif
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
new file mode 100644
index 000000000000..1128fceca0af
--- /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 a5e4b5a1557d..3099b8341f6e 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/keystore/Android.mk b/cmds/keystore/Android.mk
index 15a199f30c46..67dd9f8679ea 100644
--- a/cmds/keystore/Android.mk
+++ b/cmds/keystore/Android.mk
@@ -19,14 +19,14 @@ 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
diff --git a/cmds/keystore/keystore.c b/cmds/keystore/keystore.c
deleted file mode 100644
index afa64f8c9d12..000000000000
--- 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 000000000000..b48be6ef3885
--- /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) {
+ 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, ~GET},
+ {AID_VPN, AID_SYSTEM, GET},
+ {AID_WIFI, AID_SYSTEM, GET},
+ {AID_ROOT, AID_SYSTEM, GET},
+ {AID_KEYCHAIN, AID_SYSTEM, TEST | GET | SAW},
+ {~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 5ef51e9cd7d0..5ae3d24acee0 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 e8afb5a945b2..dcd3bcb8fc01 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/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 1b2326a43562..b0e4a864b6b0 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);
@@ -128,6 +130,16 @@ public final class Pm {
return;
}
+ if ("createUser".equals(op)) {
+ runCreateUser();
+ return;
+ }
+
+ if ("removeUser".equals(op)) {
+ runRemoveUser();
+ return;
+ }
+
try {
if (args.length == 1) {
if (args[0].equalsIgnoreCase("-l")) {
@@ -789,6 +801,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;
@@ -1032,6 +1101,8 @@ public final class Pm {
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 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:");
diff --git a/cmds/runtime/main_runtime.cpp b/cmds/runtime/main_runtime.cpp
index 83cb53317028..dbff095f6501 100644
--- a/cmds/runtime/main_runtime.cpp
+++ b/cmds/runtime/main_runtime.cpp
@@ -12,7 +12,7 @@
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
-#include <utils/Log.h>
+#include <utils/Log.h>
#include <cutils/zygote.h>
#include <cutils/properties.h>
@@ -41,7 +41,7 @@
#undef LOG_TAG
#define LOG_TAG "runtime"
-static const char* ZYGOTE_ARGV[] = {
+static const char* ZYGOTE_ARGV[] = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,3001,3002,3003",
@@ -68,7 +68,6 @@ extern Condition gEventQCondition;
namespace android {
-extern status_t app_init(const char* className);
extern void set_finish_init_func(void (*func)());
@@ -76,7 +75,7 @@ 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:
+public:
GrimReaper() { }
virtual void binderDied(const wp<IBinder>& who)
@@ -170,7 +169,7 @@ LOGI("run() sending FIRST_CALL_TRANSACTION to activity manager");
/*
* 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
@@ -210,17 +209,17 @@ static bool contextChecker(
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);
}
@@ -258,7 +257,7 @@ static void validateTime()
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));
@@ -346,14 +345,14 @@ int main(int argc, char* const argv[])
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]);
@@ -455,7 +454,7 @@ int main(int argc, char* const argv[])
#if 0
// Hack to keep libc from beating the filesystem to death. It's
- // hitting /etc/localtime frequently,
+ // 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
@@ -467,15 +466,15 @@ int main(int argc, char* const argv[])
/* track our progress through the boot sequence */
const int LOG_BOOT_PROGRESS_START = 3000;
- LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,
+ 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.
@@ -488,8 +487,8 @@ int main(int argc, char* const argv[])
property_get("log.redirect-stdio", propBuf, "");
logStdio = (strcmp(propBuf, "true") == 0);
- zygote_run_oneshot((int)(!logStdio),
- sizeof(ZYGOTE_ARGV) / sizeof(ZYGOTE_ARGV[0]),
+ zygote_run_oneshot((int)(!logStdio),
+ sizeof(ZYGOTE_ARGV) / sizeof(ZYGOTE_ARGV[0]),
ZYGOTE_ARGV);
//start_process("/system/bin/mediaserver");
@@ -497,8 +496,8 @@ int main(int argc, char* const argv[])
} else {
#ifndef HAVE_ANDROID_OS
QuickRuntime* runt = new QuickRuntime();
- runt->start("com/android/server/SystemServer",
- false /* spontaneously fork system server from zygote */);
+ runt->start("com/android/server/SystemServer",
+ "" /* spontaneously fork system server from zygote */);
#endif
}
@@ -506,11 +505,11 @@ int main(int argc, char* const argv[])
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 400a36b76719..ca8008bdba31 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/sensorservice/Android.mk b/cmds/sensorservice/Android.mk
new file mode 100644
index 000000000000..0811be57f61d
--- /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 000000000000..8610627b80c3
--- /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/stagefright/Android.mk b/cmds/stagefright/Android.mk
index 1b13dd954535..e9642f7cc8c5 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 87336629f2b2..858681f6ce45 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/sf2.cpp b/cmds/stagefright/sf2.cpp
index c1d08036d6bd..289665f51b5d 100644
--- a/cmds/stagefright/sf2.cpp
+++ b/cmds/stagefright/sf2.cpp
@@ -543,7 +543,6 @@ int main(int argc, char **argv) {
CHECK_EQ(composerClient->initCheck(), (status_t)OK);
control = composerClient->createSurface(
- getpid(),
String8("A Surface"),
0,
1280,
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index a875c3afeeb7..01262fa7a136 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -695,12 +695,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 +717,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;
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index bb84bd1ab995..f780afb80a0a 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -107,7 +107,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) {
@@ -149,7 +149,6 @@ int main(int argc, char **argv) {
sp<SurfaceControl> control =
composerClient->createSurface(
- getpid(),
String8("A Surface"),
0,
1280,
diff --git a/cmds/system_server/library/system_init.cpp b/cmds/system_server/library/system_init.cpp
index a29ba733a0f0..a19711e4794b 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,8 +70,11 @@ extern "C" status_t system_init()
SurfaceFlinger::instantiate();
}
- // Start the sensor service
- SensorService::instantiate();
+ property_get("system_init.startsensorservice", propBuf, "1");
+ if (strcmp(propBuf, "1") == 0) {
+ // 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
@@ -97,12 +100,23 @@ 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");
-
+ 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);
+
// 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.
@@ -114,4 +128,3 @@ extern "C" status_t system_init()
}
return NO_ERROR;
}
-