Merge change 2411 into donut

* changes:
  Manual merge from cupcake_dcm. Need to be reviewed by enf.
diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp
index 4e6859c..a3ab641 100644
--- a/camera/libcameraservice/CameraService.cpp
+++ b/camera/libcameraservice/CameraService.cpp
@@ -245,7 +245,7 @@
         sp<ICameraClient> oldClient;
         {
             Mutex::Autolock _l(mLock);
-            if (mClientPid != 0) {
+            if (mClientPid != 0 && checkPid() != NO_ERROR) {
                 LOGW("Tried to connect to locked camera");
                 return -EBUSY;
             }
diff --git a/cmds/keystore/Android.mk b/cmds/keystore/Android.mk
new file mode 100644
index 0000000..20f4adf
--- /dev/null
+++ b/cmds/keystore/Android.mk
@@ -0,0 +1,21 @@
+ifneq ($(TARGET_SIMULATOR),true)
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    keystore.c commands.c
+
+LOCAL_C_INCLUDES := \
+    $(call include-path-for, system-core)/cutils
+
+LOCAL_SHARED_LIBRARIES := \
+    libcutils
+
+LOCAL_STATIC_LIBRARIES :=
+
+LOCAL_MODULE:= keystore
+
+include $(BUILD_EXECUTABLE)
+
+endif # !simulator))
diff --git a/cmds/keystore/commands.c b/cmds/keystore/commands.c
new file mode 100644
index 0000000..7474d81
--- /dev/null
+++ b/cmds/keystore/commands.c
@@ -0,0 +1,141 @@
+/*
+** Copyright 2008, 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 "keystore.h"
+
+static DIR *open_keystore(const char *dir)
+{
+    DIR *d;
+    if ((d = opendir(dir)) == NULL) {
+        if (mkdir(dir, 0770) < 0) {
+            LOGE("cannot create dir '%s': %s\n", dir, strerror(errno));
+            unlink(dir);
+            return NULL;
+        }
+        d = open_keystore(dir);
+    }
+    return d;
+}
+
+static int list_files(const char *dir, char reply[REPLY_MAX]) {
+    struct dirent *de;
+    DIR *d;
+
+    if ((d = open_keystore(dir)) == NULL) {
+        return -1;
+    }
+    reply[0]=0;
+    while ((de = readdir(d))) {
+        if (de->d_type != DT_REG) continue;
+        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;
+        }
+    }
+    closedir(d);
+    return 0;
+}
+
+static int copy_keyfile(const char *keystore, const char *srcfile) {
+    int srcfd, dstfd;
+    int length;
+    char buf[2048];
+    char dstfile[KEYNAME_LENGTH];
+    const char *filename = strrchr(srcfile, '/');
+
+    strlcpy(dstfile, keystore, KEYNAME_LENGTH);
+    strlcat(dstfile, "/", KEYNAME_LENGTH);
+    if (strlcat(dstfile, filename ? filename + 1 : srcfile,
+                KEYNAME_LENGTH) >= KEYNAME_LENGTH) {
+        LOGE("keyname is too long '%s'\n", srcfile);
+        return -1;
+    }
+
+    if ((srcfd = open(srcfile, O_RDONLY)) == -1) {
+        LOGE("Cannot open the original file '%s'\n", srcfile);
+        return -1;
+    }
+    if ((dstfd = open(dstfile, O_CREAT|O_RDWR)) == -1) {
+        LOGE("Cannot open the destination file '%s'\n", dstfile);
+        return -1;
+    }
+    while((length = read(srcfd, buf, 2048)) > 0) {
+        write(dstfd, buf, length);
+    }
+    close(srcfd);
+    close(dstfd);
+    chmod(dstfile, 0440);
+    return 0;
+}
+
+static int install_key(const char *dir, const char *keyfile)
+{
+    struct dirent *de;
+    DIR *d;
+
+    if ((d = open_keystore(dir)) == NULL) {
+        return -1;
+    }
+    return copy_keyfile(dir, keyfile);
+}
+
+static int remove_key(const char *dir, const char *keyfile)
+{
+    char dstfile[KEYNAME_LENGTH];
+
+    strlcpy(dstfile, dir, KEYNAME_LENGTH);
+    strlcat(dstfile, "/", KEYNAME_LENGTH);
+    if (strlcat(dstfile, keyfile, KEYNAME_LENGTH) >= KEYNAME_LENGTH) {
+        LOGE("keyname is too long '%s'\n", keyfile);
+        return -1;
+    }
+    if (unlink(dstfile)) {
+        LOGE("cannot delete '%s': %s\n", dstfile, strerror(errno));
+        return -1;
+    }
+    return 0;
+}
+
+int list_certs(char reply[REPLY_MAX])
+{
+    return list_files(CERTS_DIR, reply);
+}
+
+int list_userkeys(char reply[REPLY_MAX])
+{
+    return list_files(USERKEYS_DIR, reply);
+}
+
+int install_cert(const char *certfile)
+{
+    return install_key(CERTS_DIR, certfile);
+}
+
+int install_userkey(const char *keyfile)
+{
+    return install_key(USERKEYS_DIR, keyfile);
+}
+
+int remove_cert(const char *certfile)
+{
+    return remove_key(CERTS_DIR, certfile);
+}
+
+int remove_userkey(const char *keyfile)
+{
+    return remove_key(USERKEYS_DIR, keyfile);
+}
diff --git a/cmds/keystore/keystore.c b/cmds/keystore/keystore.c
new file mode 100644
index 0000000..dbb62b3
--- /dev/null
+++ b/cmds/keystore/keystore.c
@@ -0,0 +1,248 @@
+/*
+** Copyright 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 "keystore.h"
+
+
+static int do_list_certs(char **arg, char reply[REPLY_MAX])
+{
+    return list_certs(reply);
+}
+
+static int do_list_userkeys(char **arg, char reply[REPLY_MAX])
+{
+    return list_userkeys(reply);
+}
+
+static int do_install_cert(char **arg, char reply[REPLY_MAX])
+{
+    return install_cert(arg[0]); /* move the certificate to keystore */
+}
+
+static int do_remove_cert(char **arg, char reply[REPLY_MAX])
+{
+    return remove_cert(arg[0]); /* certificate */
+}
+
+static int do_install_userkey(char **arg, char reply[REPLY_MAX])
+{
+    return install_userkey(arg[0]); /* move the certificate to keystore */
+}
+
+static int do_remove_userkey(char **arg, char reply[REPLY_MAX])
+{
+    return remove_userkey(arg[0]); /* userkey */
+}
+
+struct cmdinfo {
+    const char *name;
+    unsigned numargs;
+    int (*func)(char **arg, char reply[REPLY_MAX]);
+};
+
+
+struct cmdinfo cmds[] = {
+    { "listcerts",            0, do_list_certs },
+    { "listuserkeys",         0, do_list_userkeys },
+    { "installcert",          1, do_install_cert },
+    { "removecert",           1, do_remove_cert },
+    { "installuserkey",       1, do_install_userkey },
+    { "removeuserkey",        1, do_remove_userkey },
+};
+
+static int readx(int s, void *_buf, int count)
+{
+    char *buf = _buf;
+    int n = 0, r;
+    if (count < 0) return -1;
+    while (n < count) {
+        r = read(s, buf + n, count - n);
+        if (r < 0) {
+            if (errno == EINTR) continue;
+            LOGE("read error: %s\n", strerror(errno));
+            return -1;
+        }
+        if (r == 0) {
+            LOGE("eof\n");
+            return -1; /* EOF */
+        }
+        n += r;
+    }
+    return 0;
+}
+
+static int writex(int s, const void *_buf, int count)
+{
+    const char *buf = _buf;
+    int n = 0, r;
+    if (count < 0) return -1;
+    while (n < count) {
+        r = write(s, buf + n, count - n);
+        if (r < 0) {
+            if (errno == EINTR) continue;
+            LOGE("write error: %s\n", strerror(errno));
+            return -1;
+        }
+        n += r;
+    }
+    return 0;
+}
+
+
+/* Tokenize the command buffer, locate a matching command,
+ * ensure that the required number of arguments are provided,
+ * call the function(), return the result.
+ */
+static int execute(int s, char cmd[BUFFER_MAX])
+{
+    char reply[REPLY_MAX];
+    char *arg[TOKEN_MAX+1];
+    unsigned i;
+    unsigned n = 0;
+    unsigned short count;
+    int ret = -1;
+
+    /* default reply is "" */
+    reply[0] = 0;
+
+    /* n is number of args (not counting arg[0]) */
+    arg[0] = cmd;
+    while (*cmd) {
+        if (isspace(*cmd)) {
+            *cmd++ = 0;
+            n++;
+            arg[n] = cmd;
+            if (n == TOKEN_MAX) {
+                LOGE("too many arguments\n");
+                goto done;
+            }
+        }
+        cmd++;
+    }
+
+    for (i = 0; i < sizeof(cmds) / sizeof(cmds[0]); i++) {
+        if (!strcmp(cmds[i].name,arg[0])) {
+            if (n != cmds[i].numargs) {
+                LOGE("%s requires %d arguments (%d given)\n",
+                     cmds[i].name, cmds[i].numargs, n);
+            } else {
+                ret = cmds[i].func(arg + 1, reply);
+            }
+            goto done;
+        }
+    }
+    LOGE("unsupported command '%s'\n", arg[0]);
+
+done:
+    if (reply[0]) {
+        n = snprintf(cmd, BUFFER_MAX, "%d %s", ret, reply);
+    } else {
+        n = snprintf(cmd, BUFFER_MAX, "%d", ret);
+    }
+    if (n > BUFFER_MAX) n = BUFFER_MAX;
+    count = n;
+
+    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;
+    unsigned short count;
+    char cmd[BUFFER_MAX]="";
+
+    fd = socket_local_client(SOCKET_PATH,
+                             ANDROID_SOCKET_NAMESPACE_RESERVED,
+                             SOCK_STREAM);
+    if (fd == -1) {
+        fprintf(stderr, "Keystore service is not up and running\n");
+        exit(1);
+    }
+    for(i = 0; i < argc; i++) {
+        if (i > 0) strlcat(cmd, " ", BUFFER_MAX);
+        if(strlcat(cmd, argv[i], BUFFER_MAX) >= BUFFER_MAX) {
+            fprintf(stderr, "Arguments are too long\n");
+            exit(1);
+        }
+    }
+    count = strlen(cmd);
+    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);
+    return 0;
+}
+
+int main(const int argc, const char *argv[])
+{
+    char buf[BUFFER_MAX];
+    struct sockaddr addr;
+    socklen_t alen;
+    int lsocket, s, count;
+
+    if (argc > 1) {
+        return shell_command(argc - 1, argv + 1);
+    }
+
+    lsocket = android_get_control_socket(SOCKET_PATH);
+    if (lsocket < 0) {
+        LOGE("Failed to get socket from environment: %s\n", strerror(errno));
+        exit(1);
+    }
+    if (listen(lsocket, 5)) {
+        LOGE("Listen on socket failed: %s\n", strerror(errno));
+        exit(1);
+    }
+    fcntl(lsocket, F_SETFD, FD_CLOEXEC);
+
+    for (;;) {
+        alen = sizeof(addr);
+        s = accept(lsocket, &addr, &alen);
+        if (s < 0) {
+            LOGE("Accept failed: %s\n", strerror(errno));
+            continue;
+        }
+        fcntl(s, F_SETFD, FD_CLOEXEC);
+
+        LOGI("new connection\n");
+        for (;;) {
+            unsigned short count;
+            if (readx(s, &count, sizeof(count))) {
+                LOGE("failed to read size\n");
+                break;
+            }
+            if ((count < 1) || (count >= BUFFER_MAX)) {
+                LOGE("invalid size %d\n", count);
+                break;
+            }
+            if (readx(s, buf, count)) {
+                LOGE("failed to read command\n");
+                break;
+            }
+            buf[count] = 0;
+            if (execute(s, buf)) break;
+        }
+        LOGI("closing connection\n");
+        close(s);
+    }
+
+    return 0;
+}
diff --git a/cmds/keystore/keystore.h b/cmds/keystore/keystore.h
new file mode 100644
index 0000000..35acf0b
--- /dev/null
+++ b/cmds/keystore/keystore.h
@@ -0,0 +1,57 @@
+/*
+**
+** Copyright 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.
+*/
+
+#define LOG_TAG "keystore"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <utime.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <cutils/sockets.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#define SOCKET_PATH "keystore"
+
+
+/* path of the keystore */
+
+#define KEYSTORE_DIR_PREFIX "/data/misc/keystore"
+#define CERTS_DIR           KEYSTORE_DIR_PREFIX "/certs"
+#define USERKEYS_DIR         KEYSTORE_DIR_PREFIX "/userkeys"
+
+#define BUFFER_MAX      1024  /* input buffer for commands */
+#define TOKEN_MAX       8     /* max number of arguments in buffer */
+#define REPLY_MAX       1024  /* largest reply allowed */
+#define KEYNAME_LENGTH  128
+
+/* commands.c */
+int list_certs(char reply[REPLY_MAX]);
+int list_userkeys(char reply[REPLY_MAX]);
+int install_cert(const char *certfile);
+int install_userkey(const char *keyfile);
+int remove_cert(const char *certfile);
+int remove_userkey(const char *keyfile);
diff --git a/include/tts/TtsEngine.h b/include/tts/TtsEngine.h
index 06f3820..d2aa30e 100644
--- a/include/tts/TtsEngine.h
+++ b/include/tts/TtsEngine.h
@@ -16,33 +16,45 @@
 #include <media/AudioSystem.h>
 
 // This header defines the interface used by the Android platform
-// to access Text-To-Speech functionality in shared libraries that implement speech
-// synthesis and the management of resources associated with the synthesis.
-// An example of the implementation of this interface can be found in 
+// to access Text-To-Speech functionality in shared libraries that implement
+// speech synthesis and the management of resources associated with the
+// synthesis.
+// An example of the implementation of this interface can be found in
 // FIXME: add path+name to implementation of default TTS engine
 // Libraries implementing this interface are used in:
 //  frameworks/base/tts/jni/android_tts_SpeechSynthesis.cpp
 
 namespace android {
 
+enum tts_synth_status {
+    TTS_SYNTH_DONE              = 0,
+    TTS_SYNTH_PENDING           = 1
+};
+
+enum tts_callback_status {
+    TTS_CALLBACK_HALT           = 0,
+    TTS_CALLBACK_CONTINUE       = 1
+};
+
 // The callback is used by the implementation of this interface to notify its
 // client, the Android TTS service, that the last requested synthesis has been
-// completed.
+// completed. // TODO reword
 // The callback for synthesis completed takes:
-//    void *       - The userdata pointer set in the original synth call
-//    uint32_t     - Track sampling rate in Hz
-//    audio_format - The AudioSystem::audio_format enum
-//    int          - The number of channels
-//    int8_t *     - A buffer of audio data only valid during the execution of the callback
-//    size_t       - The size of the buffer
-// Note about memory management:
-//    The implementation of TtsEngine is responsible for the management of the memory
-//    it allocates to store the synthesized speech. After the execution of the callback
-//    to hand the synthesized data to the client of TtsEngine, the TTS engine is
-//    free to reuse or free the previously allocated memory.
-//    This implies that the implementation of the "synthDoneCB" callback cannot use
-//    the pointer to the buffer of audio samples outside of the callback itself.
-typedef void (synthDoneCB_t)(void *, uint32_t, AudioSystem::audio_format, int, int8_t *, size_t);
+// @param [inout] void *&       - The userdata pointer set in the original
+//                                 synth call
+// @param [in]    uint32_t      - Track sampling rate in Hz
+// @param [in]    audio_format  - The AudioSystem::audio_format enum
+// @param [in]    int           - The number of channels
+// @param [inout] int8_t *&     - A buffer of audio data only valid during the
+//                                execution of the callback
+// @param [inout] size_t  &     - The size of the buffer
+// @param [in] tts_synth_status - indicate whether the synthesis is done, or
+//                                 if more data is to be synthesized.
+// @return TTS_CALLBACK_HALT to indicate the synthesis must stop,
+//         TTS_CALLBACK_CONTINUE to indicate the synthesis must continue if
+//            there is more data to produce.
+typedef tts_callback_status (synthDoneCB_t)(void *&, uint32_t,
+        AudioSystem::audio_format, int, int8_t *&, size_t&, tts_synth_status);
 
 class TtsEngine;
 extern "C" TtsEngine* getTtsEngine();
@@ -53,7 +65,8 @@
     TTS_FEATURE_UNSUPPORTED     = -2,
     TTS_VALUE_INVALID           = -3,
     TTS_PROPERTY_UNSUPPORTED    = -4,
-    TTS_PROPERTY_SIZE_TOO_SMALL = -5
+    TTS_PROPERTY_SIZE_TOO_SMALL = -5,
+    TTS_MISSING_RESOURCES       = -6
 };
 
 class TtsEngine
@@ -68,38 +81,41 @@
     // @return TTS_SUCCESS, or TTS_FAILURE
     virtual tts_result shutdown();
 
-    // Interrupt synthesis and flushes any synthesized data that hasn't been output yet.
-    // This will block until callbacks underway are completed.
+    // Interrupt synthesis and flushes any synthesized data that hasn't been
+    // output yet. This will block until callbacks underway are completed.
     // @return TTS_SUCCESS, or TTS_FAILURE
     virtual tts_result stop();
 
-    // Load the resources associated with the specified language. The loaded language will
-    // only be used once a call to setLanguage() with the same language value is issued.
-    // Language values are based on the Android conventions for localization as described in
-    // the Android platform documentation on internationalization. This implies that language
-    // data is specified in the format xx-rYY, where xx is a two letter ISO 639-1 language code
-    // in lowercase and rYY is a two letter ISO 3166-1-alpha-2 language code in uppercase
-    // preceded by a lowercase "r".
+    // Load the resources associated with the specified language. The loaded
+    // language will only be used once a call to setLanguage() with the same
+    // language value is issued. Language values are based on the Android
+    // conventions for localization as described in the Android platform
+    // documentation on internationalization. This implies that language
+    // data is specified in the format xx-rYY, where xx is a two letter
+    // ISO 639-1 language code in lowercase and rYY is a two letter
+    // ISO 3166-1-alpha-2 language code in uppercase preceded by a
+    // lowercase "r".
     // @param value pointer to the language value
     // @param size  length of the language value
     // @return TTS_SUCCESS, or TTS_FAILURE
     virtual tts_result loadLanguage(const char *value, const size_t size);
 
-    // Signal the engine to use the specified language. This will force the language to be
-    // loaded if it wasn't loaded previously with loadLanguage().
+    // Signal the engine to use the specified language. This will force the
+    // language to be loaded if it wasn't loaded previously with loadLanguage().
     // See loadLanguage for the specification of the language.
     // @param value pointer to the language value
     // @param size  length of the language value
     // @return TTS_SUCCESS, or TTS_FAILURE
     virtual tts_result setLanguage(const char *value, const size_t size);
 
-    // Retrieve the currently set language, or an empty "value" if no language has
-    // been set.
+    // Retrieve the currently set language, or an empty "value" if no language
+    // has been set.
     // @param[out]   value pointer to the retrieved language value
-    // @param[inout] iosize  in: stores the size available to store the language value in *value
-    //                       out: stores the size required to hold the language value if
-    //                         getLanguage() returned  TTS_PROPERTY_SIZE_TOO_SMALL,
-    //                         unchanged otherwise.
+    // @param[inout] iosize in: stores the size available to store the language
+    //                         value in *value
+    //                      out: stores the size required to hold the language
+    //                         value if  getLanguage() returned
+    //                         TTS_PROPERTY_SIZE_TOO_SMALL, unchanged otherwise.
     // @return TTS_SUCCESS, or TTS_PROPERTY_SIZE_TOO_SMALL, or TTS_FAILURE
     virtual tts_result getLanguage(char *value, size_t *iosize);
 
@@ -108,23 +124,31 @@
     // @param property pointer to the property name
     // @param value    pointer to the property value
     // @param size     maximum size required to store this type of property
-    // @return         TTS_PROPERTY_UNSUPPORTED, or TTS_SUCCESS, or TTS_FAILURE, 
+    // @return         TTS_PROPERTY_UNSUPPORTED, or TTS_SUCCESS, or TTS_FAILURE,
     //                  or TTS_VALUE_INVALID
-    virtual tts_result setProperty(const char *property, const char *value, const size_t size);
+    virtual tts_result setProperty(const char *property, const char *value,
+            const size_t size);
 
     // Retrieve a property from the TTS engine
     // @param        property pointer to the property name
     // @param[out]   value    pointer to the retrieved language value
-    // @param[inout] iosize   in: stores the size available to store the property value
-    //                        out: stores the size required to hold the language value if
-    //                         getLanguage() returned  TTS_PROPERTY_SIZE_TOO_SMALL,
-    //                         unchanged otherwise.
-    // @return TTS_PROPERTY_UNSUPPORTED, or TTS_SUCCESS, or TTS_PROPERTY_SIZE_TOO_SMALL
-    virtual tts_result getProperty(const char *property, char *value, size_t *iosize);
+    // @param[inout] iosize   in: stores the size available to store the
+    //                          property value.
+    //                        out: stores the size required to hold the language
+    //                          value if getLanguage() returned
+    //                          TTS_PROPERTY_SIZE_TOO_SMALL, unchanged otherwise
+    // @return TTS_PROPERTY_UNSUPPORTED, or TTS_SUCCESS,
+    //         or TTS_PROPERTY_SIZE_TOO_SMALL
+    virtual tts_result getProperty(const char *property, char *value,
+            size_t *iosize);
 
     // Synthesize the text.
-    // When synthesis completes, the engine invokes the callback to notify the TTS framework.
-    // Note about the format of the input: the text parameter may use the following elements
+    // As the synthesis is performed, the engine invokes the callback to notify
+    // the TTS framework that it has filled the given buffer, and indicates how
+    // many bytes it wrote. The callback is called repeatedly until the engine
+    // has generated all the audio data corresponding to the text.
+    // Note about the format of the input: the text parameter may use the
+    // following elements
     // and their respective attributes as defined in the SSML 1.0 specification:
     //    * lang
     //    * say-as:
@@ -153,14 +177,25 @@
     // Text is coded in UTF-8.
     // @param text      the UTF-8 text to synthesize
     // @param userdata  pointer to be returned when the call is invoked
+    // @param buffer    the location where the synthesized data must be written
+    // @param bufferSize the number of bytes that can be written in buffer
     // @return          TTS_SUCCESS or TTS_FAILURE
-    virtual tts_result synthesizeText(const char *text, void *userdata);
+    virtual tts_result synthesizeText(const char *text, int8_t *buffer,
+            size_t bufferSize, void *userdata);
 
-    // Synthesize IPA text. When synthesis completes, the engine must call the given callback to notify the TTS API.
+    // Synthesize IPA text.
+    // As the synthesis is performed, the engine invokes the callback to notify
+    // the TTS framework that it has filled the given buffer, and indicates how
+    // many bytes it wrote. The callback is called repeatedly until the engine
+    // has generated all the audio data corresponding to the IPA data.
     // @param ipa      the IPA data to synthesize
     // @param userdata  pointer to be returned when the call is invoked
-    // @return TTS_FEATURE_UNSUPPORTED if IPA is not supported, otherwise TTS_SUCCESS or TTS_FAILURE
-    virtual tts_result synthesizeIpa(const char *ipa, void *userdata);
+    // @param buffer    the location where the synthesized data must be written
+    // @param bufferSize the number of bytes that can be written in buffer
+    // @return TTS_FEATURE_UNSUPPORTED if IPA is not supported,
+    //         otherwise TTS_SUCCESS or TTS_FAILURE
+    virtual tts_result synthesizeIpa(const char *ipa, int8_t *buffer,
+            size_t bufferSize, void *userdata);
 };
 
 } // namespace android
diff --git a/include/ui/Point.h b/include/ui/Point.h
index dbbad1e..1653120 100644
--- a/include/ui/Point.h
+++ b/include/ui/Point.h
@@ -31,12 +31,9 @@
     // because we want the compiler generated versions
 
     // Default constructor doesn't initialize the Point
-    inline Point()
-    {
+    inline Point() {
     }
-
-    inline Point(int _x, int _y) : x(_x), y(_y)
-    {
+    inline Point(int x, int y) : x(x), y(y) {
     }
 
     inline bool operator == (const Point& rhs) const {
@@ -57,8 +54,8 @@
     }
 
     inline Point& operator - () {
-        x=-x;
-        y=-y;
+        x = -x;
+        y = -y;
         return *this;
     }
     
@@ -73,11 +70,13 @@
         return *this;
     }
     
-    Point operator + (const Point& rhs) const {
-        return Point(x+rhs.x, y+rhs.y);
+    const Point operator + (const Point& rhs) const {
+        const Point result(x+rhs.x, y+rhs.y);
+        return result;
     }
-    Point operator - (const Point& rhs) const {
-        return Point(x-rhs.x, y-rhs.y);
+    const Point operator - (const Point& rhs) const {
+        const Point result(x-rhs.x, y-rhs.y);
+        return result;
     }    
 };
 
diff --git a/include/ui/Rect.h b/include/ui/Rect.h
index d232847..da72944 100644
--- a/include/ui/Rect.h
+++ b/include/ui/Rect.h
@@ -33,23 +33,16 @@
     // we don't provide copy-ctor and operator= on purpose
     // because we want the compiler generated versions
 
-    inline Rect()
-    {
+    inline Rect() {
     }
-
     inline Rect(int w, int h)
-        : left(0), top(0), right(w), bottom(h)
-    {
+        : left(0), top(0), right(w), bottom(h) {
     }
-
     inline Rect(int l, int t, int r, int b)
-        : left(l), top(t), right(r), bottom(b)
-    {
+        : left(l), top(t), right(r), bottom(b) {
     }
-
     inline Rect(const Point& lt, const Point& rb) 
-        : left(lt.x), top(lt.y), right(rb.x), bottom(rb.y)
-    {
+        : left(lt.x), top(lt.y), right(rb.x), bottom(rb.y) {
     }
 
     void makeInvalid();
@@ -78,21 +71,22 @@
         return bottom-top;
     }
 
-    // returns left-top Point non-const reference, can be assigned
-    inline Point& leftTop() {
-        return reinterpret_cast<Point&>(left);
+    void setLeftTop(const Point& lt) {
+        left = lt.x;
+        top  = lt.y;
     }
-    // returns right bottom non-const reference, can be assigned
-    inline Point& rightBottom() {
-        return reinterpret_cast<Point&>(right);
+
+    void setRightBottom(const Point& rb) {
+        right = rb.x;
+        bottom  = rb.y;
     }
     
     // the following 4 functions return the 4 corners of the rect as Point
-    inline const Point& leftTop() const {
-        return reinterpret_cast<const Point&>(left);
+    Point leftTop() const {
+        return Point(left, top);
     }
-    inline const Point& rightBottom() const {
-        return reinterpret_cast<const Point&>(right);
+    Point rightBottom() const {
+        return Point(right, bottom);
     }
     Point rightTop() const {
         return Point(right, top);
@@ -133,8 +127,8 @@
     Rect& operator -= (const Point& rhs) {
         return offsetBy(-rhs.x, -rhs.y);
     }
-    Rect operator + (const Point& rhs) const;
-    Rect operator - (const Point& rhs) const;
+    const Rect operator + (const Point& rhs) const;
+    const Rect operator - (const Point& rhs) const;
 
     void translate(int dx, int dy) { // legacy, don't use.
         offsetBy(dx, dy);
diff --git a/include/utils/backup_helpers.h b/include/utils/BackupHelpers.h
similarity index 100%
rename from include/utils/backup_helpers.h
rename to include/utils/BackupHelpers.h
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
index e4f4aad..324111b 100644
--- a/libs/audioflinger/AudioFlinger.cpp
+++ b/libs/audioflinger/AudioFlinger.cpp
@@ -817,19 +817,22 @@
         {
             AutoMutex lock(mHardwareLock);
             if (mForcedSpeakerCount++ == 0) {
-                mRouteRestoreTime = 0;
-                mMusicMuteSaved = mHardwareMixerThread->streamMute(AudioSystem::MUSIC);
-                if (mForcedRoute == 0 && !(mSavedRoute & AudioSystem::ROUTE_SPEAKER)) {
-                    LOGV("Route forced to Speaker ON %08x", mSavedRoute | AudioSystem::ROUTE_SPEAKER);
-                    mHardwareMixerThread->setStreamMute(AudioSystem::MUSIC, true);
-                    usleep(mHardwareMixerThread->latency()*1000);
-                    mHardwareStatus = AUDIO_HW_SET_ROUTING;
-                    mAudioHardware->setRouting(AudioSystem::MODE_NORMAL, mSavedRoute | AudioSystem::ROUTE_SPEAKER);
-                    mHardwareStatus = AUDIO_HW_IDLE;
-                    // delay track start so that audio hardware has time to siwtch routes
-                    usleep(kStartSleepTime);
+                if (mForcedRoute == 0) {
+                    mMusicMuteSaved = mHardwareMixerThread->streamMute(AudioSystem::MUSIC);
+                    LOGV("++mForcedSpeakerCount == 0, mMusicMuteSaved = %d, mRouteRestoreTime = %d", mMusicMuteSaved, mRouteRestoreTime);
+                    if (!(mSavedRoute & AudioSystem::ROUTE_SPEAKER)) {
+                        LOGV("Route forced to Speaker ON %08x", mSavedRoute | AudioSystem::ROUTE_SPEAKER);
+                        mHardwareMixerThread->setStreamMute(AudioSystem::MUSIC, true);
+                        usleep(mHardwareMixerThread->latency()*1000);
+                        mHardwareStatus = AUDIO_HW_SET_ROUTING;
+                        mAudioHardware->setRouting(AudioSystem::MODE_NORMAL, mSavedRoute | AudioSystem::ROUTE_SPEAKER);
+                        mHardwareStatus = AUDIO_HW_IDLE;
+                        // delay track start so that audio hardware has time to siwtch routes
+                        usleep(kStartSleepTime);
+                    }
                 }
                 mForcedRoute = AudioSystem::ROUTE_SPEAKER;
+                mRouteRestoreTime = 0;
             }
             LOGV("mForcedSpeakerCount incremented to %d", mForcedSpeakerCount);
         }
@@ -1553,7 +1556,6 @@
 AudioFlinger::MixerThread::TrackBase::TrackBase(
             const sp<MixerThread>& mixerThread,
             const sp<Client>& client,
-            int streamType,
             uint32_t sampleRate,
             int format,
             int channelCount,
@@ -1563,7 +1565,6 @@
     :   RefBase(),
         mMixerThread(mixerThread),
         mClient(client),
-        mStreamType(streamType),
         mFrameCount(0),
         mState(IDLE),
         mClientTid(-1),
@@ -1713,12 +1714,13 @@
             int channelCount,
             int frameCount,
             const sp<IMemory>& sharedBuffer)
-    :   TrackBase(mixerThread, client, streamType, sampleRate, format, channelCount, frameCount, 0, sharedBuffer)
+    :   TrackBase(mixerThread, client, sampleRate, format, channelCount, frameCount, 0, sharedBuffer)
 {
     mVolume[0] = 1.0f;
     mVolume[1] = 1.0f;
     mMute = false;
     mSharedBuffer = sharedBuffer;
+    mStreamType = streamType;
 }
 
 AudioFlinger::MixerThread::Track::~Track()
@@ -1902,15 +1904,15 @@
 AudioFlinger::MixerThread::RecordTrack::RecordTrack(
             const sp<MixerThread>& mixerThread,
             const sp<Client>& client,
-            int streamType,
+            int inputSource,
             uint32_t sampleRate,
             int format,
             int channelCount,
             int frameCount,
             uint32_t flags)
-    :   TrackBase(mixerThread, client, streamType, sampleRate, format,
+    :   TrackBase(mixerThread, client, sampleRate, format,
                   channelCount, frameCount, flags, 0),
-        mOverflow(false)
+        mOverflow(false), mInputSource(inputSource)
 {
 }
 
@@ -2235,7 +2237,7 @@
 
 sp<IAudioRecord> AudioFlinger::openRecord(
         pid_t pid,
-        int streamType,
+        int inputSource,
         uint32_t sampleRate,
         int format,
         int channelCount,
@@ -2258,7 +2260,7 @@
         goto Exit;
     }
 
-    if (uint32_t(streamType) >= AudioRecord::NUM_STREAM_TYPES) {
+    if (uint32_t(inputSource) >= AudioRecord::NUM_INPUT_SOURCES) {
         LOGE("invalid stream type");
         lStatus = BAD_VALUE;
         goto Exit;
@@ -2301,7 +2303,7 @@
         frameCount = ((frameCount - 1)/inFrameCount + 1) * inFrameCount;
     
         // create new record track. The record track uses one track in mHardwareMixerThread by convention.
-        recordTrack = new MixerThread::RecordTrack(mHardwareMixerThread, client, streamType, sampleRate,
+        recordTrack = new MixerThread::RecordTrack(mHardwareMixerThread, client, inputSource, sampleRate,
                                                    format, channelCount, frameCount, flags);
     }
     if (recordTrack->getCblk() == NULL) {
@@ -2408,7 +2410,7 @@
                 LOGV("AudioRecordThread: loop starting");
                 if (mRecordTrack != 0) {
                     input = mAudioHardware->openInputStream(
-                                    mRecordTrack->type(),
+                                    mRecordTrack->inputSource(),
                                     mRecordTrack->format(), 
                                     mRecordTrack->channelCount(), 
                                     mRecordTrack->sampleRate(), 
diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h
index c7ca9ec..8e47b29 100644
--- a/libs/audioflinger/AudioFlinger.h
+++ b/libs/audioflinger/AudioFlinger.h
@@ -139,7 +139,7 @@
     // record interface
     virtual sp<IAudioRecord> openRecord(
                                 pid_t pid,
-                                int streamType,
+                                int inputSource,
                                 uint32_t sampleRate,
                                 int format,
                                 int channelCount,
@@ -232,7 +232,6 @@
 
                                 TrackBase(const sp<MixerThread>& mixerThread,
                                         const sp<Client>& client,
-                                        int streamType,
                                         uint32_t sampleRate,
                                         int format,
                                         int channelCount,
@@ -260,10 +259,6 @@
                 return mCblk;
             }
 
-            int type() const {
-                return mStreamType;
-            }
-
             int format() const {
                 return mFormat;
             }
@@ -293,7 +288,6 @@
             sp<Client>          mClient;
             sp<IMemory>         mCblkMemory;
             audio_track_cblk_t* mCblk;
-            int                 mStreamType;
             void*               mBuffer;
             void*               mBufferEnd;
             uint32_t            mFrameCount;
@@ -328,6 +322,11 @@
                     void        mute(bool);
                     void        setVolume(float left, float right);
 
+                    int type() const {
+                        return mStreamType;
+                    }
+
+
         protected:
             friend class MixerThread;
             friend class AudioFlinger;
@@ -364,6 +363,7 @@
             int8_t              mRetryCount;
             sp<IMemory>         mSharedBuffer;
             bool                mResetDone;
+            int                 mStreamType;
         };  // end of Track
 
         // record track
@@ -371,7 +371,7 @@
         public:
                                 RecordTrack(const sp<MixerThread>& mixerThread,
                                         const sp<Client>& client,
-                                        int streamType,
+                                        int inputSource,
                                         uint32_t sampleRate,
                                         int format,
                                         int channelCount,
@@ -385,6 +385,8 @@
                     bool        overflow() { bool tmp = mOverflow; mOverflow = false; return tmp; }
                     bool        setOverflow() { bool tmp = mOverflow; mOverflow = true; return tmp; }
 
+                    int         inputSource() const { return mInputSource; }
+
         private:
             friend class AudioFlinger;
             friend class AudioFlinger::RecordHandle;
@@ -397,6 +399,7 @@
             virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
 
             bool                mOverflow;
+            int                 mInputSource;
         };
 
         // playback track
diff --git a/libs/audioflinger/AudioHardwareGeneric.cpp b/libs/audioflinger/AudioHardwareGeneric.cpp
index a97c0bc..1e159b8 100644
--- a/libs/audioflinger/AudioHardwareGeneric.cpp
+++ b/libs/audioflinger/AudioHardwareGeneric.cpp
@@ -98,8 +98,8 @@
         status_t *status, AudioSystem::audio_in_acoustics acoustics)
 {
     // check for valid input source
-    if ((inputSource != AudioRecord::DEFAULT_INPUT) &&
-            (inputSource != AudioRecord::MIC_INPUT)) {
+    if ((inputSource < AudioRecord::DEFAULT_INPUT) ||
+        (inputSource >= AudioRecord::NUM_INPUT_SOURCES)) {
         return 0;
     }
 
diff --git a/libs/audioflinger/AudioHardwareStub.cpp b/libs/audioflinger/AudioHardwareStub.cpp
index c61e6e6..0ab4c60 100644
--- a/libs/audioflinger/AudioHardwareStub.cpp
+++ b/libs/audioflinger/AudioHardwareStub.cpp
@@ -61,8 +61,8 @@
         status_t *status, AudioSystem::audio_in_acoustics acoustics)
 {
     // check for valid input source
-    if ((inputSource != AudioRecord::DEFAULT_INPUT) &&
-            (inputSource != AudioRecord::MIC_INPUT)) {
+    if ((inputSource < AudioRecord::DEFAULT_INPUT) ||
+        (inputSource >= AudioRecord::NUM_INPUT_SOURCES)) {
         return 0;
     }
 
diff --git a/libs/surfaceflinger/LayerBitmap.cpp b/libs/surfaceflinger/LayerBitmap.cpp
index e844350..397ddc8 100644
--- a/libs/surfaceflinger/LayerBitmap.cpp
+++ b/libs/surfaceflinger/LayerBitmap.cpp
@@ -114,7 +114,9 @@
     }
 
     if (mBitsMemory==0 || mSurface.data==0) {
-        LOGE("not enough memory for layer bitmap size=%u", size);
+        LOGE("not enough memory for layer bitmap "
+             "size=%u (w=%d, h=%d, stride=%d, format=%d)",
+             size, int(w), int(h), int(stride), int(format));
         allocator->dump("LayerBitmap");
         mSurface.data = 0;
         mSize = -1U;
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk
index f944357..b3b2104 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -20,7 +20,6 @@
 	LayerState.cpp \
 	Overlay.cpp \
 	PixelFormat.cpp \
-	Point.cpp \
 	Rect.cpp \
 	Region.cpp \
 	Surface.cpp \
diff --git a/libs/ui/Point.cpp b/libs/ui/Point.cpp
deleted file mode 100644
index 438d49f..0000000
--- a/libs/ui/Point.cpp
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- *  Point.cpp
- *  Android
- *
- *  Created on 11/16/2006.
- *  Copyright 2005 The Android Open Source Project
- *
- */
-
-#include <ui/Point.h>
-
diff --git a/libs/ui/Rect.cpp b/libs/ui/Rect.cpp
index 99e68bb..66b9576 100644
--- a/libs/ui/Rect.cpp
+++ b/libs/ui/Rect.cpp
@@ -1,21 +1,28 @@
 /*
- *  Rect.cpp
- *  Android
+ * Copyright (C) 2009 The Android Open Source Project
  *
- *  Created on 10/14/05.
- *  Copyright 2005 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 <ui/Rect.h>
 
 namespace android {
 
-inline int min(int a, int b) {
+static inline int min(int a, int b) {
     return (a<b) ? a : b;
 }
 
-inline int max(int a, int b) {
+static inline int max(int a, int b) {
     return (a>b) ? a : b;
 }
 
@@ -64,14 +71,16 @@
     return *this;
 }
 
-Rect Rect::operator + (const Point& rhs) const
+const Rect Rect::operator + (const Point& rhs) const
 {
-    return Rect(left+rhs.x, top+rhs.y, right+rhs.x, bottom+rhs.y); 
+    const Rect result(left+rhs.x, top+rhs.y, right+rhs.x, bottom+rhs.y);
+    return result;
 }
 
-Rect Rect::operator - (const Point& rhs) const
+const Rect Rect::operator - (const Point& rhs) const
 {
-    return Rect(left-rhs.x, top-rhs.y, right-rhs.x, bottom-rhs.y); 
+    const Rect result(left-rhs.x, top-rhs.y, right-rhs.x, bottom-rhs.y);
+    return result;
 }
 
 bool Rect::intersect(const Rect& with, Rect* result) const
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index 5a1a89b..9bdd64a 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -117,8 +117,8 @@
 	IPermissionController.cpp \
 	IServiceManager.cpp \
 	Unicode.cpp \
-    backup_data.cpp \
-	backup_helper_file.cpp
+    BackupData.cpp \
+	BackupHelpers.cpp
 
 ifeq ($(TARGET_SIMULATOR),true)
 LOCAL_SRC_FILES += $(hostSources)
diff --git a/libs/utils/backup_data.cpp b/libs/utils/BackupData.cpp
similarity index 99%
rename from libs/utils/backup_data.cpp
rename to libs/utils/BackupData.cpp
index 95c05b7..120f23d 100644
--- a/libs/utils/backup_data.cpp
+++ b/libs/utils/BackupData.cpp
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "backup_data"
 
-#include <utils/backup_helpers.h>
+#include <utils/BackupHelpers.h>
 #include <utils/ByteOrder.h>
 
 #include <stdio.h>
diff --git a/libs/utils/backup_helper_file.cpp b/libs/utils/BackupHelpers.cpp
similarity index 99%
rename from libs/utils/backup_helper_file.cpp
rename to libs/utils/BackupHelpers.cpp
index 7ec2ce8..7f423a8 100644
--- a/libs/utils/backup_helper_file.cpp
+++ b/libs/utils/BackupHelpers.cpp
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "file_backup_helper"
 
-#include <utils/backup_helpers.h>
+#include <utils/BackupHelpers.h>
 
 #include <utils/KeyedVector.h>
 #include <utils/ByteOrder.h>
@@ -350,10 +350,11 @@
             FileState& g = newSnapshot.editValueAt(m);
 
             int fd = open(realFilename.string(), O_RDONLY);
-            if (fd != -1) {
+            if (fd < 0) {
                 // We can't open the file.  Don't report it as a delete either.  Let the
                 // server keep the old version.  Maybe they'll be able to deal with it
                 // on restore.
+                LOGP("Unable to open file %s - skipping", realFilename.string());
             } else {
                 g.crc32 = compute_crc32(fd);
 
diff --git a/libs/utils/characterData.h b/libs/utils/CharacterData.h
similarity index 100%
rename from libs/utils/characterData.h
rename to libs/utils/CharacterData.h
diff --git a/libs/utils/Unicode.cpp b/libs/utils/Unicode.cpp
index 33f535f..f92703e 100644
--- a/libs/utils/Unicode.cpp
+++ b/libs/utils/Unicode.cpp
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-#include "utils/AndroidUnicode.h"
-#include "characterData.h"
+#include <utils/AndroidUnicode.h>
+#include "CharacterData.h"
 
 #define LOG_TAG "Unicode"
-#include "utils/Log.h"
+#include <utils/Log.h>
 
 // ICU headers for using macros
 #include <unicode/utf16.h>
diff --git a/libs/utils/ZipEntry.cpp b/libs/utils/ZipEntry.cpp
index fbc9e67..96f9fc4 100644
--- a/libs/utils/ZipEntry.cpp
+++ b/libs/utils/ZipEntry.cpp
@@ -20,8 +20,8 @@
 
 #define LOG_TAG "zip"
 
-#include "utils/ZipEntry.h"
-#include "utils/Log.h"
+#include <utils/ZipEntry.h>
+#include <utils/Log.h>
 
 #include <stdio.h>
 #include <string.h>
diff --git a/libs/utils/ZipFile.cpp b/libs/utils/ZipFile.cpp
index 89aa874..6f27d17 100644
--- a/libs/utils/ZipFile.cpp
+++ b/libs/utils/ZipFile.cpp
@@ -20,9 +20,9 @@
 
 #define LOG_TAG "zip"
 
-#include "utils/ZipFile.h"
-#include "utils/ZipUtils.h"
-#include "utils/Log.h"
+#include <utils/ZipFile.h>
+#include <utils/ZipUtils.h>
+#include <utils/Log.h>
 
 #include <zlib.h>
 #define DEF_MEM_LEVEL 8                // normally in zutil.h?
diff --git a/libs/utils/ZipFileCRO.cpp b/libs/utils/ZipFileCRO.cpp
index d312daf..45f6c8b 100644
--- a/libs/utils/ZipFileCRO.cpp
+++ b/libs/utils/ZipFileCRO.cpp
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#include "utils/ZipFileCRO.h"
-#include "utils/ZipFileRO.h"
+#include <utils/ZipFileCRO.h>
+#include <utils/ZipFileRO.h>
 
 using namespace android;
 
diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp
index ae8c719..6c701dd 100644
--- a/libs/utils/ZipFileRO.cpp
+++ b/libs/utils/ZipFileRO.cpp
@@ -19,9 +19,9 @@
 //
 #define LOG_TAG "zipro"
 //#define LOG_NDEBUG 0
-#include "utils/ZipFileRO.h"
-#include "utils/Log.h"
-#include "utils/misc.h"
+#include <utils/ZipFileRO.h>
+#include <utils/Log.h>
+#include <utils/misc.h>
 
 #include <zlib.h>
 
diff --git a/libs/utils/ZipUtils.cpp b/libs/utils/ZipUtils.cpp
index bfbacfe..5df94cb 100644
--- a/libs/utils/ZipUtils.cpp
+++ b/libs/utils/ZipUtils.cpp
@@ -20,9 +20,9 @@
 
 #define LOG_TAG "ziputil"
 
-#include "utils/ZipUtils.h"
-#include "utils/ZipFileRO.h"
-#include "utils/Log.h"
+#include <utils/ZipUtils.h>
+#include <utils/ZipFileRO.h>
+#include <utils/Log.h>
 
 #include <stdlib.h>
 #include <string.h>
diff --git a/opengl/libagl/array.cpp b/opengl/libagl/array.cpp
index 8fa7566..3e9c6a5 100644
--- a/opengl/libagl/array.cpp
+++ b/opengl/libagl/array.cpp
@@ -951,6 +951,8 @@
     v->index = first;
     first &= vertex_cache_t::INDEX_MASK;
     const GLubyte* vp = c->arrays.vertex.element(first);
+    v->obj.z = 0;
+    v->obj.w = 0x10000;
     c->arrays.vertex.fetch(c, v->obj.v, vp);
     c->arrays.mvp_transform(&c->transforms.mvp, &v->clip, &v->obj);
     c->arrays.perspective(c, v);
@@ -966,6 +968,8 @@
     do {
         v->flags = 0;
         v->index = first++;
+        v->obj.z = 0;
+        v->obj.w = 0x10000;
         c->arrays.vertex.fetch(c, v->obj.v, vp);
         c->arrays.mvp_transform(mvp, &v->clip, &v->obj);
         c->arrays.perspective(c, v);
diff --git a/opengl/libagl/light.cpp b/opengl/libagl/light.cpp
index 25c41d0..8ae32cc 100644
--- a/opengl/libagl/light.cpp
+++ b/opengl/libagl/light.cpp
@@ -38,13 +38,14 @@
 static void lightVertexMaterial(ogles_context_t* c, vertex_t* v);
 
 static inline void vscale3(GLfixed* d, const GLfixed* m, GLfixed s);
-static inline void vsub3w(GLfixed* d, const GLfixed* a, const GLfixed* b);
 
 static __attribute__((noinline))
 void vnorm3(GLfixed* d, const GLfixed* a);
 
 static inline void vsa3(GLfixed* d,
     const GLfixed* m, GLfixed s, const GLfixed* a);
+static inline void vss3(GLfixed* d,
+    const GLfixed* m, GLfixed s, const GLfixed* a);
 static inline void vmla3(GLfixed* d,
     const GLfixed* m0, const GLfixed* m1, const GLfixed* a);
 static inline void vmul3(GLfixed* d,
@@ -151,18 +152,10 @@
 }
 
 static inline
-void vsub3w(GLfixed* d, const GLfixed* a, const GLfixed* b) {
-    const GLfixed wa = a[3];
-    const GLfixed wb = b[3];
-    if (ggl_likely(wa == wb)) {
-        d[0] = a[0] - b[0];
-        d[1] = a[1] - b[1];
-        d[2] = a[2] - b[2];
-    } else {
-        d[0] = gglMulSubx(a[0], wb, gglMulx(b[0], wa));
-        d[1] = gglMulSubx(a[1], wb, gglMulx(b[1], wa));
-        d[2] = gglMulSubx(a[2], wb, gglMulx(b[2], wa));
-    }
+void vss3(GLfixed* d, const GLfixed* m, GLfixed s, const GLfixed* a) {
+    d[0] = gglMulSubx(m[0], s, a[0]);
+    d[1] = gglMulSubx(m[1], s, a[1]);
+    d[2] = gglMulSubx(m[2], s, a[2]);
 }
 
 static inline
@@ -227,7 +220,7 @@
         const int i = 31 - gglClz(en);
         en &= ~(1<<i);
         light_t& l = c->lighting.lights[i];
-        c->transforms.mvui.point3(&c->transforms.mvui,
+        c->transforms.mvui.point4(&c->transforms.mvui,
                 &l.objPosition, &l.position);
         vnorm3(l.normalizedObjPosition.v, l.objPosition.v);
     }
@@ -318,6 +311,11 @@
         vmul3(l.implicitAmbient.v,  material.ambient.v,  l.ambient.v);
         vmul3(l.implicitDiffuse.v,  material.diffuse.v,  l.diffuse.v);
         vmul3(l.implicitSpecular.v, material.specular.v, l.specular.v);
+        // this is just a flag to tell if we have a specular component
+        l.implicitSpecular.v[3] =
+                l.implicitSpecular.r |
+                l.implicitSpecular.g |
+                l.implicitSpecular.b;
     }
     // emission and ambient for the whole scene
     vmla3(  c->lighting.implicitSceneEmissionAndAmbient.v,
@@ -343,7 +341,11 @@
         vec4_t n;
         c->arrays.normal.fetch(c, n.v,
             c->arrays.normal.element(v->index & vertex_cache_t::INDEX_MASK));
-        if (c->transforms.rescaleNormals == GL_NORMALIZE)
+
+        // TODO: right now we handle GL_RESCALE_NORMALS as if ti were
+        // GL_NORMALIZE. We could optimize this by  scaling mvui 
+        // appropriately instead.
+        if (c->transforms.rescaleNormals)
             vnorm3(n.v, n.v);
 
         const material_t& material = c->lighting.front;
@@ -360,7 +362,8 @@
 
             // compute vertex-to-light vector
             if (ggl_unlikely(l.position.w)) {
-                vsub3w(d.v, l.objPosition.v, v->obj.v);
+                // lightPos/1.0 - vertex/vertex.w == lightPos*vertex.w - vertex
+                vss3(d.v, l.objPosition.v, v->obj.w, v->obj.v);
                 sqDist = dot3(d.v, d.v);
                 vscale3(d.v, d.v, gglSqrtRecipx(sqDist));
             } else {
diff --git a/opengl/libagl/matrix.cpp b/opengl/libagl/matrix.cpp
index f175cda..0b68dc0 100644
--- a/opengl/libagl/matrix.cpp
+++ b/opengl/libagl/matrix.cpp
@@ -55,7 +55,7 @@
 static void point2__generic(transform_t const*, vec4_t* c, vec4_t const* o);
 static void point3__generic(transform_t const*, vec4_t* c, vec4_t const* o);
 static void point4__generic(transform_t const*, vec4_t* c, vec4_t const* o);
-static void normal__generic(transform_t const*, vec4_t* c, vec4_t const* o);
+static void point4__mvui(transform_t const*, vec4_t* c, vec4_t const* o);
 
 // ----------------------------------------------------------------------------
 #if 0
@@ -209,7 +209,8 @@
 {
     flags = 0;
     ops = OP_ALL;
-    point3 = normal__generic;
+    point3 = point4__mvui;
+    point4 = point4__mvui;
 }
 
 void transform_t::dump(const char* what)
@@ -596,66 +597,19 @@
 
 void transform_state_t::update_mvui()
 {
+    GLfloat r[16];
     const GLfloat* const mv = modelview.top().elements();
-
-    /*
-    When transforming normals, we can use the upper 3x3 matrix, see:
-    http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node26.html
-    */
     
-    // Also note that:
-    //      l(obj) =  tr(M).l(eye) for infinite light
-    //      l(obj) = inv(M).l(eye) for local light
-
-    const uint32_t ops = modelview.top_ops() & ~OP_TRANSLATE;
-    if (ggl_likely((!(ops & ~OP_ROTATE)) ||
-        (rescaleNormals && modelview.isRigidBody()))) {
-        // if the modelview matrix is a rigid body transformation
-        // (translation, rotation, uniform scaling), then we can bypass
-        // the inverse by transposing the matrix.
-        GLfloat rescale = 1.0f;
-        if (rescaleNormals == GL_RESCALE_NORMAL) {
-            if (!(ops & ~OP_UNIFORM_SCALE)) {
-                rescale = reciprocalf(mv[I(0,0)]);
-            } else {
-                rescale = rsqrtf(
-                        sqrf(mv[I(2,0)]) + sqrf(mv[I(2,1)]) + sqrf(mv[I(2,2)]));
-            }
-        }
-        GLfixed* const x = mvui.matrix.m;
-        for (int i=0 ; i<3 ; i++) {
-            x[I(i,0)] = gglFloatToFixed(mv[I(0,i)] * rescale);
-            x[I(i,1)] = gglFloatToFixed(mv[I(1,i)] * rescale);
-            x[I(i,2)] = gglFloatToFixed(mv[I(2,i)] * rescale);
-        }
-        mvui.picker();
-        return;
-    }
-
-    GLfloat r[3][3];
-    r[0][0] = det22(mv[I(1,1)], mv[I(2,1)], mv[I(1,2)], mv[I(2,2)]);
-    r[0][1] =ndet22(mv[I(0,1)], mv[I(2,1)], mv[I(0,2)], mv[I(2,2)]);
-    r[0][2] = det22(mv[I(0,1)], mv[I(1,1)], mv[I(0,2)], mv[I(1,2)]);
-    r[1][0] =ndet22(mv[I(1,0)], mv[I(2,0)], mv[I(1,2)], mv[I(2,2)]);
-    r[1][1] = det22(mv[I(0,0)], mv[I(2,0)], mv[I(0,2)], mv[I(2,2)]);
-    r[1][2] =ndet22(mv[I(0,0)], mv[I(1,0)], mv[I(0,2)], mv[I(1,2)]);
-    r[2][0] = det22(mv[I(1,0)], mv[I(2,0)], mv[I(1,1)], mv[I(2,1)]);
-    r[2][1] =ndet22(mv[I(0,0)], mv[I(2,0)], mv[I(0,1)], mv[I(2,1)]);
-    r[2][2] = det22(mv[I(0,0)], mv[I(1,0)], mv[I(0,1)], mv[I(1,1)]);        
-
-    GLfloat rdet;
-    if (rescaleNormals == GL_RESCALE_NORMAL) {
-        rdet = rsqrtf(sqrf(r[0][2]) + sqrf(r[1][2]) + sqrf(r[2][2]));
-    } else {
-        rdet = reciprocalf( 
-            r[0][0]*mv[I(0,0)] + r[0][1]*mv[I(1,0)] + r[0][2]*mv[I(2,0)]);
-    }
+    // TODO: we need a faster invert, especially for when the modelview
+    // is a rigid-body matrix
+    invert(r, mv);
 
     GLfixed* const x = mvui.matrix.m;
-    for (int i=0 ; i<3 ; i++) {
-        x[I(i,0)] = gglFloatToFixed(r[i][0] * rdet);
-        x[I(i,1)] = gglFloatToFixed(r[i][1] * rdet);
-        x[I(i,2)] = gglFloatToFixed(r[i][2] * rdet);
+    for (int i=0 ; i<4 ; i++) {
+        x[I(i,0)] = gglFloatToFixed(r[I(i,0)]);
+        x[I(i,1)] = gglFloatToFixed(r[I(i,1)]);
+        x[I(i,2)] = gglFloatToFixed(r[I(i,2)]);
+        x[I(i,4)] = gglFloatToFixed(r[I(i,3)]);
     }
     mvui.picker();
 }
@@ -783,14 +737,19 @@
     lhs->w = mla4(rx, m[ 3], ry, m[ 7], rz, m[11], rw, m[15]);
 }
 
-void normal__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
+void point4__mvui(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
+    // this used for transforming light positions back to object space.
+    // Lights have 3 components positions, so w is always 1.
+    // however, it is used as a switch for directional lights, so we need
+    // to preserve it.
     const GLfixed* const m = mx->matrix.m;
     const GLfixed rx = rhs->x;
     const GLfixed ry = rhs->y;
     const GLfixed rz = rhs->z;
-    lhs->x = mla3(rx, m[ 0], ry, m[ 4], rz, m[ 8]); 
-    lhs->y = mla3(rx, m[ 1], ry, m[ 5], rz, m[ 9]);
-    lhs->z = mla3(rx, m[ 2], ry, m[ 6], rz, m[10]);
+    lhs->x = mla3a(rx, m[ 0], ry, m[ 4], rz, m[ 8], m[12]); 
+    lhs->y = mla3a(rx, m[ 1], ry, m[ 5], rz, m[ 9], m[13]);
+    lhs->z = mla3a(rx, m[ 2], ry, m[ 6], rz, m[10], m[14]);
+    lhs->w = rhs->w;
 }
 
 
diff --git a/opengl/tests/lighting1709/Android.mk b/opengl/tests/lighting1709/Android.mk
new file mode 100644
index 0000000..9563e61
--- /dev/null
+++ b/opengl/tests/lighting1709/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := LightingTest
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/opengl/tests/lighting1709/AndroidManifest.xml b/opengl/tests/lighting1709/AndroidManifest.xml
new file mode 100644
index 0000000..6c23d42
--- /dev/null
+++ b/opengl/tests/lighting1709/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.lightingtest">
+
+    <application>
+        <activity android:name="ClearActivity" android:label="LightingTest">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/opengl/tests/lighting1709/src/com/android/lightingtest/ClearActivity.java b/opengl/tests/lighting1709/src/com/android/lightingtest/ClearActivity.java
new file mode 100644
index 0000000..3ae8c5c
--- /dev/null
+++ b/opengl/tests/lighting1709/src/com/android/lightingtest/ClearActivity.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2007 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.lightingtest;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.app.Activity;
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.MotionEvent;
+
+public class ClearActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mGLView = new ClearGLSurfaceView(this);
+        setContentView(mGLView);
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        mGLView.onPause();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mGLView.onResume();
+    }
+    private GLSurfaceView mGLView;
+}
+
+class ClearGLSurfaceView extends GLSurfaceView {
+    public ClearGLSurfaceView(Context context) {
+        super(context);
+        mRenderer = new ClearRenderer();
+        setRenderer(mRenderer);
+    }
+
+    ClearRenderer mRenderer;
+}
+
+class ClearRenderer implements GLSurfaceView.Renderer {
+    public ClearRenderer() {
+    }
+
+    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+        // Do nothing special.
+    }
+
+    public void onSurfaceChanged(GL10 gl, int w, int h) {
+        // Compute the projection matrix
+        gl.glMatrixMode(GL10.GL_PROJECTION);
+        gl.glLoadIdentity();
+
+        // Compute the boundaries of the frustum
+        float fl = (float) (-(w / 2)) / 288;
+        float fr = (float) (w / 2) / 288;
+        float ft = (float) (h / 2) / 288;
+        float fb = (float) (-(h / 2)) / 288;
+
+        // Set the view frustum
+        gl.glFrustumf(fl, fr, fb, ft, 1.0f, 2000.0f);
+
+        // Set the viewport dimensions
+        gl.glMatrixMode(GL10.GL_MODELVIEW);
+        gl.glLoadIdentity();
+        gl.glViewport(0, 0, w, h);
+    }
+
+    public void onDrawFrame(GL10 gl) {
+        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+
+        final float lightOff[]        = {0.0f, 0.0f,  0.0f, 1.0f};
+        final float lightAmbient[]    = {5.0f, 0.0f,  0.0f, 1.0f};
+        final float lightDiffuse[]    = {0.0f, 2.0f,  0.0f, 0.0f};
+        final float lightPosSpot[]    = {0.0f, 0.0f, -8.0f, 1.0f};
+
+        final float pos[] = {
+                    -5.0f, -1.5f, 0.0f,
+                     0.0f, -1.5f, 0.0f,
+                     5.0f, -1.5f, 0.0f,
+                };
+        
+        final float v[] = new float[9];
+        ByteBuffer vbb = ByteBuffer.allocateDirect(v.length*4);
+        vbb.order(ByteOrder.nativeOrder());
+        FloatBuffer vb = vbb.asFloatBuffer();
+
+        gl.glDisable(GL10.GL_DITHER);
+
+        gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, lightAmbient, 0);
+        gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, lightDiffuse, 0);
+        gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPECULAR, lightOff, 0);
+        gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPosSpot, 0);
+        gl.glEnable(GL10.GL_LIGHT0);
+        
+        gl.glEnable(GL10.GL_LIGHTING);
+
+
+        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
+        gl.glNormal3f(0, 0, 1);
+        
+
+        // draw first 3 triangles, without using transforms
+        for (int i=0 ; i<3 ; i++) {
+            v[0] = -1; v[1] =-1; v[2] = -10;
+            v[3] =  0; v[4] = 1; v[5] = -10;
+            v[6] =  1; v[7] =-1; v[8] = -10;
+            for (int j=0 ; j<3 ; j++) {
+                v[j*3+0] -= pos[i*3+0];
+                v[j*3+1] -= pos[i*3+1];
+                v[j*3+2] -= pos[i*3+2];
+            }
+            vb.put(v).position(0);
+            gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vb);
+            gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
+        }
+        
+        // draw the 2nd batch this time with transforms
+        v[0] = -1; v[1] =-1; v[2] = -10;
+        v[3] =  0; v[4] = 1; v[5] = -10;
+        v[6] =  1; v[7] =-1; v[8] = -10;
+        vb.put(v).position(0);
+        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vb);
+
+        // draw lower left triangle
+        gl.glPushMatrix();
+        gl.glTranslatef(pos[0], pos[1], pos[2]);
+        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
+        gl.glPopMatrix();
+
+        // draw lower middle triangle
+        gl.glPushMatrix();
+        gl.glTranslatef(pos[3], pos[4], pos[5]);
+        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
+        gl.glPopMatrix();
+
+        // draw lower right triangle
+        gl.glPushMatrix();
+        gl.glTranslatef(pos[6], pos[7], pos[8]);
+        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
+        gl.glPopMatrix();      
+    }
+
+    public int[] getConfigSpec() {
+        int[] configSpec = { EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE };
+        return configSpec;      
+    }
+}
+
diff --git a/tts/java/android/tts/ITts.aidl b/tts/java/android/tts/ITts.aidl
index 1fe4a6a..3558f5a 100755
--- a/tts/java/android/tts/ITts.aidl
+++ b/tts/java/android/tts/ITts.aidl
@@ -1,59 +1,57 @@
-/*
- * 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.tts;
-
-import android.tts.ITtsCallback;
-
-import android.content.Intent;
-
-/**
- * AIDL for the TTS Service
- * ITts.java is autogenerated from this.
- *
- * {@hide}
- */
-interface ITts {
-    void setEngine(in String engineName, in String[] requestedLanguages, in int strictness);
-
-    void setEngineWithIntent(in Intent engineIntent);
-
-    void setSpeechRate(in int speechRate);
-
-    void speak(in String text, in int queueMode, in String[] params);
-
-    boolean isSpeaking();
-
-    void stop();
-
-    void addSpeech(in String text, in String packageName, in int resId);
-
-    void addSpeechFile(in String text, in String filename);
-
-    void setLanguage(in String language);
-
-    boolean synthesizeToFile(in String text, in String[] params, in String outputDirectory);
-
-    void playEarcon(in String earcon, in int queueMode, in String[] params);
-
-    void addEarcon(in String earcon, in String packageName, in int resId);
-
-    void addEarconFile(in String earcon, in String filename);
-
-    void registerCallback(ITtsCallback cb);
-
-    void unregisterCallback(ITtsCallback cb);
-}
+/*

+ * 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.tts;

+

+import android.tts.ITtsCallback;

+

+import android.content.Intent;

+

+/**

+ * AIDL for the TTS Service

+ * ITts.java is autogenerated from this.

+ *

+ * {@hide}

+ */

+interface ITts {

+    void setSpeechRate(in int speechRate);

+

+    void speak(in String text, in int queueMode, in String[] params);

+

+    boolean isSpeaking();

+

+    void stop();

+

+    void addSpeech(in String text, in String packageName, in int resId);

+

+    void addSpeechFile(in String text, in String filename);

+

+    void setLanguage(in String language);

+

+    boolean synthesizeToFile(in String text, in String[] params, in String outputDirectory);

+

+    void playEarcon(in String earcon, in int queueMode, in String[] params);

+

+    void addEarcon(in String earcon, in String packageName, in int resId);

+

+    void addEarconFile(in String earcon, in String filename);

+

+    void registerCallback(ITtsCallback cb);

+

+    void unregisterCallback(ITtsCallback cb);

+

+    void playSilence(in long duration, in int queueMode, in String[] params);

+}

diff --git a/tts/java/android/tts/SynthProxy.java b/tts/java/android/tts/SynthProxy.java
new file mode 100755
index 0000000..e065f40
--- /dev/null
+++ b/tts/java/android/tts/SynthProxy.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * 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.tts;
+
+import android.util.Log;
+import java.lang.ref.WeakReference;
+
+/**
+ * @hide
+ *
+ * The SpeechSynthesis class provides a high-level api to create and play
+ * synthesized speech. This class is used internally to talk to a native
+ * TTS library that implements the interface defined in
+ * frameworks/base/include/tts/TtsEngine.h
+ *
+ */
+@SuppressWarnings("unused")
+public class SynthProxy {
+
+    //
+    // External API
+    //
+
+    /**
+     * Constructor; pass the location of the native TTS .so to use.
+     */
+    public SynthProxy(String nativeSoLib) {
+        Log.e("TTS is loading", nativeSoLib);
+        native_setup(new WeakReference<SynthProxy>(this), nativeSoLib);
+    }
+
+    /**
+     * Stops and clears the AudioTrack.
+     */
+    public void stop() {
+        native_stop(mJniData);
+    }
+
+    /**
+     * Synthesize speech and speak it directly using AudioTrack.
+     */
+    public void speak(String text) {
+        native_speak(mJniData, text);
+    }
+
+    /**
+     * Synthesize speech to a file. The current implementation writes a valid
+     * WAV file to the given path, assuming it is writable. Something like
+     * "/sdcard/???.wav" is recommended.
+     */
+    public void synthesizeToFile(String text, String filename) {
+        native_synthesizeToFile(mJniData, text, filename);
+    }
+
+    // TODO add IPA methods
+
+    /**
+     * Sets the language
+     */
+    public void setLanguage(String language) {
+        native_setLanguage(mJniData, language);
+    }
+
+    /**
+     * Sets the speech rate
+     */
+    public final void setSpeechRate(int speechRate) {
+        native_setSpeechRate(mJniData, speechRate);
+    }
+
+
+    /**
+     * Plays the given audio buffer
+     */
+    public void playAudioBuffer(int bufferPointer, int bufferSize) {
+        native_playAudioBuffer(mJniData, bufferPointer, bufferSize);
+    }
+
+    /**
+     * Gets the currently set language
+     */
+    public String getLanguage() {
+        return native_getLanguage(mJniData);
+    }
+
+    /**
+     * Gets the currently set rate
+     */
+    public int getRate() {
+        return native_getRate(mJniData);
+    }
+
+    /**
+     * Shuts down the native synthesizer
+     */
+    public void shutdown()  {
+        native_shutdown(mJniData);
+    }
+
+    //
+    // Internal
+    //
+
+    protected void finalize() {
+        native_finalize(mJniData);
+        mJniData = 0;
+    }
+
+    static {
+        System.loadLibrary("ttssynthproxy");
+    }
+
+    private final static String TAG = "SynthProxy";
+
+    /**
+     * Accessed by native methods
+     */
+    private int mJniData = 0;
+
+    private native final void native_setup(Object weak_this,
+            String nativeSoLib);
+
+    private native final void native_finalize(int jniData);
+
+    private native final void native_stop(int jniData);
+
+    private native final void native_speak(int jniData, String text);
+
+    private native final void native_synthesizeToFile(int jniData, String text, String filename);
+
+    private native final void native_setLanguage(int jniData, String language);
+
+    private native final void native_setSpeechRate(int jniData, int speechRate);
+
+    // TODO add buffer format
+    private native final void native_playAudioBuffer(int jniData, int bufferPointer, int bufferSize);
+
+    private native final String native_getLanguage(int jniData);
+
+    private native final int native_getRate(int jniData);
+
+    private native final void native_shutdown(int jniData);
+
+
+    /**
+     * Callback from the C layer
+     */
+    @SuppressWarnings("unused")
+    private static void postNativeSpeechSynthesizedInJava(Object tts_ref,
+            int bufferPointer, int bufferSize) {
+
+        Log.i("TTS plugin debug", "bufferPointer: " + bufferPointer
+                + " bufferSize: " + bufferSize);
+
+        SynthProxy nativeTTS = (SynthProxy)((WeakReference)tts_ref).get();
+        // TODO notify TTS service of synthesis/playback completion,
+        //      method definition to be changed.
+    }
+}
diff --git a/tts/java/android/tts/TtsService.java b/tts/java/android/tts/TtsService.java
new file mode 100755
index 0000000..2373e96
--- /dev/null
+++ b/tts/java/android/tts/TtsService.java
@@ -0,0 +1,734 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * 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.tts;
+
+import android.tts.ITts.Stub;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnCompletionListener;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.preference.PreferenceManager;
+import android.util.Log;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * @hide Synthesizes speech from text. This is implemented as a service so that
+ *       other applications can call the TTS without needing to bundle the TTS
+ *       in the build.
+ *
+ */
+public class TtsService extends Service implements OnCompletionListener {
+
+    private static class SpeechItem {
+        public static final int SPEECH = 0;
+        public static final int EARCON = 1;
+        public static final int SILENCE = 2;
+        public String mText = null;
+        public ArrayList<String> mParams = null;
+        public int mType = SPEECH;
+        public long mDuration = 0;
+
+        public SpeechItem(String text, ArrayList<String> params, int itemType) {
+            mText = text;
+            mParams = params;
+            mType = itemType;
+        }
+
+        public SpeechItem(long silenceTime) {
+            mDuration = silenceTime;
+        }
+    }
+
+    /**
+     * Contains the information needed to access a sound resource; the name of
+     * the package that contains the resource and the resID of the resource
+     * within that package.
+     */
+    private static class SoundResource {
+        public String mSourcePackageName = null;
+        public int mResId = -1;
+        public String mFilename = null;
+
+        public SoundResource(String packageName, int id) {
+            mSourcePackageName = packageName;
+            mResId = id;
+            mFilename = null;
+        }
+
+        public SoundResource(String file) {
+            mSourcePackageName = null;
+            mResId = -1;
+            mFilename = file;
+        }
+    }
+
+    private static final String ACTION = "android.intent.action.USE_TTS";
+    private static final String CATEGORY = "android.intent.category.TTS";
+    private static final String PKGNAME = "android.tts";
+
+    final RemoteCallbackList<ITtsCallback> mCallbacks = new RemoteCallbackList<ITtsCallback>();
+
+    private Boolean mIsSpeaking;
+    private ArrayList<SpeechItem> mSpeechQueue;
+    private HashMap<String, SoundResource> mEarcons;
+    private HashMap<String, SoundResource> mUtterances;
+    private MediaPlayer mPlayer;
+    private TtsService mSelf;
+
+    private SharedPreferences prefs;
+
+    private final ReentrantLock speechQueueLock = new ReentrantLock();
+    private final ReentrantLock synthesizerLock = new ReentrantLock();
+
+    private SynthProxy nativeSynth;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.i("TTS", "TTS starting");
+
+        // TODO: Make this work when the settings are done in the main Settings
+        // app.
+        prefs = PreferenceManager.getDefaultSharedPreferences(this);
+
+        PackageManager pm = this.getPackageManager();
+        String soLibPath = "";
+        try {
+            soLibPath = pm.getApplicationInfo("com.svox.pico", 0).dataDir;
+        } catch (NameNotFoundException e) {
+            // This exception cannot actually happen as com.svox.pico is
+            // included with the system image.
+            e.printStackTrace();
+        }
+        soLibPath = soLibPath + "/lib/libttspico.so";
+        nativeSynth = new SynthProxy(soLibPath);
+
+        mSelf = this;
+        mIsSpeaking = false;
+
+        mEarcons = new HashMap<String, SoundResource>();
+        mUtterances = new HashMap<String, SoundResource>();
+
+        mSpeechQueue = new ArrayList<SpeechItem>();
+        mPlayer = null;
+
+        setLanguage(prefs.getString("lang_pref", "en-rUS"));
+        setSpeechRate(Integer.parseInt(prefs.getString("rate_pref", "140")));
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        // Don't hog the media player
+        cleanUpPlayer();
+
+        nativeSynth.shutdown();
+
+        // Unregister all callbacks.
+        mCallbacks.kill();
+    }
+
+    private void setSpeechRate(int rate) {
+        if (prefs.getBoolean("override_pref", false)) {
+            // This is set to the default here so that the preview in the prefs
+            // activity will show the change without a restart, even if apps are
+            // not allowed to change the defaults.
+            rate = Integer.parseInt(prefs.getString("rate_pref", "140"));
+        }
+        nativeSynth.setSpeechRate(rate);
+    }
+
+    private void setLanguage(String lang) {
+        if (prefs.getBoolean("override_pref", false)) {
+            // This is set to the default here so that the preview in the prefs
+            // activity will show the change without a restart, even if apps are
+            // not
+            // allowed to change the defaults.
+            lang = prefs.getString("lang_pref", "en-rUS");
+        }
+        nativeSynth.setLanguage(lang);
+    }
+
+    /**
+     * Adds a sound resource to the TTS.
+     *
+     * @param text
+     *            The text that should be associated with the sound resource
+     * @param packageName
+     *            The name of the package which has the sound resource
+     * @param resId
+     *            The resource ID of the sound within its package
+     */
+    private void addSpeech(String text, String packageName, int resId) {
+        mUtterances.put(text, new SoundResource(packageName, resId));
+    }
+
+    /**
+     * Adds a sound resource to the TTS.
+     *
+     * @param text
+     *            The text that should be associated with the sound resource
+     * @param filename
+     *            The filename of the sound resource. This must be a complete
+     *            path like: (/sdcard/mysounds/mysoundbite.mp3).
+     */
+    private void addSpeech(String text, String filename) {
+        mUtterances.put(text, new SoundResource(filename));
+    }
+
+    /**
+     * Adds a sound resource to the TTS as an earcon.
+     *
+     * @param earcon
+     *            The text that should be associated with the sound resource
+     * @param packageName
+     *            The name of the package which has the sound resource
+     * @param resId
+     *            The resource ID of the sound within its package
+     */
+    private void addEarcon(String earcon, String packageName, int resId) {
+        mEarcons.put(earcon, new SoundResource(packageName, resId));
+    }
+
+    /**
+     * Adds a sound resource to the TTS as an earcon.
+     *
+     * @param earcon
+     *            The text that should be associated with the sound resource
+     * @param filename
+     *            The filename of the sound resource. This must be a complete
+     *            path like: (/sdcard/mysounds/mysoundbite.mp3).
+     */
+    private void addEarcon(String earcon, String filename) {
+        mEarcons.put(earcon, new SoundResource(filename));
+    }
+
+    /**
+     * Speaks the given text using the specified queueing mode and parameters.
+     *
+     * @param text
+     *            The text that should be spoken
+     * @param queueMode
+     *            0 for no queue (interrupts all previous utterances), 1 for
+     *            queued
+     * @param params
+     *            An ArrayList of parameters. This is not implemented for all
+     *            engines.
+     */
+    private void speak(String text, int queueMode, ArrayList<String> params) {
+        if (queueMode == 0) {
+            stop();
+        }
+        mSpeechQueue.add(new SpeechItem(text, params, SpeechItem.SPEECH));
+        if (!mIsSpeaking) {
+            processSpeechQueue();
+        }
+    }
+
+    /**
+     * Plays the earcon using the specified queueing mode and parameters.
+     *
+     * @param earcon
+     *            The earcon that should be played
+     * @param queueMode
+     *            0 for no queue (interrupts all previous utterances), 1 for
+     *            queued
+     * @param params
+     *            An ArrayList of parameters. This is not implemented for all
+     *            engines.
+     */
+    private void playEarcon(String earcon, int queueMode,
+            ArrayList<String> params) {
+        if (queueMode == 0) {
+            stop();
+        }
+        mSpeechQueue.add(new SpeechItem(earcon, params, SpeechItem.EARCON));
+        if (!mIsSpeaking) {
+            processSpeechQueue();
+        }
+    }
+
+    /**
+     * Stops all speech output and removes any utterances still in the queue.
+     */
+    private void stop() {
+        Log.i("TTS", "Stopping");
+        mSpeechQueue.clear();
+
+        nativeSynth.stop();
+        mIsSpeaking = false;
+        if (mPlayer != null) {
+            try {
+                mPlayer.stop();
+            } catch (IllegalStateException e) {
+                // Do nothing, the player is already stopped.
+            }
+        }
+        Log.i("TTS", "Stopped");
+    }
+
+    public void onCompletion(MediaPlayer arg0) {
+        processSpeechQueue();
+    }
+
+    private void playSilence(long duration, int queueMode,
+            ArrayList<String> params) {
+        if (queueMode == 0) {
+            stop();
+        }
+        mSpeechQueue.add(new SpeechItem(duration));
+        if (!mIsSpeaking) {
+            processSpeechQueue();
+        }
+    }
+
+    private void silence(final long duration) {
+        class SilenceThread implements Runnable {
+            public void run() {
+                try {
+                    Thread.sleep(duration);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                } finally {
+                    processSpeechQueue();
+                }
+            }
+        }
+        Thread slnc = (new Thread(new SilenceThread()));
+        slnc.setPriority(Thread.MIN_PRIORITY);
+        slnc.start();
+    }
+
+    private void speakInternalOnly(final String text,
+            final ArrayList<String> params) {
+        class SynthThread implements Runnable {
+            public void run() {
+                boolean synthAvailable = false;
+                try {
+                    synthAvailable = synthesizerLock.tryLock();
+                    if (!synthAvailable) {
+                        Thread.sleep(100);
+                        Thread synth = (new Thread(new SynthThread()));
+                        synth.setPriority(Thread.MIN_PRIORITY);
+                        synth.start();
+                        return;
+                    }
+                    nativeSynth.speak(text);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                } finally {
+                    // This check is needed because finally will always run;
+                    // even if the
+                    // method returns somewhere in the try block.
+                    if (synthAvailable) {
+                        synthesizerLock.unlock();
+                    }
+                }
+            }
+        }
+        Thread synth = (new Thread(new SynthThread()));
+        synth.setPriority(Thread.MIN_PRIORITY);
+        synth.start();
+    }
+
+    private SoundResource getSoundResource(SpeechItem speechItem) {
+        SoundResource sr = null;
+        String text = speechItem.mText;
+        if (speechItem.mType == SpeechItem.SILENCE) {
+            // Do nothing if this is just silence
+        } else if (speechItem.mType == SpeechItem.EARCON) {
+            sr = mEarcons.get(text);
+        } else {
+            sr = mUtterances.get(text);
+        }
+        return sr;
+    }
+
+    private void dispatchSpeechCompletedCallbacks(String mark) {
+        Log.i("TTS callback", "dispatch started");
+        // Broadcast to all clients the new value.
+        final int N = mCallbacks.beginBroadcast();
+        for (int i = 0; i < N; i++) {
+            try {
+                mCallbacks.getBroadcastItem(i).markReached(mark);
+            } catch (RemoteException e) {
+                // The RemoteCallbackList will take care of removing
+                // the dead object for us.
+            }
+        }
+        mCallbacks.finishBroadcast();
+        Log.i("TTS callback", "dispatch completed to " + N);
+    }
+
+    private void processSpeechQueue() {
+        boolean speechQueueAvailable = false;
+        try {
+            speechQueueAvailable = speechQueueLock.tryLock();
+            if (!speechQueueAvailable) {
+                return;
+            }
+            if (mSpeechQueue.size() < 1) {
+                mIsSpeaking = false;
+                // Dispatch a completion here as this is the
+                // only place where speech completes normally.
+                // Nothing left to say in the queue is a special case
+                // that is always a "mark" - associated text is null.
+                dispatchSpeechCompletedCallbacks("");
+                return;
+            }
+
+            SpeechItem currentSpeechItem = mSpeechQueue.get(0);
+            mIsSpeaking = true;
+            SoundResource sr = getSoundResource(currentSpeechItem);
+            // Synth speech as needed - synthesizer should call
+            // processSpeechQueue to continue running the queue
+            Log.i("TTS processing: ", currentSpeechItem.mText);
+            if (sr == null) {
+                if (currentSpeechItem.mType == SpeechItem.SPEECH) {
+                    // TODO: Split text up into smaller chunks before accepting
+                    // them for processing.
+                    speakInternalOnly(currentSpeechItem.mText,
+                            currentSpeechItem.mParams);
+                } else {
+                    // This is either silence or an earcon that was missing
+                    silence(currentSpeechItem.mDuration);
+                }
+            } else {
+                cleanUpPlayer();
+                if (sr.mSourcePackageName == PKGNAME) {
+                    // Utterance is part of the TTS library
+                    mPlayer = MediaPlayer.create(this, sr.mResId);
+                } else if (sr.mSourcePackageName != null) {
+                    // Utterance is part of the app calling the library
+                    Context ctx;
+                    try {
+                        ctx = this.createPackageContext(sr.mSourcePackageName,
+                                0);
+                    } catch (NameNotFoundException e) {
+                        e.printStackTrace();
+                        mSpeechQueue.remove(0); // Remove it from the queue and
+                        // move on
+                        mIsSpeaking = false;
+                        return;
+                    }
+                    mPlayer = MediaPlayer.create(ctx, sr.mResId);
+                } else {
+                    // Utterance is coming from a file
+                    mPlayer = MediaPlayer.create(this, Uri.parse(sr.mFilename));
+                }
+
+                // Check if Media Server is dead; if it is, clear the queue and
+                // give up for now - hopefully, it will recover itself.
+                if (mPlayer == null) {
+                    mSpeechQueue.clear();
+                    mIsSpeaking = false;
+                    return;
+                }
+                mPlayer.setOnCompletionListener(this);
+                try {
+                    mPlayer.start();
+                } catch (IllegalStateException e) {
+                    mSpeechQueue.clear();
+                    mIsSpeaking = false;
+                    cleanUpPlayer();
+                    return;
+                }
+            }
+            if (mSpeechQueue.size() > 0) {
+                mSpeechQueue.remove(0);
+            }
+        } finally {
+            // This check is needed because finally will always run; even if the
+            // method returns somewhere in the try block.
+            if (speechQueueAvailable) {
+                speechQueueLock.unlock();
+            }
+        }
+    }
+
+    private void cleanUpPlayer() {
+        if (mPlayer != null) {
+            mPlayer.release();
+            mPlayer = null;
+        }
+    }
+
+    /**
+     * Synthesizes the given text using the specified queuing mode and
+     * parameters.
+     *
+     * @param text
+     *            The String of text that should be synthesized
+     * @param params
+     *            An ArrayList of parameters. The first element of this array
+     *            controls the type of voice to use.
+     * @param filename
+     *            The string that gives the full output filename; it should be
+     *            something like "/sdcard/myappsounds/mysound.wav".
+     * @return A boolean that indicates if the synthesis succeeded
+     */
+    private boolean synthesizeToFile(String text, ArrayList<String> params,
+            String filename, boolean calledFromApi) {
+        // Only stop everything if this is a call made by an outside app trying
+        // to
+        // use the API. Do NOT stop if this is a call from within the service as
+        // clearing the speech queue here would be a mistake.
+        if (calledFromApi) {
+            stop();
+        }
+        Log.i("TTS", "Synthesizing to " + filename);
+        boolean synthAvailable = false;
+        try {
+            synthAvailable = synthesizerLock.tryLock();
+            if (!synthAvailable) {
+                return false;
+            }
+            // Don't allow a filename that is too long
+            // TODO use platform constant
+            if (filename.length() > 250) {
+                return false;
+            }
+            nativeSynth.synthesizeToFile(text, filename);
+        } finally {
+            // This check is needed because finally will always run; even if the
+            // method returns somewhere in the try block.
+            if (synthAvailable) {
+                synthesizerLock.unlock();
+            }
+        }
+        Log.i("TTS", "Completed synthesis for " + filename);
+        return true;
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (ACTION.equals(intent.getAction())) {
+            for (String category : intent.getCategories()) {
+                if (category.equals(CATEGORY)) {
+                    return mBinder;
+                }
+            }
+        }
+        return null;
+    }
+
+    private final ITts.Stub mBinder = new Stub() {
+
+        public void registerCallback(ITtsCallback cb) {
+            if (cb != null)
+                mCallbacks.register(cb);
+        }
+
+        public void unregisterCallback(ITtsCallback cb) {
+            if (cb != null)
+                mCallbacks.unregister(cb);
+        }
+
+        /**
+         * Speaks the given text using the specified queueing mode and
+         * parameters.
+         *
+         * @param text
+         *            The text that should be spoken
+         * @param queueMode
+         *            0 for no queue (interrupts all previous utterances), 1 for
+         *            queued
+         * @param params
+         *            An ArrayList of parameters. The first element of this
+         *            array controls the type of voice to use.
+         */
+        public void speak(String text, int queueMode, String[] params) {
+            ArrayList<String> speakingParams = new ArrayList<String>();
+            if (params != null) {
+                speakingParams = new ArrayList<String>(Arrays.asList(params));
+            }
+            mSelf.speak(text, queueMode, speakingParams);
+        }
+
+        /**
+         * Plays the earcon using the specified queueing mode and parameters.
+         *
+         * @param earcon
+         *            The earcon that should be played
+         * @param queueMode
+         *            0 for no queue (interrupts all previous utterances), 1 for
+         *            queued
+         * @param params
+         *            An ArrayList of parameters.
+         */
+        public void playEarcon(String earcon, int queueMode, String[] params) {
+            ArrayList<String> speakingParams = new ArrayList<String>();
+            if (params != null) {
+                speakingParams = new ArrayList<String>(Arrays.asList(params));
+            }
+            mSelf.playEarcon(earcon, queueMode, speakingParams);
+        }
+
+        /**
+         * Plays the silence using the specified queueing mode and parameters.
+         *
+         * @param duration
+         *            The duration of the silence that should be played
+         * @param queueMode
+         *            0 for no queue (interrupts all previous utterances), 1 for
+         *            queued
+         * @param params
+         *            An ArrayList of parameters.
+         */
+        public void playSilence(long duration, int queueMode, String[] params) {
+            ArrayList<String> speakingParams = new ArrayList<String>();
+            if (params != null) {
+                speakingParams = new ArrayList<String>(Arrays.asList(params));
+            }
+            mSelf.playSilence(duration, queueMode, speakingParams);
+        }
+
+        /**
+         * Stops all speech output and removes any utterances still in the
+         * queue.
+         */
+        public void stop() {
+            mSelf.stop();
+        }
+
+        /**
+         * Returns whether or not the TTS is speaking.
+         *
+         * @return Boolean to indicate whether or not the TTS is speaking
+         */
+        public boolean isSpeaking() {
+            return (mSelf.mIsSpeaking && (mSpeechQueue.size() < 1));
+        }
+
+        /**
+         * Adds a sound resource to the TTS.
+         *
+         * @param text
+         *            The text that should be associated with the sound resource
+         * @param packageName
+         *            The name of the package which has the sound resource
+         * @param resId
+         *            The resource ID of the sound within its package
+         */
+        public void addSpeech(String text, String packageName, int resId) {
+            mSelf.addSpeech(text, packageName, resId);
+        }
+
+        /**
+         * Adds a sound resource to the TTS.
+         *
+         * @param text
+         *            The text that should be associated with the sound resource
+         * @param filename
+         *            The filename of the sound resource. This must be a
+         *            complete path like: (/sdcard/mysounds/mysoundbite.mp3).
+         */
+        public void addSpeechFile(String text, String filename) {
+            mSelf.addSpeech(text, filename);
+        }
+
+        /**
+         * Adds a sound resource to the TTS as an earcon.
+         *
+         * @param earcon
+         *            The text that should be associated with the sound resource
+         * @param packageName
+         *            The name of the package which has the sound resource
+         * @param resId
+         *            The resource ID of the sound within its package
+         */
+        public void addEarcon(String earcon, String packageName, int resId) {
+            mSelf.addEarcon(earcon, packageName, resId);
+        }
+
+        /**
+         * Adds a sound resource to the TTS as an earcon.
+         *
+         * @param earcon
+         *            The text that should be associated with the sound resource
+         * @param filename
+         *            The filename of the sound resource. This must be a
+         *            complete path like: (/sdcard/mysounds/mysoundbite.mp3).
+         */
+        public void addEarconFile(String earcon, String filename) {
+            mSelf.addEarcon(earcon, filename);
+        }
+
+        /**
+         * Sets the speech rate for the TTS. Note that this will only have an
+         * effect on synthesized speech; it will not affect pre-recorded speech.
+         *
+         * @param speechRate
+         *            The speech rate that should be used
+         */
+        public void setSpeechRate(int speechRate) {
+            mSelf.setSpeechRate(speechRate);
+        }
+
+        /**
+         * Sets the speech rate for the TTS. Note that this will only have an
+         * effect on synthesized speech; it will not affect pre-recorded speech.
+         *
+         * @param language
+         *            Language values are based on the Android conventions for
+         *            localization as described in the Android platform
+         *            documentation on internationalization. This implies that
+         *            language data is specified in the format xx-rYY, where xx
+         *            is a two letter ISO 639-1 language code in lowercase and
+         *            rYY is a two letter ISO 3166-1-alpha-2 language code in
+         *            uppercase preceded by a lowercase "r".
+         */
+        public void setLanguage(String language) {
+            mSelf.setLanguage(language);
+        }
+
+        /**
+         * Speaks the given text using the specified queueing mode and
+         * parameters.
+         *
+         * @param text
+         *            The String of text that should be synthesized
+         * @param params
+         *            An ArrayList of parameters. The first element of this
+         *            array controls the type of voice to use.
+         * @param filename
+         *            The string that gives the full output filename; it should
+         *            be something like "/sdcard/myappsounds/mysound.wav".
+         * @return A boolean that indicates if the synthesis succeeded
+         */
+        public boolean synthesizeToFile(String text, String[] params,
+                String filename) {
+            ArrayList<String> speakingParams = new ArrayList<String>();
+            if (params != null) {
+                speakingParams = new ArrayList<String>(Arrays.asList(params));
+            }
+            return mSelf.synthesizeToFile(text, speakingParams, filename, true);
+        }
+    };
+
+}
diff --git a/tts/jni/Android.mk b/tts/jni/Android.mk
new file mode 100755
index 0000000..665d6d2
--- /dev/null
+++ b/tts/jni/Android.mk
@@ -0,0 +1,31 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	android_tts_SynthProxy.cpp
+
+LOCAL_C_INCLUDES += \
+	$(JNI_H_INCLUDE)
+
+LOCAL_SHARED_LIBRARIES := \
+	libandroid_runtime \
+	libnativehelper \
+	libmedia \
+	libutils \
+	libcutils
+
+ifeq ($(TARGET_SIMULATOR),true)
+ LOCAL_LDLIBS += -ldl
+else
+ LOCAL_SHARED_LIBRARIES += libdl
+endif
+
+
+LOCAL_MODULE:= libttssynthproxy
+
+LOCAL_ARM_MODE := arm
+
+LOCAL_PRELINK_MODULE := false
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/tts/jni/android_tts_SynthProxy.cpp b/tts/jni/android_tts_SynthProxy.cpp
new file mode 100644
index 0000000..582e621
--- /dev/null
+++ b/tts/jni/android_tts_SynthProxy.cpp
@@ -0,0 +1,616 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * 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 <unistd.h>
+
+#define LOG_TAG "SynthProxy"
+
+#include <utils/Log.h>
+#include <nativehelper/jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <tts/TtsEngine.h>
+#include <media/AudioTrack.h>
+
+#include <dlfcn.h>
+
+#define DEFAULT_TTS_RATE        16000
+#define DEFAULT_TTS_FORMAT      AudioSystem::PCM_16_BIT
+#define DEFAULT_TTS_NB_CHANNELS 1
+#define DEFAULT_TTS_BUFFERSIZE  1024
+
+#define USAGEMODE_PLAY_IMMEDIATELY 0
+#define USAGEMODE_WRITE_TO_FILE    1
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+struct fields_t {
+    jfieldID    synthProxyFieldJniData;
+    jclass      synthProxyClass;
+    jmethodID   synthProxyMethodPost;
+};
+
+struct afterSynthData_t {
+    jint jniStorage;
+    int  usageMode;
+    FILE* outputFile;
+};
+
+// ----------------------------------------------------------------------------
+static fields_t javaTTSFields;
+
+// ----------------------------------------------------------------------------
+class SynthProxyJniStorage {
+    public :
+        //jclass                    tts_class;
+        jobject                   tts_ref;
+        TtsEngine*                mNativeSynthInterface;
+        AudioTrack*               mAudioOut;
+        uint32_t                  mSampleRate;
+        AudioSystem::audio_format mAudFormat;
+        int                       mNbChannels;
+        int8_t *                  mBuffer;
+        size_t                    mBufferSize;
+
+        SynthProxyJniStorage() {
+            //tts_class = NULL;
+            tts_ref = NULL;
+            mNativeSynthInterface = NULL;
+            mAudioOut = NULL;
+            mSampleRate = DEFAULT_TTS_RATE;
+            mAudFormat  = DEFAULT_TTS_FORMAT;
+            mNbChannels = DEFAULT_TTS_NB_CHANNELS;
+            mBufferSize = DEFAULT_TTS_BUFFERSIZE;
+            mBuffer = new int8_t[mBufferSize];
+        }
+
+        ~SynthProxyJniStorage() {
+            killAudio();
+            if (mNativeSynthInterface) {
+                mNativeSynthInterface->shutdown();
+                mNativeSynthInterface = NULL;
+            }
+            delete mBuffer;
+        }
+
+        void killAudio() {
+            if (mAudioOut) {
+                mAudioOut->stop();
+                delete mAudioOut;
+                mAudioOut = NULL;
+            }
+        }
+
+        void createAudioOut(uint32_t rate, AudioSystem::audio_format format,
+                int channel) {
+            mSampleRate = rate;
+            mAudFormat  = format;
+            mNbChannels = channel;
+
+            // TODO use the TTS stream type
+            int streamType = AudioSystem::MUSIC;
+
+            // retrieve system properties to ensure successful creation of the
+            // AudioTrack object for playback
+            int afSampleRate;
+            if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {
+                afSampleRate = 44100;
+            }
+            int afFrameCount;
+            if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) {
+                afFrameCount = 2048;
+            }
+            uint32_t afLatency;
+            if (AudioSystem::getOutputLatency(&afLatency, streamType) != NO_ERROR) {
+                afLatency = 500;
+            }
+            uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate);
+            if (minBufCount < 2) minBufCount = 2;
+            int minFrameCount = (afFrameCount * rate * minBufCount)/afSampleRate;
+
+            mAudioOut = new AudioTrack(streamType, rate, format, channel,
+                    minFrameCount > 4096 ? minFrameCount : 4096,
+                    0, 0, 0, 0); // not using an AudioTrack callback
+
+            if (mAudioOut->initCheck() != NO_ERROR) {
+              LOGI("AudioTrack error");
+              delete mAudioOut;
+              mAudioOut = NULL;
+            } else {
+              LOGI("AudioTrack OK");
+              mAudioOut->start();
+              LOGI("AudioTrack started");
+            }
+        }
+};
+
+
+// ----------------------------------------------------------------------------
+void prepAudioTrack(SynthProxyJniStorage* pJniData,
+        uint32_t rate, AudioSystem::audio_format format, int channel)
+{
+    // Don't bother creating a new audiotrack object if the current
+    // object is already set.
+    if ( pJniData->mAudioOut &&
+         (rate == pJniData->mSampleRate) &&
+         (format == pJniData->mAudFormat) &&
+         (channel == pJniData->mNbChannels) ){
+        return;
+    }
+    if (pJniData->mAudioOut){
+        pJniData->killAudio();
+    }
+    pJniData->createAudioOut(rate, format, channel);
+}
+
+
+// ----------------------------------------------------------------------------
+/*
+ * Callback from TTS engine.
+ * Directly speaks using AudioTrack or write to file
+ */
+static tts_callback_status ttsSynthDoneCB(void *& userdata, uint32_t rate,
+                           AudioSystem::audio_format format, int channel,
+                           int8_t *&wav, size_t &bufferSize, tts_synth_status status) {
+    LOGI("ttsSynthDoneCallback: %d bytes", bufferSize);
+
+    if (userdata == NULL){
+        LOGE("userdata == NULL");
+        return TTS_CALLBACK_HALT;
+    }
+    afterSynthData_t* pForAfter = (afterSynthData_t*)userdata;
+    SynthProxyJniStorage* pJniData = (SynthProxyJniStorage*)(pForAfter->jniStorage);
+
+    if (pForAfter->usageMode == USAGEMODE_PLAY_IMMEDIATELY){
+        LOGI("Direct speech");
+
+        if (wav == NULL) {
+            delete pForAfter;
+            LOGI("Null: speech has completed");
+        }
+
+        if (bufferSize > 0) {
+            prepAudioTrack(pJniData, rate, format, channel);
+            if (pJniData->mAudioOut) {
+                pJniData->mAudioOut->write(wav, bufferSize);
+                LOGI("AudioTrack wrote: %d bytes", bufferSize);
+            } else {
+                LOGI("Can't play, null audiotrack");
+            }
+        }
+    } else  if (pForAfter->usageMode == USAGEMODE_WRITE_TO_FILE) {
+        LOGI("Save to file");
+        if (wav == NULL) {
+            delete pForAfter;
+            LOGI("Null: speech has completed");
+        }
+        if (bufferSize > 0){
+            fwrite(wav, 1, bufferSize, pForAfter->outputFile);
+        }
+    }
+    // TODO update to call back into the SynthProxy class through the
+    //      javaTTSFields.synthProxyMethodPost methode to notify
+    //      playback has completed if the synthesis is done, i.e.
+    //      if status == TTS_SYNTH_DONE
+    //delete pForAfter;
+
+    // we don't update the wav (output) parameter as we'll let the next callback
+    // write at the same location, we've consumed the data already, but we need
+    // to update bufferSize to let the TTS engine know how much it can write the
+    // next time it calls this function.
+    bufferSize = pJniData->mBufferSize;
+
+    return TTS_CALLBACK_CONTINUE;
+}
+
+
+// ----------------------------------------------------------------------------
+static void
+android_tts_SynthProxy_native_setup(JNIEnv *env, jobject thiz,
+        jobject weak_this, jstring nativeSoLib)
+{
+    SynthProxyJniStorage* pJniStorage = new SynthProxyJniStorage();
+
+    prepAudioTrack(pJniStorage,
+            DEFAULT_TTS_RATE, DEFAULT_TTS_FORMAT, DEFAULT_TTS_NB_CHANNELS);
+
+    const char *nativeSoLibNativeString =
+            env->GetStringUTFChars(nativeSoLib, 0);
+
+    void *engine_lib_handle = dlopen(nativeSoLibNativeString,
+            RTLD_NOW | RTLD_LOCAL);
+    if (engine_lib_handle==NULL) {
+       LOGI("engine_lib_handle==NULL");
+       // TODO report error so the TTS can't be used
+    } else {
+        TtsEngine *(*get_TtsEngine)() =
+            reinterpret_cast<TtsEngine* (*)()>(dlsym(engine_lib_handle, "getTtsEngine"));
+
+        pJniStorage->mNativeSynthInterface = (*get_TtsEngine)();
+
+        if (pJniStorage->mNativeSynthInterface) {
+            pJniStorage->mNativeSynthInterface->init(ttsSynthDoneCB);
+        }
+    }
+
+    // we use a weak reference so the SynthProxy object can be garbage collected.
+    pJniStorage->tts_ref = env->NewGlobalRef(weak_this);
+
+    // save the JNI resources so we can use them (and free them) later
+    env->SetIntField(thiz, javaTTSFields.synthProxyFieldJniData,
+            (int)pJniStorage);
+
+    env->ReleaseStringUTFChars(nativeSoLib, nativeSoLibNativeString);
+}
+
+
+static void
+android_tts_SynthProxy_native_finalize(JNIEnv *env, jobject thiz, jint jniData)
+{
+    if (jniData) {
+        SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+        delete pSynthData;
+    }
+}
+
+
+static void
+android_tts_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jint jniData,
+        jstring language)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_setLanguage(): invalid JNI data");
+        return;
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+    const char *langNativeString = env->GetStringUTFChars(language, 0);
+    // TODO check return codes
+    if (pSynthData->mNativeSynthInterface) {
+        pSynthData->mNativeSynthInterface->setLanguage(langNativeString,
+                strlen(langNativeString));
+    }
+    env->ReleaseStringUTFChars(language, langNativeString);
+}
+
+
+static void
+android_tts_SynthProxy_setSpeechRate(JNIEnv *env, jobject thiz, jint jniData,
+        int speechRate)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_setSpeechRate(): invalid JNI data");
+        return;
+    }
+
+    int bufSize = 10;
+    char buffer [bufSize];
+    sprintf(buffer, "%d", speechRate);
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+    LOGI("setting speech rate to %d", speechRate);
+    // TODO check return codes
+    if (pSynthData->mNativeSynthInterface) {
+        pSynthData->mNativeSynthInterface->setProperty("rate", buffer, bufSize);
+    }
+}
+
+
+// TODO: Refactor this to get rid of any assumptions about sample rate, etc.
+static void
+android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData,
+        jstring textJavaString, jstring filenameJavaString)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_synthesizeToFile(): invalid JNI data");
+        return;
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+
+    const char *filenameNativeString =
+            env->GetStringUTFChars(filenameJavaString, 0);
+    const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
+
+    afterSynthData_t* pForAfter = new (afterSynthData_t);
+    pForAfter->jniStorage = jniData;
+    pForAfter->usageMode  = USAGEMODE_WRITE_TO_FILE;
+
+    pForAfter->outputFile = fopen(filenameNativeString, "wb");
+
+    // Write 44 blank bytes for WAV header, then come back and fill them in
+    // after we've written the audio data
+    char header[44];
+    fwrite(header, 1, 44, pForAfter->outputFile);
+
+    unsigned int unique_identifier;
+
+    // TODO check return codes
+    if (pSynthData->mNativeSynthInterface) {
+        pSynthData->mNativeSynthInterface->synthesizeText(textNativeString, pSynthData->mBuffer, pSynthData->mBufferSize,
+                (void *)pForAfter);
+    }
+
+    long filelen = ftell(pForAfter->outputFile);
+
+    int samples = (((int)filelen) - 44) / 2;
+    header[0] = 'R';
+    header[1] = 'I';
+    header[2] = 'F';
+    header[3] = 'F';
+    ((uint32_t *)(&header[4]))[0] = filelen - 8;
+    header[8] = 'W';
+    header[9] = 'A';
+    header[10] = 'V';
+    header[11] = 'E';
+
+    header[12] = 'f';
+    header[13] = 'm';
+    header[14] = 't';
+    header[15] = ' ';
+
+    ((uint32_t *)(&header[16]))[0] = 16;  // size of fmt
+
+    ((unsigned short *)(&header[20]))[0] = 1;  // format
+    ((unsigned short *)(&header[22]))[0] = 1;  // channels
+    ((uint32_t *)(&header[24]))[0] = 22050;  // samplerate
+    ((uint32_t *)(&header[28]))[0] = 44100;  // byterate
+    ((unsigned short *)(&header[32]))[0] = 2;  // block align
+    ((unsigned short *)(&header[34]))[0] = 16;  // bits per sample
+
+    header[36] = 'd';
+    header[37] = 'a';
+    header[38] = 't';
+    header[39] = 'a';
+
+    ((uint32_t *)(&header[40]))[0] = samples * 2;  // size of data
+
+    // Skip back to the beginning and rewrite the header
+    fseek(pForAfter->outputFile, 0, SEEK_SET);
+    fwrite(header, 1, 44, pForAfter->outputFile);
+
+    fflush(pForAfter->outputFile);
+    fclose(pForAfter->outputFile);
+
+    env->ReleaseStringUTFChars(textJavaString, textNativeString);
+    env->ReleaseStringUTFChars(filenameJavaString, filenameNativeString);
+}
+
+
+static void
+android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData,
+        jstring textJavaString)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_speak(): invalid JNI data");
+        return;
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+
+    if (pSynthData->mAudioOut) {
+        pSynthData->mAudioOut->stop();
+        pSynthData->mAudioOut->start();
+    }
+
+    afterSynthData_t* pForAfter = new (afterSynthData_t);
+    pForAfter->jniStorage = jniData;
+    pForAfter->usageMode  = USAGEMODE_PLAY_IMMEDIATELY;
+
+    if (pSynthData->mNativeSynthInterface) {
+        const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
+        pSynthData->mNativeSynthInterface->synthesizeText(textNativeString, pSynthData->mBuffer, pSynthData->mBufferSize,
+                (void *)pForAfter);
+        env->ReleaseStringUTFChars(textJavaString, textNativeString);
+    }
+}
+
+
+static void
+android_tts_SynthProxy_stop(JNIEnv *env, jobject thiz, jint jniData)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_stop(): invalid JNI data");
+        return;
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+
+    if (pSynthData->mNativeSynthInterface) {
+        pSynthData->mNativeSynthInterface->stop();
+    }
+    if (pSynthData->mAudioOut) {
+        pSynthData->mAudioOut->stop();
+    }
+}
+
+
+static void
+android_tts_SynthProxy_shutdown(JNIEnv *env, jobject thiz, jint jniData)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_shutdown(): invalid JNI data");
+        return;
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+    if (pSynthData->mNativeSynthInterface) {
+        pSynthData->mNativeSynthInterface->shutdown();
+        pSynthData->mNativeSynthInterface = NULL;
+    }
+}
+
+
+// TODO add buffer format
+static void
+android_tts_SynthProxy_playAudioBuffer(JNIEnv *env, jobject thiz, jint jniData,
+        int bufferPointer, int bufferSize)
+{
+LOGI("android_tts_SynthProxy_playAudioBuffer");
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_playAudioBuffer(): invalid JNI data");
+        return;
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+    short* wav = (short*) bufferPointer;
+    pSynthData->mAudioOut->write(wav, bufferSize);
+    LOGI("AudioTrack wrote: %d bytes", bufferSize);
+}
+
+
+JNIEXPORT jstring JNICALL
+android_tts_SynthProxy_getLanguage(JNIEnv *env, jobject thiz, jint jniData)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_getLanguage(): invalid JNI data");
+        return env->NewStringUTF("");
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+    size_t bufSize = 100;
+    char buf[bufSize];
+    memset(buf, 0, bufSize);
+    // TODO check return codes
+    if (pSynthData->mNativeSynthInterface) {
+        pSynthData->mNativeSynthInterface->getLanguage(buf, &bufSize);
+    }
+    return env->NewStringUTF(buf);
+}
+
+JNIEXPORT int JNICALL
+android_tts_SynthProxy_getRate(JNIEnv *env, jobject thiz, jint jniData)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_getRate(): invalid JNI data");
+        return 0;
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+    size_t bufSize = 100;
+
+    char buf[bufSize];
+    memset(buf, 0, bufSize);
+    // TODO check return codes
+    if (pSynthData->mNativeSynthInterface) {
+        pSynthData->mNativeSynthInterface->getProperty("rate", buf, &bufSize);
+    }
+    return atoi(buf);
+}
+
+// Dalvik VM type signatures
+static JNINativeMethod gMethods[] = {
+    {   "native_stop",
+        "(I)V",
+        (void*)android_tts_SynthProxy_stop
+    },
+    {   "native_speak",
+        "(ILjava/lang/String;)V",
+        (void*)android_tts_SynthProxy_speak
+    },
+    {   "native_synthesizeToFile",
+        "(ILjava/lang/String;Ljava/lang/String;)V",
+        (void*)android_tts_SynthProxy_synthesizeToFile
+    },
+    {   "native_setLanguage",
+        "(ILjava/lang/String;)V",
+        (void*)android_tts_SynthProxy_setLanguage
+    },
+    {   "native_setSpeechRate",
+        "(II)V",
+        (void*)android_tts_SynthProxy_setSpeechRate
+    },
+    {   "native_playAudioBuffer",
+        "(III)V",
+        (void*)android_tts_SynthProxy_playAudioBuffer
+    },
+    {   "native_getLanguage",
+        "(I)Ljava/lang/String;",
+        (void*)android_tts_SynthProxy_getLanguage
+    },
+    {   "native_getRate",
+        "(I)I",
+        (void*)android_tts_SynthProxy_getRate
+    },
+    {   "native_shutdown",
+        "(I)V",
+        (void*)android_tts_SynthProxy_shutdown
+    },
+    {   "native_setup",
+        "(Ljava/lang/Object;Ljava/lang/String;)V",
+        (void*)android_tts_SynthProxy_native_setup
+    },
+    {   "native_finalize",
+        "(I)V",
+        (void*)android_tts_SynthProxy_native_finalize
+    }
+};
+
+#define SP_JNIDATA_FIELD_NAME                "mJniData"
+#define SP_POSTSPEECHSYNTHESIZED_METHOD_NAME "postNativeSpeechSynthesizedInJava"
+
+// TODO: verify this is the correct path
+static const char* const kClassPathName = "android/tts/SynthProxy";
+
+jint JNI_OnLoad(JavaVM* vm, void* reserved)
+{
+    JNIEnv* env = NULL;
+    jint result = -1;
+    jclass clazz;
+
+    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+        LOGE("ERROR: GetEnv failed\n");
+        goto bail;
+    }
+    assert(env != NULL);
+
+    clazz = env->FindClass(kClassPathName);
+    if (clazz == NULL) {
+        LOGE("Can't find %s", kClassPathName);
+        goto bail;
+    }
+
+    javaTTSFields.synthProxyClass = clazz;
+    javaTTSFields.synthProxyFieldJniData = NULL;
+    javaTTSFields.synthProxyMethodPost = NULL;
+
+    javaTTSFields.synthProxyFieldJniData = env->GetFieldID(clazz,
+            SP_JNIDATA_FIELD_NAME, "I");
+    if (javaTTSFields.synthProxyFieldJniData == NULL) {
+        LOGE("Can't find %s.%s field", kClassPathName, SP_JNIDATA_FIELD_NAME);
+        goto bail;
+    }
+
+    javaTTSFields.synthProxyMethodPost = env->GetStaticMethodID(clazz,
+            SP_POSTSPEECHSYNTHESIZED_METHOD_NAME, "(Ljava/lang/Object;II)V");
+    if (javaTTSFields.synthProxyMethodPost == NULL) {
+        LOGE("Can't find %s.%s method", kClassPathName, SP_POSTSPEECHSYNTHESIZED_METHOD_NAME);
+        goto bail;
+    }
+
+    if (jniRegisterNativeMethods(
+            env, kClassPathName, gMethods, NELEM(gMethods)) < 0)
+        goto bail;
+
+    /* success -- return valid version number */
+    result = JNI_VERSION_1_4;
+
+ bail:
+    return result;
+}
diff --git a/vpn/java/android/net/vpn/IVpnService.aidl b/vpn/java/android/net/vpn/IVpnService.aidl
new file mode 100644
index 0000000..0e658df
--- /dev/null
+++ b/vpn/java/android/net/vpn/IVpnService.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2007, 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.net.vpn;
+
+import android.net.vpn.VpnProfile;
+
+/**
+ * Interface to access a VPN service.
+ * {@hide}
+ */
+interface IVpnService {
+    /**
+     * Sets up the VPN connection.
+     * @param profile the profile object
+     * @param username the username for authentication
+     * @param password the corresponding password for authentication
+     */
+    boolean connect(in VpnProfile profile, String username, String password);
+
+    /**
+     * Tears down the VPN connection.
+     */
+    void disconnect();
+
+    /**
+     * Makes the service broadcast the connectivity state.
+     */
+    void checkStatus(in VpnProfile profile);
+}
diff --git a/vpn/java/android/net/vpn/L2tpIpsecProfile.java b/vpn/java/android/net/vpn/L2tpIpsecProfile.java
new file mode 100644
index 0000000..a7e53d1
--- /dev/null
+++ b/vpn/java/android/net/vpn/L2tpIpsecProfile.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2007, 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.net.vpn;
+
+import android.os.Parcel;
+
+/**
+ * The profile for L2TP-over-IPSec type of VPN.
+ * {@hide}
+ */
+public class L2tpIpsecProfile extends SingleServerProfile {
+    private String mUserCertificate;
+    private String mCaCertificate;
+    private String mUserkey;
+
+    @Override
+    public VpnType getType() {
+        return VpnType.L2TP_IPSEC;
+    }
+
+    public void setCaCertificate(String name) {
+        mCaCertificate = name;
+    }
+
+    public String getCaCertificate() {
+        return mCaCertificate;
+    }
+
+    public void setUserCertificate(String name) {
+        mUserCertificate = name;
+    }
+
+    public String getUserCertificate() {
+        return mUserCertificate;
+    }
+
+    public void setUserkey(String name) {
+        mUserkey = name;
+    }
+
+    public String getUserkey() {
+        return mUserkey;
+    }
+
+    @Override
+    protected void readFromParcel(Parcel in) {
+        super.readFromParcel(in);
+        mCaCertificate = in.readString();
+        mUserCertificate = in.readString();
+        mUserkey = in.readString();
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        super.writeToParcel(parcel, flags);
+        parcel.writeString(mCaCertificate);
+        parcel.writeString(mUserCertificate);
+        parcel.writeString(mUserkey);
+    }
+}
diff --git a/vpn/java/android/net/vpn/L2tpProfile.java b/vpn/java/android/net/vpn/L2tpProfile.java
new file mode 100644
index 0000000..ca4ef75
--- /dev/null
+++ b/vpn/java/android/net/vpn/L2tpProfile.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2007, 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.net.vpn;
+
+/**
+ * The profile for L2TP type of VPN.
+ * {@hide}
+ */
+public class L2tpProfile extends SingleServerProfile {
+    @Override
+    public VpnType getType() {
+        return VpnType.L2TP;
+    }
+}
diff --git a/vpn/java/android/net/vpn/SingleServerProfile.java b/vpn/java/android/net/vpn/SingleServerProfile.java
new file mode 100644
index 0000000..59b5a7b
--- /dev/null
+++ b/vpn/java/android/net/vpn/SingleServerProfile.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2007, 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.net.vpn;
+
+import android.os.Parcel;
+
+/**
+ * The profile for single-server type of VPN.
+ * {@hide}
+ */
+public abstract class SingleServerProfile extends VpnProfile {
+    private String mServerName;
+
+    public void setServerName(String name) {
+        mServerName = name;
+    }
+
+    public String getServerName() {
+        return mServerName;
+    }
+
+    @Override
+    protected void readFromParcel(Parcel in) {
+        super.readFromParcel(in);
+        mServerName = in.readString();
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        super.writeToParcel(parcel, flags);
+        parcel.writeString(mServerName);
+    }
+}
diff --git a/vpn/java/android/net/vpn/VpnManager.java b/vpn/java/android/net/vpn/VpnManager.java
new file mode 100644
index 0000000..6c6e52a
--- /dev/null
+++ b/vpn/java/android/net/vpn/VpnManager.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2007, 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.net.vpn;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.util.Log;
+
+/**
+ * The class provides interface to manage all VPN-related tasks, including:
+ * <ul>
+ * <li>The list of supported VPN types.
+ * <li>API's to start/stop the service of a particular type.
+ * <li>API's to start the settings activity.
+ * <li>API's to create a profile.
+ * <li>API's to register/unregister a connectivity receiver and the keys to
+ *      access the fields in a connectivity broadcast event.
+ * </ul>
+ * {@hide}
+ */
+public class VpnManager {
+    // Action for broadcasting a connectivity state.
+    private static final String ACTION_VPN_CONNECTIVITY = "vpn.connectivity";
+    /** Key to the profile name of a connectivity broadcast event. */
+    public static final String BROADCAST_PROFILE_NAME = "profile_name";
+    /** Key to the connectivity state of a connectivity broadcast event. */
+    public static final String BROADCAST_CONNECTION_STATE = "connection_state";
+
+    public static final String PROFILES_PATH = "/data/misc/vpn/profiles";
+
+    private static final String PACKAGE_PREFIX =
+            VpnManager.class.getPackage().getName() + ".";
+
+    /** Action to start the activity of installing a new profile. */
+    public static final String ACTION_VPN_INSTALL_PROFILE =
+            PACKAGE_PREFIX + "INSTALL_PROFILE";
+    /**
+     * Key to the installation path in the intent of installing a new profile.
+     */
+    public static final String KEY_INSTALLATION_PATH = "install_path";
+    public static final String DEFAULT_INSTALLATION_PATH =
+            "/data/local/tmp/vpn";
+
+    // Action to start VPN installation monitor service
+    private static final String SERVICE_VPN_INSTALL_MONITOR =
+            PACKAGE_PREFIX + "INSTALLATION_MONITOR";
+
+    // Action to start VPN settings
+    private static final String ACTION_VPN_SETTINGS = PACKAGE_PREFIX + "SETTINGS";
+
+    private static final String TAG = VpnManager.class.getSimpleName();
+
+    /**
+     * Returns all supported VPN types.
+     */
+    public static VpnType[] getSupportedVpnTypes() {
+        return VpnType.values();
+    }
+
+    private Context mContext;
+
+    /**
+     * Creates a manager object with the specified context.
+     */
+    public VpnManager(Context c) {
+        mContext = c;
+    }
+
+    /**
+     * Creates a VPN profile of the specified type.
+     *
+     * @param type the VPN type
+     * @return the profile object
+     */
+    public VpnProfile createVpnProfile(VpnType type) {
+        return createVpnProfile(type, false);
+    }
+
+    /**
+     * Creates a VPN profile of the specified type.
+     *
+     * @param type the VPN type
+     * @param customized true if the profile is custom made
+     * @return the profile object
+     */
+    public VpnProfile createVpnProfile(VpnType type, boolean customized) {
+        try {
+            VpnProfile p = (VpnProfile) type.getProfileClass().newInstance();
+            p.setCustomized(customized);
+            return p;
+        } catch (InstantiationException e) {
+            return null;
+        } catch (IllegalAccessException e) {
+            return null;
+        }
+    }
+
+    private String getServiceActionName(VpnType type) {
+        return PACKAGE_PREFIX + type.getServiceName();
+    }
+
+    /**
+     * Starts the VPN service of the specified type.
+     */
+    public boolean startService(VpnType type) {
+        String serviceAction = getServiceActionName(type);
+        if (serviceAction != null) {
+            Log.i(TAG, "start service: " + serviceAction);
+            mContext.startService(new Intent(serviceAction));
+            return true;
+        } else {
+            Log.w(TAG, "unknown vpn type to start service for: " + type);
+            return false;
+        }
+    }
+
+    /**
+     * Stops the VPN service of the specified type.
+     */
+    public void stopService(VpnType type) {
+        String serviceAction = getServiceActionName(type);
+        if (serviceAction != null) {
+            Log.i(TAG, "stop service for: " + type);
+            mContext.stopService(new Intent(serviceAction));
+        } else {
+            Log.w(TAG, "unknown vpn type to stop service for: " + type);
+        }
+    }
+
+    /**
+     * Binds the specified ServiceConnection with the VPN service of the
+     * specified type.
+     */
+    public boolean bindService(VpnType type, ServiceConnection c) {
+        String serviceAction = getServiceActionName(type);
+        if (serviceAction == null) {
+            Log.w(TAG, "unknown vpn type to bind service for: " + type);
+            return false;
+        }
+        if (!mContext.bindService(new Intent(serviceAction), c, 0)) {
+            Log.w(TAG, "failed to connect to service: " + type);
+            return false;
+        } else {
+            Log.v(TAG, "succeeded to connect to service: " + type);
+            return true;
+        }
+    }
+
+    /** Broadcasts the connectivity state of the specified profile. */
+    public void broadcastConnectivity(String profileName, VpnState s) {
+        Intent intent = new Intent(ACTION_VPN_CONNECTIVITY);
+        intent.putExtra(BROADCAST_PROFILE_NAME, profileName);
+        intent.putExtra(BROADCAST_CONNECTION_STATE, s);
+        mContext.sendBroadcast(intent);
+    }
+
+    public void registerConnectivityReceiver(BroadcastReceiver r) {
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(VpnManager.ACTION_VPN_CONNECTIVITY);
+        mContext.registerReceiver(r, filter);
+    }
+
+    public void unregisterConnectivityReceiver(BroadcastReceiver r) {
+        mContext.unregisterReceiver(r);
+    }
+
+    /**
+     * Starts the installation monitor service.
+     * The service monitors the default installtion path (under /data/local/tmp)
+     * and automatically starts the activity to create a new profile when new
+     * configuration files appear in that path.
+     */
+    public void startInstallationMonitorService() {
+        mContext.startService(new Intent(SERVICE_VPN_INSTALL_MONITOR));
+    }
+
+    /** Stops the installation monitor service. */
+    public void stopInstallationMonitorService() {
+        mContext.stopService(new Intent(SERVICE_VPN_INSTALL_MONITOR));
+    }
+
+    /** Starts the VPN settings activity. */
+    public void startSettingsActivity() {
+        Intent intent = new Intent(ACTION_VPN_SETTINGS);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(intent);
+    }
+
+    /**
+     * Starts the activity to install a customized profile.
+     * @param installPath the path where all the configuration files are located
+     */
+    public void startInstallProfileActivity(String installPath) {
+        Intent intent = new Intent(ACTION_VPN_INSTALL_PROFILE);
+        intent.putExtra(KEY_INSTALLATION_PATH, installPath);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(intent);
+    }
+}
diff --git a/vpn/java/android/net/vpn/VpnProfile.aidl b/vpn/java/android/net/vpn/VpnProfile.aidl
new file mode 100644
index 0000000..ad34bfc
--- /dev/null
+++ b/vpn/java/android/net/vpn/VpnProfile.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2007, 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.net.vpn;
+
+parcelable VpnProfile;
diff --git a/vpn/java/android/net/vpn/VpnProfile.java b/vpn/java/android/net/vpn/VpnProfile.java
new file mode 100644
index 0000000..7cf2608
--- /dev/null
+++ b/vpn/java/android/net/vpn/VpnProfile.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2007, 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.net.vpn;
+
+import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+/**
+ * A VPN profile.
+ * {@hide}
+ */
+public abstract class VpnProfile implements Parcelable {
+    private String mName; // unique display name
+    private String mId; // unique identifier
+    private String mDomainSuffices; // space separated list
+    private String mRouteList; // space separated list
+    private boolean mIsCustomized;
+    private transient VpnState mState = VpnState.IDLE;
+
+    /** Sets a user-friendly name for this profile. */
+    public void setName(String name) {
+        mName = name;
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Sets an ID for this profile.  The caller should make sure the
+     * uniqueness of the ID.
+     */
+    public void setId(String id) {
+        mId = id;
+    }
+
+    public String getId() {
+        return mId;
+    }
+
+    /**
+     * Sets the domain suffices for DNS resolution.
+     *
+     * @param entries a comma-separated list of domain suffices
+     */
+    public void setDomainSuffices(String entries) {
+        mDomainSuffices = entries;
+    }
+
+    public String getDomainSuffices() {
+        return mDomainSuffices;
+    }
+
+    /**
+     * Sets the routing info for this VPN connection.
+     *
+     * @param entries a comma-separated list of routes; each entry is in the
+     *      format of "(network address)/(network mask)"
+     */
+    public void setRouteList(String entries) {
+        mRouteList = entries;
+    }
+
+    public String getRouteList() {
+        return mRouteList;
+    }
+
+    public void setState(VpnState state) {
+        mState = state;
+    }
+
+    public VpnState getState() {
+        return ((mState == null) ? VpnState.IDLE : mState);
+    }
+
+    public boolean isIdle() {
+        return (mState == VpnState.IDLE);
+    }
+
+    /**
+     * Returns whether this profile is custom made (as opposed to being
+     * created by provided user interface).
+     */
+    public boolean isCustomized() {
+        return mIsCustomized;
+    }
+
+    /**
+     * Returns the VPN type of the profile.
+     */
+    public abstract VpnType getType();
+
+    void setCustomized(boolean customized) {
+        mIsCustomized = customized;
+    }
+
+    protected void readFromParcel(Parcel in) {
+        mName = in.readString();
+        mId = in.readString();
+        mDomainSuffices = in.readString();
+        mRouteList = in.readString();
+    }
+
+    public static final Parcelable.Creator<VpnProfile> CREATOR =
+            new Parcelable.Creator<VpnProfile>() {
+                public VpnProfile createFromParcel(Parcel in) {
+                    VpnType type = Enum.valueOf(VpnType.class, in.readString());
+                    boolean customized = in.readInt() > 0;
+                    VpnProfile p = new VpnManager(null).createVpnProfile(type,
+                            customized);
+                    if (p == null) return null;
+                    p.readFromParcel(in);
+                    return p;
+                }
+
+                public VpnProfile[] newArray(int size) {
+                    return new VpnProfile[size];
+                }
+            };
+
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString(getType().toString());
+        parcel.writeInt(mIsCustomized ? 1 : 0);
+        parcel.writeString(mName);
+        parcel.writeString(mId);
+        parcel.writeString(mDomainSuffices);
+        parcel.writeString(mRouteList);
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/vpn/java/android/net/vpn/VpnState.java b/vpn/java/android/net/vpn/VpnState.java
new file mode 100644
index 0000000..78117e0
--- /dev/null
+++ b/vpn/java/android/net/vpn/VpnState.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2007, 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.net.vpn;
+
+/**
+ * Enumeration of all VPN states.
+ *
+ * A normal VPN connection lifetime starts in {@link IDLE}. When a new
+ * connection is about to be set up, it goes to {@link CONNECTING} and then
+ * {@link CONNECTED} if successful; back to {@link IDLE} if failed.
+ * When the connection is about to be torn down, it goes to
+ * {@link DISCONNECTING} and then {@link IDLE}.
+ * {@hide}
+ */
+public enum VpnState {
+    CONNECTING, DISCONNECTING, CONNECTED, IDLE
+}
diff --git a/vpn/java/android/net/vpn/VpnType.java b/vpn/java/android/net/vpn/VpnType.java
new file mode 100644
index 0000000..dcf2078
--- /dev/null
+++ b/vpn/java/android/net/vpn/VpnType.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2007, 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.net.vpn;
+
+/**
+ * Enumeration of all supported VPN types.
+ * {@hide}
+ */
+public enum VpnType {
+    L2TP_IPSEC("L2TP/IPSec", L2tpIpsecProfile.class),
+    L2TP("L2TP", L2tpProfile.class);
+
+    private String mDisplayName;
+    private Class<? extends VpnProfile> mClass;
+
+    VpnType(String displayName, Class<? extends VpnProfile> klass) {
+        mDisplayName = displayName;
+        mClass = klass;
+    }
+
+    public String getDisplayName() {
+        return mDisplayName;
+    }
+
+    public Class<? extends VpnProfile> getProfileClass() {
+        return mClass;
+    }
+
+    public String getServiceName() {
+        return this.toString();
+    }
+}