Provide the Keystore feature in the framework.

-- added the keystore library for Java application.
-- changed the marshalling of the keystore function return.
diff --git a/cmds/keystore/commands.c b/cmds/keystore/commands.c
index 7474d81..e53cece 100644
--- a/cmds/keystore/commands.c
+++ b/cmds/keystore/commands.c
@@ -40,7 +40,7 @@
     reply[0]=0;
     while ((de = readdir(d))) {
         if (de->d_type != DT_REG) continue;
-        strlcat(reply, " ", REPLY_MAX);
+        if (reply[0] != 0) strlcat(reply, " ", REPLY_MAX);
         if (strlcat(reply, de->d_name, REPLY_MAX) >= REPLY_MAX) {
             LOGE("reply is too long(too many files under '%s'\n", dir);
             return -1;
diff --git a/cmds/keystore/keystore.c b/cmds/keystore/keystore.c
index dbb62b37..5193b3d 100644
--- a/cmds/keystore/keystore.c
+++ b/cmds/keystore/keystore.c
@@ -113,7 +113,7 @@
     unsigned i;
     unsigned n = 0;
     unsigned short count;
-    int ret = -1;
+    short ret = -1;
 
     /* default reply is "" */
     reply[0] = 0;
@@ -139,7 +139,7 @@
                 LOGE("%s requires %d arguments (%d given)\n",
                      cmds[i].name, cmds[i].numargs, n);
             } else {
-                ret = cmds[i].func(arg + 1, reply);
+                ret = (short) cmds[i].func(arg + 1, reply);
             }
             goto done;
         }
@@ -148,24 +148,26 @@
 
 done:
     if (reply[0]) {
-        n = snprintf(cmd, BUFFER_MAX, "%d %s", ret, reply);
+        strlcpy(cmd, reply, BUFFER_MAX);
+        count = strlen(cmd);
     } else {
-        n = snprintf(cmd, BUFFER_MAX, "%d", ret);
+        count = 0;
     }
-    if (n > BUFFER_MAX) n = BUFFER_MAX;
-    count = n;
-
-    if (writex(s, &count, sizeof(count))) return -1;
-    if (writex(s, cmd, count)) return -1;
+    if (writex(s, &ret, sizeof(ret))) return -1;
+    if (ret == 0) {
+        if (writex(s, &count, sizeof(count))) return -1;
+        if (writex(s, cmd, count)) return -1;
+    }
 
     return 0;
 }
 
 int shell_command(const int argc, const char **argv)
 {
-    int fd, i, r;
+    int fd, i;
+    short ret;
     unsigned short count;
-    char cmd[BUFFER_MAX]="";
+    char buf[BUFFER_MAX]="";
 
     fd = socket_local_client(SOCKET_PATH,
                              ANDROID_SOCKET_NAMESPACE_RESERVED,
@@ -175,19 +177,24 @@
         exit(1);
     }
     for(i = 0; i < argc; i++) {
-        if (i > 0) strlcat(cmd, " ", BUFFER_MAX);
-        if(strlcat(cmd, argv[i], BUFFER_MAX) >= BUFFER_MAX) {
+        if (i > 0) strlcat(buf, " ", BUFFER_MAX);
+        if(strlcat(buf, argv[i], BUFFER_MAX) >= BUFFER_MAX) {
             fprintf(stderr, "Arguments are too long\n");
             exit(1);
         }
     }
-    count = strlen(cmd);
+    count = strlen(buf);
     if (writex(fd, &count, sizeof(count))) return -1;
-    if (writex(fd, cmd, strlen(cmd))) return -1;
-    if (readx(fd, &count, sizeof(count))) return -1;
-    if (readx(fd, cmd, count)) return -1;
-    cmd[count]=0;
-    fprintf(stdout, "%s\n", cmd);
+    if (writex(fd, buf, strlen(buf))) return -1;
+    if (readx(fd, &ret, sizeof(ret))) return -1;
+    if (ret == 0) {
+        if (readx(fd, &count, sizeof(count))) return -1;
+        if (readx(fd, buf, count)) return -1;
+        buf[count]=0;
+        fprintf(stdout, "%s\n", buf);
+    } else {
+        fprintf(stderr, "Failed, please check log!\n");
+    }
     return 0;
 }
 
diff --git a/keystore/java/android/security/Keystore.java b/keystore/java/android/security/Keystore.java
new file mode 100644
index 0000000..3f83473
--- /dev/null
+++ b/keystore/java/android/security/Keystore.java
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+package android.security;
+
+/**
+ * The Keystore class provides the functions to list the certs/keys in keystore.
+ * {@hide}
+ */
+public abstract class Keystore {
+    private static final String TAG = "Keystore";
+    private static final String[] NOTFOUND = new String[0];
+
+    /**
+     */
+    public static Keystore getInstance() {
+        return new FileKeystore();
+    }
+
+    /**
+     */
+    public abstract String getUserkey(String key);
+
+    /**
+     */
+    public abstract String getCertificate(String key);
+
+    /**
+     */
+    public abstract String[] getAllCertificateKeys();
+
+    /**
+     */
+    public abstract String[] getAllUserkeyKeys();
+
+    private static class FileKeystore extends Keystore {
+        private static final String SERVICE_NAME = "keystore";
+        private static final String LIST_CERTIFICATES = "listcerts";
+        private static final String LIST_USERKEYS = "listuserkeys";
+        private static final String PATH = "/data/misc/keystore/";
+        private static final String USERKEY_PATH = PATH + "userkeys/";
+        private static final String CERT_PATH = PATH + "certs/";
+        private static final ServiceCommand mServiceCommand =
+                new ServiceCommand(SERVICE_NAME);
+
+        @Override
+        public String getUserkey(String key) {
+            return USERKEY_PATH + key;
+        }
+
+        @Override
+        public String getCertificate(String key) {
+            return CERT_PATH + key;
+        }
+
+        /**
+         * Returns the array of the certificate names in keystore if successful.
+         * Or return an empty array if error.
+         *
+         * @return array of the certificates
+         */
+        @Override
+        public String[] getAllCertificateKeys() {
+            try {
+                String result = mServiceCommand.execute(LIST_CERTIFICATES);
+                if (result != null) return result.split("\\s+");
+                return NOTFOUND;
+            } catch (NumberFormatException ex) {
+                return NOTFOUND;
+            }
+        }
+
+        /**
+         * Returns the array of the names of private keys in keystore if successful.
+         * Or return an empty array if errors.
+         *
+         * @return array of the user keys
+         */
+        @Override
+        public String[] getAllUserkeyKeys() {
+            try {
+                String result = mServiceCommand.execute(LIST_USERKEYS);
+                if (result != null) return result.split("\\s+");
+                return NOTFOUND;
+            } catch (NumberFormatException ex) {
+                return NOTFOUND;
+            }
+        }
+    }
+}
diff --git a/keystore/java/android/security/ServiceCommand.java b/keystore/java/android/security/ServiceCommand.java
new file mode 100644
index 0000000..f1d4302
--- /dev/null
+++ b/keystore/java/android/security/ServiceCommand.java
@@ -0,0 +1,178 @@
+/*
+ * 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.
+ */
+
+package android.security;
+
+import android.net.LocalSocketAddress;
+import android.net.LocalSocket;
+import android.util.Config;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+/*
+ * ServiceCommand is used to connect to a service throught the local socket,
+ * and send out the command, return the result to the caller.
+ * {@hide}
+ */
+public class ServiceCommand {
+    public static final String SUCCESS = "0";
+    public static final String FAILED = "-1";
+
+    private String mServiceName;
+    private String mTag;
+    private InputStream mIn;
+    private OutputStream mOut;
+    private LocalSocket mSocket;
+    private static final int BUFFER_LENGTH = 1024;
+
+    private byte buf[] = new byte[BUFFER_LENGTH];
+    private int buflen = 0;
+
+    private boolean connect() {
+        if (mSocket != null) {
+            return true;
+        }
+        Log.i(mTag, "connecting...");
+        try {
+            mSocket = new LocalSocket();
+
+            LocalSocketAddress address = new LocalSocketAddress(
+                    mServiceName, LocalSocketAddress.Namespace.RESERVED);
+
+            mSocket.connect(address);
+
+            mIn = mSocket.getInputStream();
+            mOut = mSocket.getOutputStream();
+        } catch (IOException ex) {
+            disconnect();
+            return false;
+        }
+        return true;
+    }
+
+    private void disconnect() {
+        Log.i(mTag,"disconnecting...");
+        try {
+            if (mSocket != null) mSocket.close();
+        } catch (IOException ex) { }
+        try {
+            if (mIn != null) mIn.close();
+        } catch (IOException ex) { }
+        try {
+            if (mOut != null) mOut.close();
+        } catch (IOException ex) { }
+        mSocket = null;
+        mIn = null;
+        mOut = null;
+    }
+
+    private boolean readBytes(byte buffer[], int len) {
+        int off = 0, count;
+        if (len < 0) return false;
+        while (off != len) {
+            try {
+                count = mIn.read(buffer, off, len - off);
+                if (count <= 0) {
+                    Log.e(mTag, "read error " + count);
+                    break;
+                }
+                off += count;
+            } catch (IOException ex) {
+                Log.e(mTag,"read exception");
+                break;
+            }
+        }
+        if (off == len) return true;
+        disconnect();
+        return false;
+    }
+
+    private boolean readReply() {
+        int len, ret;
+        buflen = 0;
+
+        if (!readBytes(buf, 2)) return false;
+        ret = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
+        if (ret != 0) return false;
+
+        if (!readBytes(buf, 2)) return false;
+        len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
+        if (len > BUFFER_LENGTH) {
+            Log.e(mTag,"invalid reply length (" + len + ")");
+            disconnect();
+            return false;
+        }
+        if (!readBytes(buf, len)) return false;
+        buflen = len;
+        return true;
+    }
+
+    private boolean writeCommand(String _cmd) {
+        byte[] cmd = _cmd.getBytes();
+        int len = cmd.length;
+        if ((len < 1) || (len > BUFFER_LENGTH)) return false;
+        buf[0] = (byte) (len & 0xff);
+        buf[1] = (byte) ((len >> 8) & 0xff);
+        try {
+            mOut.write(buf, 0, 2);
+            mOut.write(cmd, 0, len);
+        } catch (IOException ex) {
+            Log.e(mTag,"write error");
+            disconnect();
+            return false;
+        }
+        return true;
+    }
+
+    private String executeCommand(String cmd) {
+        if (!writeCommand(cmd)) {
+            /* If service died and restarted in the background
+             * (unlikely but possible) we'll fail on the next
+             * write (this one).  Try to reconnect and write
+             * the command one more time before giving up.
+             */
+            Log.e(mTag, "write command failed? reconnect!");
+            if (!connect() || !writeCommand(cmd)) {
+                return null;
+            }
+        }
+        if (readReply()) {
+            return new String(buf, 0, buflen);
+        } else {
+            return null;
+        }
+    }
+
+    public synchronized String execute(String cmd) {
+      String result;
+      if (!connect()) {
+          Log.e(mTag, "connection failed");
+          return null;
+      }
+      result = executeCommand(cmd);
+      disconnect();
+      return result;
+    }
+
+    public ServiceCommand(String service) {
+        mServiceName = service;
+        mTag = service;
+    }
+}