Merge "changes to SurfaceTexture needed for unification with SF"
diff --git a/Android.mk b/Android.mk
index 28340cf..cd9ae7d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -137,8 +137,8 @@
 	core/java/android/view/IWindowSession.aidl \
 	core/java/android/speech/IRecognitionListener.aidl \
 	core/java/android/speech/IRecognitionService.aidl \
-	core/java/android/speech/tts/ITts.aidl \
-	core/java/android/speech/tts/ITtsCallback.aidl \
+	core/java/android/speech/tts/ITextToSpeechCallback.aidl \
+	core/java/android/speech/tts/ITextToSpeechService.aidl \
 	core/java/com/android/internal/app/IBatteryStats.aidl \
 	core/java/com/android/internal/app/IUsageStats.aidl \
 	core/java/com/android/internal/app/IMediaContainerService.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 023ce59..50292e4 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -96,6 +96,7 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/RSTest_intermediates/)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/hardware/IUsbManager.java)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/nfc)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
 
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/api/current.txt b/api/current.txt
index e946c06..975444e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -87,6 +87,7 @@
     field public static final java.lang.String RECEIVE_SMS = "android.permission.RECEIVE_SMS";
     field public static final java.lang.String RECEIVE_WAP_PUSH = "android.permission.RECEIVE_WAP_PUSH";
     field public static final java.lang.String RECORD_AUDIO = "android.permission.RECORD_AUDIO";
+    field public static final java.lang.String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
     field public static final java.lang.String REORDER_TASKS = "android.permission.REORDER_TASKS";
     field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
     field public static final java.lang.String SEND_SMS = "android.permission.SEND_SMS";
@@ -855,6 +856,7 @@
     field public static final int state_window_focused = 16842909; // 0x101009d
     field public static final int staticWallpaperPreview = 16843569; // 0x1010331
     field public static final int stepSize = 16843078; // 0x1010146
+    field public static final int stopWithTask = 16843623; // 0x1010367
     field public static final int streamType = 16843273; // 0x1010209
     field public static final int stretchColumns = 16843081; // 0x1010149
     field public static final int stretchMode = 16843030; // 0x1010116
@@ -929,9 +931,9 @@
     field public static final int textEditPasteWindowLayout = 16843540; // 0x1010314
     field public static final int textEditSideNoPasteWindowLayout = 16843615; // 0x101035f
     field public static final int textEditSidePasteWindowLayout = 16843614; // 0x101035e
-    field public static final int textEditSuggestionItemLayout = 16843626; // 0x101036a
-    field public static final int textEditSuggestionsBottomWindowLayout = 16843624; // 0x1010368
-    field public static final int textEditSuggestionsTopWindowLayout = 16843625; // 0x1010369
+    field public static final int textEditSuggestionItemLayout = 16843627; // 0x101036b
+    field public static final int textEditSuggestionsBottomWindowLayout = 16843625; // 0x1010369
+    field public static final int textEditSuggestionsTopWindowLayout = 16843626; // 0x101036a
     field public static final int textFilterEnabled = 16843007; // 0x10100ff
     field public static final int textIsSelectable = 16843542; // 0x1010316
     field public static final int textOff = 16843045; // 0x1010125
@@ -943,7 +945,7 @@
     field public static final int textSelectHandleWindowStyle = 16843464; // 0x10102c8
     field public static final int textSize = 16842901; // 0x1010095
     field public static final int textStyle = 16842903; // 0x1010097
-    field public static final int textSuggestionsWindowStyle = 16843623; // 0x1010367
+    field public static final int textSuggestionsWindowStyle = 16843624; // 0x1010368
     field public static final int textViewStyle = 16842884; // 0x1010084
     field public static final int theme = 16842752; // 0x1010000
     field public static final int thickness = 16843360; // 0x1010260
@@ -2849,6 +2851,7 @@
     method public void onSaveInstanceState(android.os.Bundle);
     method public void onStart();
     method public void onStop();
+    method public void onViewCreated(android.view.View, android.os.Bundle);
     method public void registerForContextMenu(android.view.View);
     method public void setArguments(android.os.Bundle);
     method public void setHasOptionsMenu(boolean);
@@ -2921,8 +2924,10 @@
     method public abstract android.app.FragmentTransaction add(int, android.app.Fragment);
     method public abstract android.app.FragmentTransaction add(int, android.app.Fragment, java.lang.String);
     method public abstract android.app.FragmentTransaction addToBackStack(java.lang.String);
+    method public abstract android.app.FragmentTransaction attach(android.app.Fragment);
     method public abstract int commit();
     method public abstract int commitAllowingStateLoss();
+    method public abstract android.app.FragmentTransaction detach(android.app.Fragment);
     method public abstract android.app.FragmentTransaction disallowAddToBackStack();
     method public abstract android.app.FragmentTransaction hide(android.app.Fragment);
     method public abstract boolean isAddToBackStackAllowed();
@@ -3384,6 +3389,7 @@
     method public void onRebind(android.content.Intent);
     method public deprecated void onStart(android.content.Intent, int);
     method public int onStartCommand(android.content.Intent, int, int);
+    method public void onTaskRemoved(android.content.Intent);
     method public boolean onUnbind(android.content.Intent);
     method public final void startForeground(int, android.app.Notification);
     method public final void stopForeground(boolean);
@@ -5840,6 +5846,8 @@
     method public int describeContents();
     method public void dump(android.util.Printer, java.lang.String);
     field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final int FLAG_STOP_WITH_TASK = 1; // 0x1
+    field public int flags;
     field public java.lang.String permission;
   }
 
@@ -22574,6 +22582,7 @@
     method public java.util.List<android.view.inputmethod.InputMethodInfo> getEnabledInputMethodList();
     method public java.util.List<android.view.inputmethod.InputMethodSubtype> getEnabledInputMethodSubtypeList(android.view.inputmethod.InputMethodInfo, boolean);
     method public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodList();
+    method public android.view.inputmethod.InputMethodSubtype getLastInputMethodSubtype();
     method public java.util.Map<android.view.inputmethod.InputMethodInfo, java.util.List<android.view.inputmethod.InputMethodSubtype>> getShortcutInputMethodsAndSubtypes();
     method public void hideSoftInputFromInputMethod(android.os.IBinder, int);
     method public boolean hideSoftInputFromWindow(android.os.IBinder, int);
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index 0159edd..371268f 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -1,8 +1,8 @@
 /*
  * Main entry of app process.
- * 
+ *
  * Starts the interpreted runtime, then starts up the application.
- * 
+ *
  */
 
 #define LOG_TAG "appproc"
@@ -25,23 +25,13 @@
         "Usage: app_process [java-options] cmd-dir start-class-name [options]\n");
 }
 
-status_t app_init(const char* className, int argc, const char* const argv[])
-{
-    LOGV("Entered app_init()!\n");
-
-    AndroidRuntime* jr = AndroidRuntime::getRuntime();
-    jr->callMain(className, argc, argv);
-    
-    LOGV("Exiting app_init()!\n");
-    return NO_ERROR;
-}
-
 class AppRuntime : public AndroidRuntime
 {
 public:
     AppRuntime()
         : mParentDir(NULL)
         , mClassName(NULL)
+        , mClass(NULL)
         , mArgC(0)
         , mArgV(NULL)
     {
@@ -60,6 +50,35 @@
         return mClassName;
     }
 
+    virtual void onVmCreated(JNIEnv* env)
+    {
+        if (mClassName == NULL) {
+            return; // Zygote. Nothing to do here.
+        }
+
+        /*
+         * This is a little awkward because the JNI FindClass call uses the
+         * class loader associated with the native method we're executing in.
+         * If called in onStarted (from RuntimeInit.finishInit because we're
+         * launching "am", for example), FindClass would see that we're calling
+         * from a boot class' native method, and so wouldn't look for the class
+         * we're trying to look up in CLASSPATH. Unfortunately it needs to,
+         * because the "am" classes are not boot classes.
+         *
+         * The easiest fix is to call FindClass here, early on before we start
+         * executing boot class Java code and thereby deny ourselves access to
+         * non-boot classes.
+         */
+        char* slashClassName = toSlashClassName(mClassName);
+        mClass = env->FindClass(slashClassName);
+        if (mClass == NULL) {
+            LOGE("ERROR: could not find class '%s'\n", mClassName);
+        }
+        free(slashClassName);
+
+        mClass = reinterpret_cast<jclass>(env->NewGlobalRef(mClass));
+    }
+
     virtual void onStarted()
     {
         sp<ProcessState> proc = ProcessState::self();
@@ -67,8 +86,9 @@
             LOGV("App process: starting thread pool.\n");
             proc->startThreadPool();
         }
-        
-        app_init(mClassName, mArgC, mArgV);
+
+        AndroidRuntime* ar = AndroidRuntime::getRuntime();
+        ar->callMain(mClassName, mClass, mArgC, mArgV);
 
         if (ProcessState::self()->supportsProcesses()) {
             IPCThreadState::self()->stopProcess();
@@ -81,7 +101,7 @@
         if (proc->supportsProcesses()) {
             LOGV("App process: starting thread pool.\n");
             proc->startThreadPool();
-        }       
+        }
     }
 
     virtual void onExit(int code)
@@ -96,9 +116,10 @@
         AndroidRuntime::onExit(code);
     }
 
-    
+
     const char* mParentDir;
     const char* mClassName;
+    jclass mClass;
     int mArgC;
     const char* const* mArgV;
 };
@@ -120,7 +141,7 @@
     // These are global variables in ProcessState.cpp
     mArgC = argc;
     mArgV = argv;
-    
+
     mArgLen = 0;
     for (int i=0; i<argc; i++) {
         mArgLen += strlen(argv[i]) + 1;
@@ -139,7 +160,7 @@
     argv++;
 
     // Everything up to '--' or first non '-' arg goes to the vm
-    
+
     int i = runtime.addVmArguments(argc, argv);
 
     // Next arg is parent directory
@@ -151,7 +172,7 @@
     if (i < argc) {
         arg = argv[i++];
         if (0 == strcmp("--zygote", arg)) {
-            bool startSystemServer = (i < argc) ? 
+            bool startSystemServer = (i < argc) ?
                     strcmp(argv[i], "--start-system-server") == 0 : false;
             setArgv0(argv0, "zygote");
             set_process_name("zygote");
diff --git a/cmds/runtime/main_runtime.cpp b/cmds/runtime/main_runtime.cpp
index 83cb533..785e4cc 100644
--- a/cmds/runtime/main_runtime.cpp
+++ b/cmds/runtime/main_runtime.cpp
@@ -12,7 +12,7 @@
 
 #include <binder/IPCThreadState.h>
 #include <binder/ProcessState.h>
-#include <utils/Log.h>  
+#include <utils/Log.h>
 #include <cutils/zygote.h>
 
 #include <cutils/properties.h>
@@ -41,7 +41,7 @@
 #undef LOG_TAG
 #define LOG_TAG "runtime"
 
-static const char* ZYGOTE_ARGV[] = { 
+static const char* ZYGOTE_ARGV[] = {
     "--setuid=1000",
     "--setgid=1000",
     "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,3001,3002,3003",
@@ -68,7 +68,6 @@
 
 namespace android {
 
-extern status_t app_init(const char* className);
 extern void set_finish_init_func(void (*func)());
 
 
@@ -76,7 +75,7 @@
  * This class is used to kill this process (runtime) when the system_server dies.
  */
 class GrimReaper : public IBinder::DeathRecipient {
-public: 
+public:
     GrimReaper() { }
 
     virtual void binderDied(const wp<IBinder>& who)
@@ -170,7 +169,7 @@
 
 /*
  * Post-system-process initialization.
- * 
+ *
  * This function continues initialization after the system process
  * has been initialized.  It needs to be separate because the system
  * initialization needs to care of starting the Android runtime if it is not
@@ -210,17 +209,17 @@
 static void boot_init()
 {
     LOGI("Entered boot_init()!\n");
-    
+
     sp<ProcessState> proc(ProcessState::self());
     LOGD("ProcessState: %p\n", proc.get());
     proc->becomeContextManager(contextChecker, NULL);
-    
+
     if (proc->supportsProcesses()) {
         LOGI("Binder driver opened.  Multiprocess enabled.\n");
     } else {
         LOGI("Binder driver not found.  Processes not supported.\n");
     }
-    
+
     sp<BServiceManager> sm = new BServiceManager;
     proc->setContextObject(sm);
 }
@@ -258,7 +257,7 @@
     int res;
     time_t min_time = 1167652800; // jan 1 2007, type 'date -ud "1/1 12:00" +%s' to get value for current year
     struct timespec ts;
-    
+
     fd = open("/dev/alarm", O_RDWR);
     if(fd < 0) {
         LOGW("Unable to open alarm driver: %s\n", strerror(errno));
@@ -346,14 +345,14 @@
     int ic;
     int result = 1;
     pid_t systemPid;
-    
+
     sp<ProcessState> proc;
 
 #ifndef HAVE_ANDROID_OS
     /* Set stdout/stderr to unbuffered for MinGW/MSYS. */
     //setvbuf(stdout, NULL, _IONBF, 0);
     //setvbuf(stderr, NULL, _IONBF, 0);
-    
+
     LOGI("commandline args:\n");
     for (int i = 0; i < argc; i++)
         LOGI("  %2d: '%s'\n", i, argv[i]);
@@ -455,7 +454,7 @@
 
 #if 0
     // Hack to keep libc from beating the filesystem to death.  It's
-    // hitting /etc/localtime frequently, 
+    // hitting /etc/localtime frequently,
     //
     // This statement locks us into Pacific time.  We could do better,
     // but there's not much point until we're sure that the library
@@ -467,15 +466,15 @@
 
     /* track our progress through the boot sequence */
     const int LOG_BOOT_PROGRESS_START = 3000;
-    LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, 
+    LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,
         ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
 
     validateTime();
 
     proc = ProcessState::self();
-    
+
     boot_init();
-    
+
     /* If we are in multiprocess mode, have zygote spawn the system
      * server process and call system_init(). If we are running in
      * single process mode just call system_init() directly.
@@ -488,8 +487,8 @@
         property_get("log.redirect-stdio", propBuf, "");
         logStdio = (strcmp(propBuf, "true") == 0);
 
-        zygote_run_oneshot((int)(!logStdio), 
-                sizeof(ZYGOTE_ARGV) / sizeof(ZYGOTE_ARGV[0]), 
+        zygote_run_oneshot((int)(!logStdio),
+                sizeof(ZYGOTE_ARGV) / sizeof(ZYGOTE_ARGV[0]),
                 ZYGOTE_ARGV);
 
         //start_process("/system/bin/mediaserver");
@@ -497,7 +496,7 @@
     } else {
 #ifndef HAVE_ANDROID_OS
         QuickRuntime* runt = new QuickRuntime();
-        runt->start("com/android/server/SystemServer", 
+        runt->start("com/android/server/SystemServer",
                     false /* spontaneously fork system server from zygote */);
 #endif
     }
@@ -506,11 +505,11 @@
 
     finish_system_init(proc);
     run(proc);
-    
+
 bail:
     if (proc != NULL) {
         proc->setContextObject(NULL);
     }
-    
+
     return 0;
 }
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index bb84bd1..be443d0 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -107,7 +107,7 @@
         : mEOS(false) {
     }
 
-    virtual void notify(int msg, int ext1, int ext2) {
+    virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) {
         Mutex::Autolock autoLock(mLock);
 
         if (msg == MEDIA_ERROR || msg == MEDIA_PLAYBACK_COMPLETE) {
diff --git a/cmds/system_server/library/system_init.cpp b/cmds/system_server/library/system_init.cpp
index a29ba73..b615764 100644
--- a/cmds/system_server/library/system_init.cpp
+++ b/cmds/system_server/library/system_init.cpp
@@ -37,7 +37,7 @@
  * This class is used to kill this process when the runtime dies.
  */
 class GrimReaper : public IBinder::DeathRecipient {
-public: 
+public:
     GrimReaper() { }
 
     virtual void binderDied(const wp<IBinder>& who)
@@ -54,15 +54,15 @@
 extern "C" status_t system_init()
 {
     LOGI("Entered system_init()");
-    
+
     sp<ProcessState> proc(ProcessState::self());
-    
+
     sp<IServiceManager> sm = defaultServiceManager();
     LOGI("ServiceManager: %p\n", sm.get());
-    
+
     sp<GrimReaper> grim = new GrimReaper();
     sm->asBinder()->linkToDeath(grim, grim.get(), 0);
-    
+
     char propBuf[PROPERTY_VALUE_MAX];
     property_get("system_init.startsurfaceflinger", propBuf, "1");
     if (strcmp(propBuf, "1") == 0) {
@@ -97,12 +97,23 @@
     // the beginning of their processes's main(), before calling
     // the init function.
     LOGI("System server: starting Android runtime.\n");
-    
     AndroidRuntime* runtime = AndroidRuntime::getRuntime();
 
     LOGI("System server: starting Android services.\n");
-    runtime->callStatic("com/android/server/SystemServer", "init2");
-        
+    JNIEnv* env = runtime->getJNIEnv();
+    if (env == NULL) {
+        return UNKNOWN_ERROR;
+    }
+    jclass clazz = env->FindClass("com/android/server/SystemServer");
+    if (clazz == NULL) {
+        return UNKNOWN_ERROR;
+    }
+    jmethodID methodId = env->GetStaticMethodID(clazz, "init2", "()V");
+    if (methodId == NULL) {
+        return UNKNOWN_ERROR;
+    }
+    env->CallStaticVoidMethod(clazz, methodId);
+
     // If running in our own process, just go into the thread
     // pool.  Otherwise, call the initialization finished
     // func to let this process continue its initilization.
@@ -114,4 +125,3 @@
     }
     return NO_ERROR;
 }
-
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 1b4ef0d..fca6868 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -16,6 +16,9 @@
 
 package android.app;
 
+import com.android.internal.app.IUsageStats;
+import com.android.internal.os.PkgUsageStats;
+
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -26,17 +29,15 @@
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.os.Debug;
-import android.os.RemoteException;
 import android.os.Handler;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.Log;
-import com.android.internal.app.IUsageStats;
-import com.android.internal.os.PkgUsageStats;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -204,13 +205,6 @@
     public static final int RECENT_IGNORE_UNAVAILABLE = 0x0002;
 
     /**
-     * Flag for use with {@link #getRecentTasks}: also return the thumbnail
-     * bitmap (if available) for each recent task.
-     * @hide
-     */
-    public static final int TASKS_GET_THUMBNAILS = 0x0001000;
-    
-    /**
      * Return a list of the tasks that the user has recently launched, with
      * the most recent being first and older ones after in order.
      * 
@@ -240,7 +234,7 @@
     /**
      * Information you can retrieve about a particular task that is currently
      * "running" in the system.  Note that a running task does not mean the
-     * given task actual has a process it is actively running in; it simply
+     * given task actually has a process it is actively running in; it simply
      * means that the user has gone to it and never closed it, but currently
      * the system may have killed its process and is only holding on to its
      * last state in order to restart it when the user returns.
@@ -395,6 +389,55 @@
         return getRunningTasks(maxNum, 0, null);
     }
 
+    /**
+     * Remove some end of a task's activity stack that is not part of
+     * the main application.  The selected activities will be finished, so
+     * they are no longer part of the main task.
+     *
+     * @param taskId The identifier of the task.
+     * @param subTaskIndex The number of the sub-task; this corresponds
+     * to the index of the thumbnail returned by {@link #getTaskThumbnails(int)}.
+     * @return Returns true if the sub-task was found and was removed.
+     *
+     * @hide
+     */
+    public boolean removeSubTask(int taskId, int subTaskIndex)
+            throws SecurityException {
+        try {
+            return ActivityManagerNative.getDefault().removeSubTask(taskId, subTaskIndex);
+        } catch (RemoteException e) {
+            // System dead, we will be dead too soon!
+            return false;
+        }
+    }
+
+    /**
+     * If set, the process of the root activity of the task will be killed
+     * as part of removing the task.
+     * @hide
+     */
+    public static final int REMOVE_TASK_KILL_PROCESS = 0x0001;
+
+    /**
+     * Completely remove the given task.
+     *
+     * @param taskId Identifier of the task to be removed.
+     * @param flags Additional operational flags.  May be 0 or
+     * {@link #REMOVE_TASK_KILL_PROCESS}.
+     * @return Returns true if the given task was found and removed.
+     *
+     * @hide
+     */
+    public boolean removeTask(int taskId, int flags)
+            throws SecurityException {
+        try {
+            return ActivityManagerNative.getDefault().removeTask(taskId, flags);
+        } catch (RemoteException e) {
+            // System dead, we will be dead too soon!
+            return false;
+        }
+    }
+
     /** @hide */
     public static class TaskThumbnails implements Parcelable {
         public Bitmap mainThumbnail;
@@ -404,9 +447,6 @@
         /** @hide */
         public IThumbnailRetriever retriever;
 
-        /** @hide Magic for ActivityManagerService.  Not marshalled */
-        public ArrayList<Bitmap> otherThumbnails;
-
         public TaskThumbnails() {
         }
 
@@ -676,7 +716,7 @@
     public List<RunningServiceInfo> getRunningServices(int maxNum)
             throws SecurityException {
         try {
-            return (List<RunningServiceInfo>)ActivityManagerNative.getDefault()
+            return ActivityManagerNative.getDefault()
                     .getServices(maxNum, 0);
         } catch (RemoteException e) {
             // System dead, we will be dead too soon!
@@ -1331,4 +1371,17 @@
             return new HashMap<String, Integer>();
         }
     }
+
+    /**
+     * @param userid the user's id. Zero indicates the default user 
+     * @hide
+     */
+    public boolean switchUser(int userid) {
+        try {
+            return ActivityManagerNative.getDefault().switchUser(userid);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
 }
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 5fbc70f..4b09b34c 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1397,6 +1397,37 @@
             return true;
         }
 
+        case SWITCH_USER_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            int userid = data.readInt();
+            boolean result = switchUser(userid);
+            reply.writeNoException();
+            reply.writeInt(result ? 1 : 0);
+            return true;
+        }
+        
+        case REMOVE_SUB_TASK_TRANSACTION:
+        {
+            data.enforceInterface(IActivityManager.descriptor);
+            int taskId = data.readInt();
+            int subTaskIndex = data.readInt();
+            boolean result = removeSubTask(taskId, subTaskIndex);
+            reply.writeNoException();
+            reply.writeInt(result ? 1 : 0);
+            return true;
+        }
+
+        case REMOVE_TASK_TRANSACTION:
+        {
+            data.enforceInterface(IActivityManager.descriptor);
+            int taskId = data.readInt();
+            int fl = data.readInt();
+            boolean result = removeTask(taskId, fl);
+            reply.writeNoException();
+            reply.writeInt(result ? 1 : 0);
+            return true;
+        }
+
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -3141,5 +3172,46 @@
         return result;
     }
 
+    public boolean switchUser(int userid) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeInt(userid);
+        mRemote.transact(SWITCH_USER_TRANSACTION, data, reply, 0);
+        reply.readException();
+        boolean result = reply.readInt() != 0;
+        reply.recycle();
+        data.recycle();
+        return result;
+    }
+    
+    public boolean removeSubTask(int taskId, int subTaskIndex) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeInt(taskId);
+        data.writeInt(subTaskIndex);
+        mRemote.transact(REMOVE_SUB_TASK_TRANSACTION, data, reply, 0);
+        reply.readException();
+        boolean result = reply.readInt() != 0;
+        reply.recycle();
+        data.recycle();
+        return result;
+    }
+
+    public boolean removeTask(int taskId, int flags) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeInt(taskId);
+        data.writeInt(flags);
+        mRemote.transact(REMOVE_TASK_TRANSACTION, data, reply, 0);
+        reply.readException();
+        boolean result = reply.readInt() != 0;
+        reply.recycle();
+        data.recycle();
+        return result;
+    }
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 57a79a9..4dfba91 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -334,6 +334,7 @@
 
     private static final class ServiceArgsData {
         IBinder token;
+        boolean taskRemoved;
         int startId;
         int flags;
         Intent args;
@@ -534,10 +535,11 @@
             queueOrSendMessage(H.UNBIND_SERVICE, s);
         }
 
-        public final void scheduleServiceArgs(IBinder token, int startId,
+        public final void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
             int flags ,Intent args) {
             ServiceArgsData s = new ServiceArgsData();
             s.token = token;
+            s.taskRemoved = taskRemoved;
             s.startId = startId;
             s.flags = flags;
             s.args = args;
@@ -2129,7 +2131,13 @@
                 if (data.args != null) {
                     data.args.setExtrasClassLoader(s.getClassLoader());
                 }
-                int res = s.onStartCommand(data.args, data.flags, data.startId);
+                int res;
+                if (!data.taskRemoved) {
+                    res = s.onStartCommand(data.args, data.flags, data.startId);
+                } else {
+                    s.onTaskRemoved(data.args);
+                    res = Service.START_TASK_REMOVED_COMPLETE;
+                }
 
                 QueuedWork.waitToFinish();
 
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 50e56c7..ef8ba8e 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -33,13 +33,13 @@
 import android.content.pm.IPackageStatsObserver;
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManager;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.graphics.drawable.Drawable;
@@ -1106,6 +1106,56 @@
         return PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
     }
 
+    // Multi-user support
+
+    /**
+     * @hide
+     */
+    @Override
+    public UserInfo createUser(String name, int flags) {
+        // TODO
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public List<UserInfo> getUsers() {
+        // TODO:
+        // Dummy code, always returns just the primary user
+        ArrayList<UserInfo> users = new ArrayList<UserInfo>();
+        UserInfo primary = new UserInfo(0, "Root!",
+                UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
+        users.add(primary);
+        return users;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean removeUser(int id) {
+        // TODO:
+        return false;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void updateUserName(int id, String name) {
+        // TODO:
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void updateUserFlags(int id, int flags) {
+        // TODO:
+    }
+
     private final ContextImpl mContext;
     private final IPackageManager mPM;
 
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index a82234e..0e511f2 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -219,6 +219,7 @@
         {
             data.enforceInterface(IApplicationThread.descriptor);
             IBinder token = data.readStrongBinder();
+            boolean taskRemoved = data.readInt() != 0;
             int startId = data.readInt();
             int fl = data.readInt();
             Intent args;
@@ -227,7 +228,7 @@
             } else {
                 args = null;
             }
-            scheduleServiceArgs(token, startId, fl, args);
+            scheduleServiceArgs(token, taskRemoved, startId, fl, args);
             return true;
         }
 
@@ -688,11 +689,12 @@
         data.recycle();
     }
 
-    public final void scheduleServiceArgs(IBinder token, int startId,
+    public final void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
 	    int flags, Intent args) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeStrongBinder(token);
+        data.writeInt(taskRemoved ? 1 : 0);
         data.writeInt(startId);
         data.writeInt(flags);
         if (args != null) {
diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
index 850f56a..e5a7980 100644
--- a/core/java/android/app/BackStackRecord.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -173,6 +173,8 @@
     static final int OP_REMOVE = 3;
     static final int OP_HIDE = 4;
     static final int OP_SHOW = 5;
+    static final int OP_DETACH = 6;
+    static final int OP_ATTACH = 7;
 
     static final class Op {
         Op next;
@@ -416,6 +418,32 @@
         return this;
     }
 
+    public FragmentTransaction detach(Fragment fragment) {
+        //if (fragment.mImmediateActivity == null) {
+        //    throw new IllegalStateException("Fragment not added: " + fragment);
+        //}
+
+        Op op = new Op();
+        op.cmd = OP_DETACH;
+        op.fragment = fragment;
+        addOp(op);
+
+        return this;
+    }
+
+    public FragmentTransaction attach(Fragment fragment) {
+        //if (fragment.mImmediateActivity == null) {
+        //    throw new IllegalStateException("Fragment not added: " + fragment);
+        //}
+
+        Op op = new Op();
+        op.cmd = OP_ATTACH;
+        op.fragment = fragment;
+        addOp(op);
+
+        return this;
+    }
+
     public FragmentTransaction setCustomAnimations(int enter, int exit) {
         return setCustomAnimations(enter, exit, 0, 0);
     }
@@ -589,6 +617,16 @@
                     f.mNextAnim = op.enterAnim;
                     mManager.showFragment(f, mTransition, mTransitionStyle);
                 } break;
+                case OP_DETACH: {
+                    Fragment f = op.fragment;
+                    f.mNextAnim = op.exitAnim;
+                    mManager.detachFragment(f, mTransition, mTransitionStyle);
+                } break;
+                case OP_ATTACH: {
+                    Fragment f = op.fragment;
+                    f.mNextAnim = op.enterAnim;
+                    mManager.attachFragment(f, mTransition, mTransitionStyle);
+                } break;
                 default: {
                     throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
                 }
@@ -655,6 +693,16 @@
                     mManager.hideFragment(f,
                             FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
                 } break;
+                case OP_DETACH: {
+                    Fragment f = op.fragment;
+                    mManager.attachFragment(f,
+                            FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
+                } break;
+                case OP_ATTACH: {
+                    Fragment f = op.fragment;
+                    mManager.detachFragment(f,
+                            FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
+                } break;
                 default: {
                     throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
                 }
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 53dc7c8..dd158f9 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -52,6 +52,7 @@
     final int mContainerId;
     final String mTag;
     final boolean mRetainInstance;
+    final boolean mDetached;
     final Bundle mArguments;
     
     Bundle mSavedFragmentState;
@@ -66,6 +67,7 @@
         mContainerId = frag.mContainerId;
         mTag = frag.mTag;
         mRetainInstance = frag.mRetainInstance;
+        mDetached = frag.mDetached;
         mArguments = frag.mArguments;
     }
     
@@ -77,6 +79,7 @@
         mContainerId = in.readInt();
         mTag = in.readString();
         mRetainInstance = in.readInt() != 0;
+        mDetached = in.readInt() != 0;
         mArguments = in.readBundle();
         mSavedFragmentState = in.readBundle();
     }
@@ -103,6 +106,7 @@
         mInstance.mContainerId = mContainerId;
         mInstance.mTag = mTag;
         mInstance.mRetainInstance = mRetainInstance;
+        mInstance.mDetached = mDetached;
         mInstance.mFragmentManager = activity.mFragments;
         
         return mInstance;
@@ -120,6 +124,7 @@
         dest.writeInt(mContainerId);
         dest.writeString(mTag);
         dest.writeInt(mRetainInstance ? 1 : 0);
+        dest.writeInt(mDetached ? 1 : 0);
         dest.writeBundle(mArguments);
         dest.writeBundle(mSavedFragmentState);
     }
@@ -321,8 +326,9 @@
     static final int INITIALIZING = 0;     // Not yet created.
     static final int CREATED = 1;          // Created.
     static final int ACTIVITY_CREATED = 2; // The activity has finished its creation.
-    static final int STARTED = 3;          // Created and started, not resumed.
-    static final int RESUMED = 4;          // Created started and resumed.
+    static final int STOPPED = 3;          // Fully created, not started.
+    static final int STARTED = 4;          // Created and started, not resumed.
+    static final int RESUMED = 5;          // Created started and resumed.
     
     int mState = INITIALIZING;
     
@@ -404,6 +410,9 @@
     // from the user.
     boolean mHidden;
     
+    // Set to true when the app has requested that this fragment be detached.
+    boolean mDetached;
+
     // If set this fragment would like its instance retained across
     // configuration changes.
     boolean mRetainInstance;
@@ -511,23 +520,27 @@
         }
     }
     
-    void restoreViewState() {
+    final void restoreViewState() {
         if (mSavedViewState != null) {
             mView.restoreHierarchyState(mSavedViewState);
             mSavedViewState = null;
         }
     }
     
-    void setIndex(int index) {
+    final void setIndex(int index) {
         mIndex = index;
         mWho = "android:fragment:" + mIndex;
    }
     
-    void clearIndex() {
+    final void clearIndex() {
         mIndex = -1;
         mWho = null;
     }
     
+    final boolean isInBackStack() {
+        return mBackStackNesting > 0;
+    }
+
     /**
      * Subclasses can not override equals().
      */
@@ -947,6 +960,19 @@
     }
     
     /**
+     * Called immediately after {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}
+     * has returned, but before any saved state has been restored in to the view.
+     * This gives subclasses a chance to initialize themselves once
+     * they know their view hierarchy has been completely created.  The fragment's
+     * view hierarchy is not however attached to its parent at this point.
+     * @param view The View returned by {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}.
+     * @param savedInstanceState If non-null, this fragment is being re-constructed
+     * from a previous saved state as given here.
+     */
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+    }
+    
+    /**
      * Called to have the fragment instantiate its user interface view.
      * This is optional, and non-graphical fragments can return null (which
      * is the default implementation).  This will be called between
@@ -1280,6 +1306,7 @@
                 writer.print(" mFromLayout="); writer.print(mFromLayout);
                 writer.print(" mInLayout="); writer.println(mInLayout);
         writer.print(prefix); writer.print("mHidden="); writer.print(mHidden);
+                writer.print(" mDetached="); writer.print(mDetached);
                 writer.print(" mRetainInstance="); writer.print(mRetainInstance);
                 writer.print(" mRetaining="); writer.print(mRetaining);
                 writer.print(" mHasMenu="); writer.println(mHasMenu);
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index ab60cf0..0da656f 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -714,13 +714,14 @@
                                 null, f.mSavedFragmentState);
                         if (f.mView != null) {
                             f.mView.setSaveFromParentEnabled(false);
+                            if (f.mHidden) f.mView.setVisibility(View.GONE);
                             f.restoreViewState();
-                            if (f.mHidden) f.mView.setVisibility(View.GONE); 
+                            f.onViewCreated(f.mView, f.mSavedFragmentState);
                         }
                     }
                 case Fragment.CREATED:
                     if (newState > Fragment.CREATED) {
-                        if (DEBUG) Log.v(TAG, "moveto CONTENT: " + f);
+                        if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
                         if (!f.mFromLayout) {
                             ViewGroup container = null;
                             if (f.mContainerId != 0) {
@@ -744,9 +745,10 @@
                                         anim.start();
                                     }
                                     container.addView(f.mView);
-                                    f.restoreViewState();
                                 }
-                                if (f.mHidden) f.mView.setVisibility(View.GONE); 
+                                if (f.mHidden) f.mView.setVisibility(View.GONE);
+                                f.restoreViewState();
+                                f.onViewCreated(f.mView, f.mSavedFragmentState);
                             }
                         }
                         
@@ -756,10 +758,13 @@
                             throw new SuperNotCalledException("Fragment " + f
                                     + " did not call through to super.onActivityCreated()");
                         }
+                        if (f.mView != null) {
+                        }
                         f.mSavedFragmentState = null;
                     }
                 case Fragment.ACTIVITY_CREATED:
-                    if (newState > Fragment.ACTIVITY_CREATED) {
+                case Fragment.STOPPED:
+                    if (newState > Fragment.STOPPED) {
                         if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
                         f.mCalled = false;
                         f.onStart();
@@ -803,9 +808,10 @@
                                     + " did not call through to super.onStop()");
                         }
                     }
+                case Fragment.STOPPED:
                 case Fragment.ACTIVITY_CREATED:
                     if (newState < Fragment.ACTIVITY_CREATED) {
-                        if (DEBUG) Log.v(TAG, "movefrom CONTENT: " + f);
+                        if (DEBUG) Log.v(TAG, "movefrom ACTIVITY_CREATED: " + f);
                         if (f.mView != null) {
                             // Need to save the current view state if not
                             // done already.
@@ -971,32 +977,36 @@
         if (mAdded == null) {
             mAdded = new ArrayList<Fragment>();
         }
-        mAdded.add(fragment);
-        makeActive(fragment);
         if (DEBUG) Log.v(TAG, "add: " + fragment);
-        fragment.mAdded = true;
-        fragment.mRemoving = false;
-        if (fragment.mHasMenu) {
-            mNeedMenuInvalidate = true;
-        }
-        if (moveToStateNow) {
-            moveToState(fragment);
+        makeActive(fragment);
+        if (!fragment.mDetached) {
+            mAdded.add(fragment);
+            fragment.mAdded = true;
+            fragment.mRemoving = false;
+            if (fragment.mHasMenu) {
+                mNeedMenuInvalidate = true;
+            }
+            if (moveToStateNow) {
+                moveToState(fragment);
+            }
         }
     }
     
     public void removeFragment(Fragment fragment, int transition, int transitionStyle) {
         if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
-        mAdded.remove(fragment);
-        final boolean inactive = fragment.mBackStackNesting <= 0;
-        if (fragment.mHasMenu) {
-            mNeedMenuInvalidate = true;
-        }
-        fragment.mAdded = false;
-        fragment.mRemoving = true;
-        moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,
-                transition, transitionStyle);
-        if (inactive) {
-            makeInactive(fragment);
+        final boolean inactive = !fragment.isInBackStack();
+        if (!fragment.mDetached || inactive) {
+            mAdded.remove(fragment);
+            if (fragment.mHasMenu) {
+                mNeedMenuInvalidate = true;
+            }
+            fragment.mAdded = false;
+            fragment.mRemoving = true;
+            moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,
+                    transition, transitionStyle);
+            if (inactive) {
+                makeInactive(fragment);
+            }
         }
     }
     
@@ -1052,6 +1062,39 @@
         }
     }
     
+    public void detachFragment(Fragment fragment, int transition, int transitionStyle) {
+        if (DEBUG) Log.v(TAG, "detach: " + fragment);
+        if (!fragment.mDetached) {
+            fragment.mDetached = true;
+            if (fragment.mAdded) {
+                // We are not already in back stack, so need to remove the fragment.
+                mAdded.remove(fragment);
+                if (fragment.mHasMenu) {
+                    mNeedMenuInvalidate = true;
+                }
+                fragment.mAdded = false;
+                fragment.mRemoving = true;
+                moveToState(fragment, Fragment.CREATED, transition, transitionStyle);
+            }
+        }
+    }
+
+    public void attachFragment(Fragment fragment, int transition, int transitionStyle) {
+        if (DEBUG) Log.v(TAG, "attach: " + fragment);
+        if (fragment.mDetached) {
+            fragment.mDetached = false;
+            if (!fragment.mAdded) {
+                mAdded.add(fragment);
+                fragment.mAdded = true;
+                fragment.mRemoving = false;
+                if (fragment.mHasMenu) {
+                    mNeedMenuInvalidate = true;
+                }
+                moveToState(fragment, mCurState, transition, transitionStyle);
+            }
+        }
+    }
+
     public Fragment findFragmentById(int id) {
         if (mActive != null) {
             // First look through added fragments.
@@ -1594,7 +1637,7 @@
     }
     
     public void dispatchStop() {
-        moveToState(Fragment.ACTIVITY_CREATED, false);
+        moveToState(Fragment.STOPPED, false);
     }
     
     public void dispatchDestroy() {
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
index 68600b3..c1f3cd6 100644
--- a/core/java/android/app/FragmentTransaction.java
+++ b/core/java/android/app/FragmentTransaction.java
@@ -87,6 +87,31 @@
     public abstract FragmentTransaction show(Fragment fragment);
 
     /**
+     * Detach the given fragment from the UI.  This is the same state as
+     * when it is put on the back stack: the fragment is removed from
+     * the UI, however its state is still being actively managed by the
+     * fragment manager.  When going into this state its view hierarchy
+     * is destroyed.
+     *
+     * @param fragment The fragment to be detached.
+     *
+     * @return Returns the same FragmentTransaction instance.
+     */
+    public abstract FragmentTransaction detach(Fragment fragment);
+
+    /**
+     * Re-attach a fragment after it had previously been deatched from
+     * the UI with {@link #detach(Fragment)}.  This
+     * causes its view hierarchy to be re-created, attached to the UI,
+     * and displayed.
+     *
+     * @param fragment The fragment to be attached.
+     *
+     * @return Returns the same FragmentTransaction instance.
+     */
+    public abstract FragmentTransaction attach(Fragment fragment);
+
+    /**
      * @return <code>true</code> if this transaction contains no operations,
      * <code>false</code> otherwise.
      */
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index cd26a62..bec697a 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -342,6 +342,13 @@
     public int startActivitiesInPackage(int uid,
             Intent[] intents, String[] resolvedTypes, IBinder resultTo) throws RemoteException;
 
+    // Multi-user APIs
+    public boolean switchUser(int userid) throws RemoteException;
+    
+    public boolean removeSubTask(int taskId, int subTaskIndex) throws RemoteException;
+
+    public boolean removeTask(int taskId, int flags) throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -557,4 +564,7 @@
     int START_ACTIVITIES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+120;
     int START_ACTIVITIES_IN_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+121;
     int ACTIVITY_SLEPT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+122;
+    int SWITCH_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+123;
+    int REMOVE_SUB_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+124;
+    int REMOVE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+125;
 }
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 55177a9..b29b088 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -73,8 +73,8 @@
             Intent intent, boolean rebind) throws RemoteException;
     void scheduleUnbindService(IBinder token,
             Intent intent) throws RemoteException;
-    void scheduleServiceArgs(IBinder token, int startId, int flags, Intent args)
-            throws RemoteException;
+    void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
+            int flags, Intent args) throws RemoteException;
     void scheduleStopService(IBinder token) throws RemoteException;
     static final int DEBUG_OFF = 0;
     static final int DEBUG_ON = 1;
diff --git a/core/java/android/app/ListFragment.java b/core/java/android/app/ListFragment.java
index 6e2f4b6..a5ee26c 100644
--- a/core/java/android/app/ListFragment.java
+++ b/core/java/android/app/ListFragment.java
@@ -195,11 +195,11 @@
     }
 
     /**
-     * Attach to list view once Fragment is ready to run.
+     * Attach to list view once the view hierarchy has been created.
      */
     @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
         ensureList();
     }
 
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 05b9781..c179b35 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -371,6 +371,13 @@
     public static final int START_REDELIVER_INTENT = 3;
     
     /**
+     * Special constant for reporting that we are done processing
+     * {@link #onTaskRemoved(Intent)}.
+     * @hide
+     */
+    public static final int START_TASK_REMOVED_COMPLETE = 1000;
+
+    /**
      * This flag is set in {@link #onStartCommand} if the Intent is a
      * re-delivery of a previously delivered intent, because the service
      * had previously returned {@link #START_REDELIVER_INTENT} but had been
@@ -500,6 +507,19 @@
     }
     
     /**
+     * This is called if the service is currently running and the user has
+     * removed a task that comes from the service's application.  If you have
+     * set {@link android.content.pm.ServiceInfo#FLAG_STOP_WITH_TASK ServiceInfo.FLAG_STOP_WITH_TASK}
+     * then you will not receive this callback; instead, the service will simply
+     * be stopped.
+     *
+     * @param rootIntent The original root Intent that was used to launch
+     * the task that is being removed.
+     */
+    public void onTaskRemoved(Intent rootIntent) {
+    }
+
+    /**
      * Stop the service, if it was previously started.  This is the same as
      * calling {@link android.content.Context#stopService} for this particular service.
      *  
diff --git a/core/java/android/bluetooth/BluetoothDeviceProfileState.java b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
index f4693c2..56f236d 100644
--- a/core/java/android/bluetooth/BluetoothDeviceProfileState.java
+++ b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
@@ -26,8 +26,8 @@
 import android.server.BluetoothService;
 import android.util.Log;
 
-import com.android.internal.util.HierarchicalState;
-import com.android.internal.util.HierarchicalStateMachine;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
 
 import java.util.Set;
 
@@ -57,7 +57,7 @@
  * Todo(): Write tests for this class, when the Android Mock support is completed.
  * @hide
  */
-public final class BluetoothDeviceProfileState extends HierarchicalStateMachine {
+public final class BluetoothDeviceProfileState extends StateMachine {
     private static final String TAG = "BluetoothDeviceProfileState";
     private static final boolean DBG = false;
 
@@ -235,16 +235,16 @@
         }
     }
 
-    private class BondedDevice extends HierarchicalState {
+    private class BondedDevice extends State {
         @Override
-        protected void enter() {
+        public void enter() {
             Log.i(TAG, "Entering ACL Connected state with: " + getCurrentMessage().what);
             Message m = new Message();
             m.copyFrom(getCurrentMessage());
             sendMessageAtFrontOfQueue(m);
         }
         @Override
-        protected boolean processMessage(Message message) {
+        public boolean processMessage(Message message) {
             log("ACL Connected State -> Processing Message: " + message.what);
             switch(message.what) {
                 case CONNECT_HFP_OUTGOING:
@@ -353,12 +353,12 @@
         }
     }
 
-    private class OutgoingHandsfree extends HierarchicalState {
+    private class OutgoingHandsfree extends State {
         private boolean mStatus = false;
         private int mCommand;
 
         @Override
-        protected void enter() {
+        public void enter() {
             Log.i(TAG, "Entering OutgoingHandsfree state with: " + getCurrentMessage().what);
             mCommand = getCurrentMessage().what;
             if (mCommand != CONNECT_HFP_OUTGOING &&
@@ -374,7 +374,7 @@
         }
 
         @Override
-        protected boolean processMessage(Message message) {
+        public boolean processMessage(Message message) {
             log("OutgoingHandsfree State -> Processing Message: " + message.what);
             Message deferMsg = new Message();
             int command = message.what;
@@ -466,12 +466,12 @@
         }
     }
 
-    private class IncomingHandsfree extends HierarchicalState {
+    private class IncomingHandsfree extends State {
         private boolean mStatus = false;
         private int mCommand;
 
         @Override
-        protected void enter() {
+        public void enter() {
             Log.i(TAG, "Entering IncomingHandsfree state with: " + getCurrentMessage().what);
             mCommand = getCurrentMessage().what;
             if (mCommand != CONNECT_HFP_INCOMING &&
@@ -487,7 +487,7 @@
         }
 
         @Override
-        protected boolean processMessage(Message message) {
+        public boolean processMessage(Message message) {
             log("IncomingHandsfree State -> Processing Message: " + message.what);
             switch(message.what) {
                 case CONNECT_HFP_OUTGOING:
@@ -546,12 +546,12 @@
         }
     }
 
-    private class OutgoingA2dp extends HierarchicalState {
+    private class OutgoingA2dp extends State {
         private boolean mStatus = false;
         private int mCommand;
 
         @Override
-        protected void enter() {
+        public void enter() {
             Log.i(TAG, "Entering OutgoingA2dp state with: " + getCurrentMessage().what);
             mCommand = getCurrentMessage().what;
             if (mCommand != CONNECT_A2DP_OUTGOING &&
@@ -567,7 +567,7 @@
         }
 
         @Override
-        protected boolean processMessage(Message message) {
+        public boolean processMessage(Message message) {
             log("OutgoingA2dp State->Processing Message: " + message.what);
             Message deferMsg = new Message();
             switch(message.what) {
@@ -656,12 +656,12 @@
         }
     }
 
-    private class IncomingA2dp extends HierarchicalState {
+    private class IncomingA2dp extends State {
         private boolean mStatus = false;
         private int mCommand;
 
         @Override
-        protected void enter() {
+        public void enter() {
             Log.i(TAG, "Entering IncomingA2dp state with: " + getCurrentMessage().what);
             mCommand = getCurrentMessage().what;
             if (mCommand != CONNECT_A2DP_INCOMING &&
@@ -677,7 +677,7 @@
         }
 
         @Override
-        protected boolean processMessage(Message message) {
+        public boolean processMessage(Message message) {
             log("IncomingA2dp State->Processing Message: " + message.what);
             switch(message.what) {
                 case CONNECT_HFP_OUTGOING:
@@ -734,12 +734,12 @@
     }
 
 
-    private class OutgoingHid extends HierarchicalState {
+    private class OutgoingHid extends State {
         private boolean mStatus = false;
         private int mCommand;
 
         @Override
-        protected void enter() {
+        public void enter() {
             log("Entering OutgoingHid state with: " + getCurrentMessage().what);
             mCommand = getCurrentMessage().what;
             if (mCommand != CONNECT_HID_OUTGOING &&
@@ -751,7 +751,7 @@
         }
 
         @Override
-        protected boolean processMessage(Message message) {
+        public boolean processMessage(Message message) {
             log("OutgoingHid State->Processing Message: " + message.what);
             Message deferMsg = new Message();
             switch(message.what) {
@@ -815,12 +815,12 @@
         }
     }
 
-  private class IncomingHid extends HierarchicalState {
+  private class IncomingHid extends State {
       private boolean mStatus = false;
       private int mCommand;
 
       @Override
-      protected void enter() {
+    public void enter() {
           log("Entering IncomingHid state with: " + getCurrentMessage().what);
           mCommand = getCurrentMessage().what;
           if (mCommand != CONNECT_HID_INCOMING &&
@@ -832,7 +832,7 @@
       }
 
       @Override
-      protected boolean processMessage(Message message) {
+    public boolean processMessage(Message message) {
           log("IncomingHid State->Processing Message: " + message.what);
           Message deferMsg = new Message();
           switch(message.what) {
diff --git a/core/java/android/bluetooth/BluetoothProfileState.java b/core/java/android/bluetooth/BluetoothProfileState.java
index 18060a0..98afdb8 100644
--- a/core/java/android/bluetooth/BluetoothProfileState.java
+++ b/core/java/android/bluetooth/BluetoothProfileState.java
@@ -22,8 +22,8 @@
 import android.os.Message;
 import android.util.Log;
 
-import com.android.internal.util.HierarchicalState;
-import com.android.internal.util.HierarchicalStateMachine;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
 
 /**
  * This state machine is used to serialize the connections
@@ -39,7 +39,7 @@
  * @hide
  */
 
-public class BluetoothProfileState extends HierarchicalStateMachine {
+public class BluetoothProfileState extends StateMachine {
     private static final boolean DBG = true;
     private static final String TAG = "BluetoothProfileState";
 
@@ -101,15 +101,15 @@
         context.registerReceiver(mBroadcastReceiver, filter);
     }
 
-    private class StableState extends HierarchicalState {
+    private class StableState extends State {
         @Override
-        protected void enter() {
+        public void enter() {
             log("Entering Stable State");
             mPendingDevice = null;
         }
 
         @Override
-        protected boolean processMessage(Message msg) {
+        public boolean processMessage(Message msg) {
             if (msg.what != TRANSITION_TO_STABLE) {
                 transitionTo(mPendingCommandState);
             }
@@ -117,15 +117,15 @@
         }
     }
 
-    private class PendingCommandState extends HierarchicalState {
+    private class PendingCommandState extends State {
         @Override
-        protected void enter() {
+        public void enter() {
             log("Entering PendingCommandState State");
             dispatchMessage(getCurrentMessage());
         }
 
         @Override
-        protected boolean processMessage(Message msg) {
+        public boolean processMessage(Message msg) {
             if (msg.what == TRANSITION_TO_STABLE) {
                 transitionTo(mStableState);
             } else {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 80bed0d..99c4c7f 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -150,21 +150,21 @@
      * {@link PackageInfo#permissions}.
      */
     public static final int GET_PERMISSIONS               = 0x00001000;
-    
+
     /**
      * Flag parameter to retrieve all applications(even uninstalled ones) with data directories.
-     * This state could have resulted if applications have been deleted with flag 
+     * This state could have resulted if applications have been deleted with flag
      * DONT_DELETE_DATA
      * with a possibility of being replaced or reinstalled in future
      */
     public static final int GET_UNINSTALLED_PACKAGES = 0x00002000;
-    
+
     /**
      * {@link PackageInfo} flag: return information about
      * hardware preferences in
      * {@link PackageInfo#configPreferences PackageInfo.configPreferences} and
      * requested features in {@link PackageInfo#reqFeatures
-     * PackageInfo.reqFeatures}. 
+     * PackageInfo.reqFeatures}.
      */
     public static final int GET_CONFIGURATIONS = 0x00004000;
 
@@ -244,7 +244,7 @@
     public static final int INSTALL_REPLACE_EXISTING = 0x00000002;
 
     /**
-     * Flag parameter for {@link #installPackage} to indicate that you want to 
+     * Flag parameter for {@link #installPackage} to indicate that you want to
      * allow test packages (those that have set android:testOnly in their
      * manifest) to be installed.
      * @hide
@@ -555,7 +555,7 @@
      * Return code for when package deletion succeeds. This is passed to the
      * {@link IPackageDeleteObserver} by {@link #deletePackage()} if the system
      * succeeded in deleting the package.
-     * 
+     *
      * @hide
      */
     public static final int DELETE_SUCCEEDED = 1;
@@ -564,7 +564,7 @@
      * Deletion failed return code: this is passed to the
      * {@link IPackageDeleteObserver} by {@link #deletePackage()} if the system
      * failed to delete the package for an unspecified reason.
-     * 
+     *
      * @hide
      */
     public static final int DELETE_FAILED_INTERNAL_ERROR = -1;
@@ -574,7 +574,7 @@
      * {@link IPackageDeleteObserver} by {@link #deletePackage()} if the system
      * failed to delete the package because it is the active DevicePolicy
      * manager.
-     * 
+     *
      * @hide
      */
     public static final int DELETE_FAILED_DEVICE_POLICY_MANAGER = -2;
@@ -583,7 +583,7 @@
      * Return code that is passed to the {@link IPackageMoveObserver} by
      * {@link #movePackage(android.net.Uri, IPackageMoveObserver)} when the
      * package has been successfully moved by the system.
-     * 
+     *
      * @hide
      */
     public static final int MOVE_SUCCEEDED = 1;
@@ -641,7 +641,7 @@
      * {@link #movePackage(android.net.Uri, IPackageMoveObserver)} if the
      * specified package already has an operation pending in the
      * {@link PackageHandler} queue.
-     * 
+     *
      * @hide
      */
     public static final int MOVE_FAILED_OPERATION_PENDING = -7;
@@ -789,7 +789,7 @@
      */
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_SENSOR_PROXIMITY = "android.hardware.sensor.proximity";
-    
+
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device has a telephony radio with data
@@ -797,14 +797,14 @@
      */
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_TELEPHONY = "android.hardware.telephony";
-    
+
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device has a CDMA telephony stack.
      */
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_TELEPHONY_CDMA = "android.hardware.telephony.cdma";
-    
+
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device has a GSM telephony stack.
@@ -847,8 +847,8 @@
      */
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_TOUCHSCREEN = "android.hardware.touchscreen";
-    
-    
+
+
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device's touch screen supports
@@ -856,7 +856,7 @@
      */
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_TOUCHSCREEN_MULTITOUCH = "android.hardware.touchscreen.multitouch";
-    
+
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device's touch screen is capable of
@@ -932,11 +932,11 @@
      * @return Returns a PackageInfo object containing information about the package.
      *         If flag GET_UNINSTALLED_PACKAGES is set and  if the package is not
      *         found in the list of installed applications, the package information is
-     *         retrieved from the list of uninstalled applications(which includes 
+     *         retrieved from the list of uninstalled applications(which includes
      *         installed applications as well as applications
      *         with data directory ie applications which had been
      *         deleted with DONT_DELTE_DATA flag set).
-     *         
+     *
      * @see #GET_ACTIVITIES
      * @see #GET_GIDS
      * @see #GET_CONFIGURATIONS
@@ -947,7 +947,7 @@
      * @see #GET_SERVICES
      * @see #GET_SIGNATURES
      * @see #GET_UNINSTALLED_PACKAGES
-     *                  
+     *
      */
     public abstract PackageInfo getPackageInfo(String packageName, int flags)
             throws NameNotFoundException;
@@ -960,7 +960,7 @@
      * the canonical name for each package.
      */
     public abstract String[] currentToCanonicalPackageNames(String[] names);
-    
+
     /**
      * Map from a packages canonical name to the current name in use on the device.
      * @param names Array of new names to be mapped.
@@ -968,7 +968,7 @@
      * the current name for each package.
      */
     public abstract String[] canonicalToCurrentPackageNames(String[] names);
-    
+
     /**
      * Return a "good" intent to launch a front-door activity in a package,
      * for use for example to implement an "open" button when browsing through
@@ -976,12 +976,12 @@
      * activity in the category {@link Intent#CATEGORY_INFO}, next for a
      * main activity in the category {@link Intent#CATEGORY_LAUNCHER}, or return
      * null if neither are found.
-     * 
+     *
      * <p>Throws {@link NameNotFoundException} if a package with the given
      * name can not be found on the system.
      *
      * @param packageName The name of the package to inspect.
-     * 
+     *
      * @return Returns either a fully-qualified Intent that can be used to
      * launch the main activity in the package, or null if the package does
      * not contain such an activity.
@@ -1077,16 +1077,16 @@
      *
      * @param packageName The full name (i.e. com.google.apps.contacts) of an
      *                    application.
-     * @param flags Additional option flags. Use any combination of 
+     * @param flags Additional option flags. Use any combination of
      * {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
      * {@link #GET_UNINSTALLED_PACKAGES} to modify the data returned.
      *
-     * @return  {@link ApplicationInfo} Returns ApplicationInfo object containing 
+     * @return  {@link ApplicationInfo} Returns ApplicationInfo object containing
      *         information about the package.
      *         If flag GET_UNINSTALLED_PACKAGES is set and  if the package is not
-     *         found in the list of installed applications, 
-     *         the application information is retrieved from the 
-     *         list of uninstalled applications(which includes 
+     *         found in the list of installed applications,
+     *         the application information is retrieved from the
+     *         list of uninstalled applications(which includes
      *         installed applications as well as applications
      *         with data directory ie applications which had been
      *         deleted with DONT_DELTE_DATA flag set).
@@ -1108,7 +1108,7 @@
      * @param component The full component name (i.e.
      * com.google.apps.contacts/com.google.apps.contacts.ContactsList) of an Activity
      * class.
-     * @param flags Additional option flags. Use any combination of 
+     * @param flags Additional option flags. Use any combination of
      * {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
      * to modify the data (in ApplicationInfo) returned.
      *
@@ -1131,7 +1131,7 @@
      * @param component The full component name (i.e.
      * com.google.apps.calendar/com.google.apps.calendar.CalendarAlarm) of a Receiver
      * class.
-     * @param flags Additional option flags.  Use any combination of 
+     * @param flags Additional option flags.  Use any combination of
      * {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
      * to modify the data returned.
      *
@@ -1154,12 +1154,12 @@
      * @param component The full component name (i.e.
      * com.google.apps.media/com.google.apps.media.BackgroundPlayback) of a Service
      * class.
-     * @param flags Additional option flags.  Use any combination of 
+     * @param flags Additional option flags.  Use any combination of
      * {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
      * to modify the data returned.
      *
      * @return ServiceInfo containing information about the service.
-     * 
+     *
      * @see #GET_META_DATA
      * @see #GET_SHARED_LIBRARY_FILES
      */
@@ -1206,7 +1206,7 @@
      *
      * @return A List of PackageInfo objects, one for each package that is
      *         installed on the device.  In the unlikely case of there being no
-     *         installed packages, an empty list is returned. 
+     *         installed packages, an empty list is returned.
      *         If flag GET_UNINSTALLED_PACKAGES is set, a list of all
      *         applications including those deleted with DONT_DELETE_DATA
      *         (partially installed apps with data directory) will be returned.
@@ -1221,7 +1221,7 @@
      * @see #GET_SERVICES
      * @see #GET_SIGNATURES
      * @see #GET_UNINSTALLED_PACKAGES
-     * 
+     *
      */
     public abstract List<PackageInfo> getInstalledPackages(int flags);
 
@@ -1283,7 +1283,7 @@
      * the device is rebooted before it is written.
      */
     public abstract boolean addPermissionAsync(PermissionInfo info);
-    
+
     /**
      * Removes a permission that was previously added with
      * {@link #addPermission(PermissionInfo)}.  The same ownership rules apply
@@ -1370,7 +1370,7 @@
      * user id is not currently assigned.
      */
     public abstract String getNameForUid(int uid);
-    
+
     /**
      * Return the user id associated with a shared user name. Multiple
      * applications can specify a shared user name in their manifest and thus
@@ -1391,38 +1391,38 @@
      * device. If flag GET_UNINSTALLED_PACKAGES has been set, a list of all
      * applications including those deleted with DONT_DELETE_DATA(partially
      * installed apps with data directory) will be returned.
-     * 
-     * @param flags Additional option flags. Use any combination of 
+     *
+     * @param flags Additional option flags. Use any combination of
      * {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
      * {link #GET_UNINSTALLED_PACKAGES} to modify the data returned.
      *
      * @return A List of ApplicationInfo objects, one for each application that
      *         is installed on the device.  In the unlikely case of there being
-     *         no installed applications, an empty list is returned. 
+     *         no installed applications, an empty list is returned.
      *         If flag GET_UNINSTALLED_PACKAGES is set, a list of all
      *         applications including those deleted with DONT_DELETE_DATA
      *         (partially installed apps with data directory) will be returned.
-     *         
+     *
      * @see #GET_META_DATA
      * @see #GET_SHARED_LIBRARY_FILES
      * @see #GET_UNINSTALLED_PACKAGES
      */
     public abstract List<ApplicationInfo> getInstalledApplications(int flags);
-    
+
     /**
      * Get a list of shared libraries that are available on the
      * system.
-     * 
+     *
      * @return An array of shared library names that are
      * available on the system, or null if none are installed.
-     * 
+     *
      */
     public abstract String[] getSystemSharedLibraryNames();
 
     /**
      * Get a list of features that are available on the
      * system.
-     * 
+     *
      * @return An array of FeatureInfo classes describing the features
      * that are available on the system, or null if there are none(!!).
      */
@@ -1431,7 +1431,7 @@
     /**
      * Check whether the given feature name is one of the available
      * features as returned by {@link #getSystemAvailableFeatures()}.
-     * 
+     *
      * @return Returns true if the devices supports the feature, else
      * false.
      */
@@ -1448,7 +1448,7 @@
      * that {@link android.content.Context#startActivity(Intent)} and
      * {@link android.content.Intent#resolveActivity(PackageManager)
      * Intent.resolveActivity(PackageManager)} do.</p>
-     * 
+     *
      * @param intent An intent containing all of the desired specification
      *               (action, data, type, category, and/or component).
      * @param flags Additional option flags.  The most important is
@@ -1747,7 +1747,7 @@
      *
      * @return Returns the image of the logo or null if the activity has no
      * logo specified.
-     * 
+     *
      * @throws NameNotFoundException Thrown if the resources for the given
      * activity could not be loaded.
      *
@@ -1768,7 +1768,7 @@
      *
      * @return Returns the image of the logo, or null if the activity has no
      * logo specified.
-     * 
+     *
      * @throws NameNotFoundException Thrown if the resources for application
      * matching the given intent could not be loaded.
      *
@@ -1801,7 +1801,7 @@
      *
      * @return Returns the image of the logo, or null if no application logo
      * has been specified.
-     * 
+     *
      * @throws NameNotFoundException Thrown if the resources for the given
      * application could not be loaded.
      *
@@ -1935,7 +1935,7 @@
      * @see #GET_RECEIVERS
      * @see #GET_SERVICES
      * @see #GET_SIGNATURES
-     * 
+     *
      */
     public PackageInfo getPackageArchiveInfo(String archiveFilePath, int flags) {
         PackageParser packageParser = new PackageParser(archiveFilePath);
@@ -1952,7 +1952,7 @@
 
     /**
      * @hide
-     * 
+     *
      * Install a package. Since this may take a little while, the result will
      * be posted back to the given observer.  An installation will fail if the calling context
      * lacks the {@link android.Manifest.permission#INSTALL_PACKAGES} permission, if the
@@ -2012,11 +2012,11 @@
     /**
      * Retrieve the package name of the application that installed a package. This identifies
      * which market the package came from.
-     * 
+     *
      * @param packageName The name of the package to query
      */
     public abstract String getInstallerPackageName(String packageName);
-    
+
     /**
      * Attempts to clear the user data directory of an application.
      * Since this may take a little while, the result will
@@ -2071,7 +2071,7 @@
      * of bytes if possible.
      * @param observer call back used to notify when
      * the operation is completed
-     * 
+     *
      * @hide
      */
     public abstract void freeStorageAndNotify(long freeStorageSize, IPackageDataObserver observer);
@@ -2096,7 +2096,7 @@
      * @param pi IntentSender call back used to
      * notify when the operation is completed.May be null
      * to indicate that no call back is desired.
-     * 
+     *
      * @hide
      */
     public abstract void freeStorage(long freeStorageSize, IntentSender pi);
@@ -2172,7 +2172,7 @@
      * @deprecated This is a protected API that should not have been available
      * to third party applications.  It is the platform's responsibility for
      * assigning preferred activities and this can not be directly modified.
-     * 
+     *
      * Add a new preferred activity mapping to the system.  This will be used
      * to automatically select the given activity component when
      * {@link Context#startActivity(Intent) Context.startActivity()} finds
@@ -2195,7 +2195,7 @@
      * @deprecated This is a protected API that should not have been available
      * to third party applications.  It is the platform's responsibility for
      * assigning preferred activities and this can not be directly modified.
-     * 
+     *
      * Replaces an existing preferred activity mapping to the system, and if that were not present
      * adds a new preferred activity.  This will be used
      * to automatically select the given activity component when
@@ -2304,7 +2304,7 @@
      */
     public abstract void setApplicationEnabledSetting(String packageName,
             int newState, int flags);
-    
+
     /**
      * Return the the enabled setting for an application.  This returns
      * the last value set by
@@ -2345,4 +2345,46 @@
      */
     public abstract void movePackage(
             String packageName, IPackageMoveObserver observer, int flags);
+
+    /**
+     * Creates a user with the specified name and options.
+     *
+     * @param name the user's name
+     * @param flags flags that identify the type of user and other properties.
+     * @see UserInfo
+     *
+     * @return the UserInfo object for the created user, or null if the user could not be created.
+     * @hide
+     */
+    public abstract UserInfo createUser(String name, int flags);
+
+    /**
+     * @return the list of users that were created
+     * @hide
+     */
+    public abstract List<UserInfo> getUsers();
+
+    /**
+     * @param id the ID of the user, where 0 is the primary user.
+     * @hide
+     */
+    public abstract boolean removeUser(int id);
+
+    /**
+     * Updates the user's name.
+     *
+     * @param id the user's id
+     * @param name the new name for the user
+     * @hide
+     */
+    public abstract void updateUserName(int id, String name);
+
+    /**
+     * Changes the user's properties specified by the flags.
+     *
+     * @param id the user's id
+     * @param flags the new flags for the user
+     * @hide
+     */
+    public abstract void updateUserFlags(int id, int flags);
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 67cd4a2..54a8842 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2473,6 +2473,13 @@
             s.info.permission = str.length() > 0 ? str.toString().intern() : null;
         }
 
+        s.info.flags = 0;
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestService_stopWithTask,
+                false)) {
+            s.info.flags |= ServiceInfo.FLAG_STOP_WITH_TASK;
+        }
+
         sa.recycle();
 
         if ((owner.applicationInfo.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 087a4fe..612e345 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -33,17 +33,35 @@
      */
     public String permission;
 
+    /**
+     * Bit in {@link #flags}: If set, the service will automatically be
+     * stopped by the system if the user removes a task that is rooted
+     * in one of the application's activities.  Set from the
+     * {@link android.R.attr#stopWithTask} attribute.
+     */
+    public static final int FLAG_STOP_WITH_TASK = 0x0001;
+
+    /**
+     * Options that have been set in the service declaration in the
+     * manifest.
+     * These include:
+     * {@link #FLAG_STOP_WITH_TASK}
+     */
+    public int flags;
+
     public ServiceInfo() {
     }
 
     public ServiceInfo(ServiceInfo orig) {
         super(orig);
         permission = orig.permission;
+        flags = orig.flags;
     }
 
     public void dump(Printer pw, String prefix) {
         super.dumpFront(pw, prefix);
         pw.println(prefix + "permission=" + permission);
+        pw.println(prefix + "flags=0x" + Integer.toHexString(flags));
     }
     
     public String toString() {
@@ -59,6 +77,7 @@
     public void writeToParcel(Parcel dest, int parcelableFlags) {
         super.writeToParcel(dest, parcelableFlags);
         dest.writeString(permission);
+        dest.writeInt(flags);
     }
 
     public static final Creator<ServiceInfo> CREATOR =
@@ -74,5 +93,6 @@
     private ServiceInfo(Parcel source) {
         super(source);
         permission = source.readString();
+        flags = source.readInt();
     }
 }
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
new file mode 100644
index 0000000..3704d3a
--- /dev/null
+++ b/core/java/android/content/pm/UserInfo.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Per-user information.
+ * @hide
+ */
+public class UserInfo implements Parcelable {
+    /**
+     * Primary user. Only one user can have this flag set. Meaning of this
+     * flag TBD.
+     */
+    public static final int FLAG_PRIMARY = 0x00000001;
+
+    /**
+     * User with administrative privileges. Such a user can create and
+     * delete users.
+     */
+    public static final int FLAG_ADMIN   = 0x00000002;
+
+    /**
+     * Indicates a guest user that may be transient.
+     */
+    public static final int FLAG_GUEST   = 0x00000004;
+
+    public int id;
+    public String name;
+    public int flags;
+
+    public UserInfo(int id, String name, int flags) {
+        this.id = id;
+        this.name = name;
+        this.flags = flags;
+    }
+
+    public boolean isPrimary() {
+        return (flags & FLAG_PRIMARY) == FLAG_PRIMARY;
+    }
+
+    public boolean isAdmin() {
+        return (flags & FLAG_ADMIN) == FLAG_ADMIN;
+    }
+
+    public boolean isGuest() {
+        return (flags & FLAG_GUEST) == FLAG_GUEST;
+    }
+
+    public UserInfo() {
+    }
+
+    public UserInfo(UserInfo orig) {
+        name = orig.name;
+        id = orig.id;
+        flags = orig.flags;
+    }
+
+    @Override
+    public String toString() {
+        return "UserInfo{"
+ + id + ":" + name + ":" + Integer.toHexString(flags) + "}";
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        dest.writeInt(id);
+        dest.writeString(name);
+        dest.writeInt(flags);
+    }
+
+    public static final Parcelable.Creator<UserInfo> CREATOR
+            = new Parcelable.Creator<UserInfo>() {
+        public UserInfo createFromParcel(Parcel source) {
+            return new UserInfo(source);
+        }
+        public UserInfo[] newArray(int size) {
+            return new UserInfo[size];
+        }
+    };
+
+    private UserInfo(Parcel source) {
+        id = source.readInt();
+        name = source.readString();
+        flags = source.readInt();
+    }
+}
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 0a13b4e6..328a55b 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -1086,7 +1086,7 @@
     /**
      * Area class for focus.
      *
-     * @see #setFocusAreas(List<Area>)
+     * @see #setFocusAreas(List)
      * @see #getFocusAreas()
      * @hide
      */
@@ -1180,6 +1180,8 @@
         private static final String KEY_MAX_EXPOSURE_COMPENSATION = "max-exposure-compensation";
         private static final String KEY_MIN_EXPOSURE_COMPENSATION = "min-exposure-compensation";
         private static final String KEY_EXPOSURE_COMPENSATION_STEP = "exposure-compensation-step";
+        private static final String KEY_METERING_AREAS = "metering-areas";
+        private static final String KEY_MAX_NUM_METERING_AREAS = "max-num-metering-areas";
         private static final String KEY_ZOOM = "zoom";
         private static final String KEY_MAX_ZOOM = "max-zoom";
         private static final String KEY_ZOOM_RATIOS = "zoom-ratios";
@@ -1518,6 +1520,27 @@
             mMap.put(key, Integer.toString(value));
         }
 
+        private void set(String key, List<Area> areas) {
+            StringBuilder buffer = new StringBuilder();
+            for (int i = 0; i < areas.size(); i++) {
+                Area area = areas.get(i);
+                Rect rect = area.rect;
+                buffer.append('(');
+                buffer.append(rect.left);
+                buffer.append(',');
+                buffer.append(rect.top);
+                buffer.append(',');
+                buffer.append(rect.right);
+                buffer.append(',');
+                buffer.append(rect.bottom);
+                buffer.append(',');
+                buffer.append(area.weight);
+                buffer.append(')');
+                if (i != areas.size() - 1) buffer.append(',');
+            }
+            set(key, buffer.toString());
+        }
+
         /**
          * Returns the value of a String parameter.
          *
@@ -2550,8 +2573,8 @@
 
         /**
          * Gets the maximum number of focus areas supported. This is the maximum
-         * length of the list in {@link #setFocusArea(List<Area>)} and
-         * {@link #getFocusArea()}.
+         * length of the list in {@link #setFocusAreas(List)} and
+         * {@link #getFocusAreas()}.
          *
          * @return the maximum number of focus areas supported by the camera.
          * @see #getFocusAreas()
@@ -2562,10 +2585,11 @@
         }
 
         /**
-         * Gets the current focus areas.
+         * Gets the current focus areas. Camera driver uses the areas to decide
+         * focus.
          *
-         * Before using this API or {@link #setFocusAreas(List<int>)}, apps
-         * should call {@link #getMaxNumFocusArea()} to know the maximum number of
+         * Before using this API or {@link #setFocusAreas(List)}, apps should
+         * call {@link #getMaxNumFocusAreas()} to know the maximum number of
          * focus areas first. If the value is 0, focus area is not supported.
          *
          * Each focus area is a rectangle with specified weight. The direction
@@ -2594,7 +2618,7 @@
          *
          * Focus area only has effect if the current focus mode is
          * {@link #FOCUS_MODE_AUTO}, {@link #FOCUS_MODE_MACRO}, or
-         * {@link #FOCUS_MODE_CONTINOUS_VIDEO}.
+         * {@link #FOCUS_MODE_CONTINUOUS_VIDEO}.
          *
          * @return a list of current focus areas
          * @hide
@@ -2606,29 +2630,80 @@
         /**
          * Sets focus areas. See {@link #getFocusAreas()} for documentation.
          *
-         * @param focusArea the focus areas
+         * @param focusAreas the focus areas
          * @see #getFocusAreas()
          * @hide
          */
-        public void setFocusAreas(List<Area> focusArea) {
-            StringBuilder buffer = new StringBuilder();
-            for (int i = 0; i < focusArea.size(); i++) {
-                Area area = focusArea.get(i);
-                Rect rect = area.rect;
-                buffer.append('(');
-                buffer.append(rect.left);
-                buffer.append(',');
-                buffer.append(rect.top);
-                buffer.append(',');
-                buffer.append(rect.right);
-                buffer.append(',');
-                buffer.append(rect.bottom);
-                buffer.append(',');
-                buffer.append(area.weight);
-                buffer.append(')');
-                if (i != focusArea.size() - 1) buffer.append(',');
-            }
-            set(KEY_FOCUS_AREAS, buffer.toString());
+        public void setFocusAreas(List<Area> focusAreas) {
+            set(KEY_FOCUS_AREAS, focusAreas);
+        }
+
+        /**
+         * Gets the maximum number of metering areas supported. This is the
+         * maximum length of the list in {@link #setMeteringAreas(List)} and
+         * {@link #getMeteringAreas()}.
+         *
+         * @return the maximum number of metering areas supported by the camera.
+         * @see #getMeteringAreas()
+         * @hide
+         */
+        public int getMaxNumMeteringAreas() {
+            return getInt(KEY_MAX_NUM_METERING_AREAS, 0);
+        }
+
+        /**
+         * Gets the current metering areas. Camera driver uses these areas to
+         * decide exposure.
+         *
+         * Before using this API or {@link #setMeteringAreas(List)}, apps should
+         * call {@link #getMaxNumMeteringAreas()} to know the maximum number of
+         * metering areas first. If the value is 0, metering area is not
+         * supported.
+         *
+         * Each metering area is a rectangle with specified weight. The
+         * direction is relative to the sensor orientation, that is, what the
+         * sensor sees. The direction is not affected by the rotation or
+         * mirroring of {@link #setDisplayOrientation(int)}. Coordinates of the
+         * rectangle range from -1000 to 1000. (-1000, -1000) is the upper left
+         * point. (1000, 1000) is the lower right point. The length and width of
+         * metering areas cannot be 0 or negative.
+         *
+         * The weight ranges from 1 to 1000. The sum of the weights of all
+         * metering areas must be 1000. Metering areas can partially overlap and
+         * the driver will add the weights in the overlap region. But apps
+         * should not set two metering areas that have identical coordinates.
+         *
+         * A special case of all-zero single metering area means driver to
+         * decide the metering area. For example, the driver may use more
+         * signals to decide metering areas and change them dynamically. Apps
+         * can set all-zero if they want the driver to decide metering areas.
+         *
+         * Metering areas are relative to the current field of view
+         * ({@link #getZoom()}). No matter what the zoom level is, (-1000,-1000)
+         * represents the top of the currently visible camera frame. The
+         * metering area cannot be set to be outside the current field of view,
+         * even when using zoom.
+         *
+         * No matter what metering areas are, the final exposure are compensated
+         * by {@link #setExposureCompensation(int)}.
+         *
+         * @return a list of current metering areas
+         * @hide
+         */
+        public List<Area> getMeteringAreas() {
+            return splitArea(KEY_METERING_AREAS);
+        }
+
+        /**
+         * Sets metering areas. See {@link #getMeteringAreas()} for
+         * documentation.
+         *
+         * @param meteringAreas the metering areas
+         * @see #getMeteringAreas()
+         * @hide
+         */
+        public void setMeteringAreas(List<Area> meteringAreas) {
+            set(KEY_METERING_AREAS, meteringAreas);
         }
 
         // Splits a comma delimited string to an ArrayList of String.
diff --git a/core/java/android/speech/tts/ITtsCallback.aidl b/core/java/android/nfc/ApduList.aidl
old mode 100755
new mode 100644
similarity index 66%
copy from core/java/android/speech/tts/ITtsCallback.aidl
copy to core/java/android/nfc/ApduList.aidl
index c9898eb..f6236b2
--- a/core/java/android/speech/tts/ITtsCallback.aidl
+++ b/core/java/android/nfc/ApduList.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2011 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,6 @@
  * limitations under the License.
  */
 
-package android.speech.tts;
+package android.nfc;
 
-/**
- * AIDL for the callback from the TTS Service
- * ITtsCallback.java is autogenerated from this.
- *
- * {@hide}
- */
-oneway interface ITtsCallback {
-    void utteranceCompleted(String utteranceId);
-}
+parcelable ApduList;
\ No newline at end of file
diff --git a/core/java/android/nfc/ApduList.java b/core/java/android/nfc/ApduList.java
new file mode 100644
index 0000000..85b0547
--- /dev/null
+++ b/core/java/android/nfc/ApduList.java
@@ -0,0 +1,68 @@
+package android.nfc;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @hide
+ */
+public class ApduList implements Parcelable {
+
+    private ArrayList<byte[]> commands = new ArrayList<byte[]>();
+
+    public ApduList() {
+    }
+
+    public void add(byte[] command) {
+        commands.add(command);
+    }
+
+    public List<byte[]> get() {
+        return commands;
+    }
+
+    public static final Parcelable.Creator<ApduList> CREATOR =
+        new Parcelable.Creator<ApduList>() {
+        @Override
+        public ApduList createFromParcel(Parcel in) {
+            return new ApduList(in);
+        }
+
+        @Override
+        public ApduList[] newArray(int size) {
+            return new ApduList[size];
+        }
+    };
+
+    private ApduList(Parcel in) {
+        int count = in.readInt();
+
+        for (int i = 0 ; i < count ; i++) {
+
+            int length = in.readInt();
+            byte[] cmd = new byte[length];
+            in.readByteArray(cmd);
+            commands.add(cmd);
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(commands.size());
+
+        for (byte[] cmd : commands) {
+            dest.writeInt(cmd.length);
+            dest.writeByteArray(cmd);
+        }
+    }
+}
+
+
diff --git a/core/java/android/nfc/INfcAdapterExtras.aidl b/core/java/android/nfc/INfcAdapterExtras.aidl
index ab5c1a6..8677a50 100755
--- a/core/java/android/nfc/INfcAdapterExtras.aidl
+++ b/core/java/android/nfc/INfcAdapterExtras.aidl
@@ -16,8 +16,10 @@
 
 package android.nfc;
 
+import android.nfc.ApduList;
 import android.os.Bundle;
 
+
 /**
  * {@hide}
  */
@@ -26,5 +28,7 @@
     Bundle close();
     Bundle transceive(in byte[] data_in);
     int getCardEmulationRoute();
-    void setCardEmulationRoute(int route);    
+    void setCardEmulationRoute(int route);
+    void registerTearDownApdus(String packageName, in ApduList apdu);
+    void unregisterTearDownApdus(String packageName);
 }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 90e2e79..b0f3ac3 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -26,6 +26,7 @@
 import android.telephony.SignalStrength;
 import android.util.Log;
 import android.util.Printer;
+import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
 
@@ -408,15 +409,19 @@
     }
 
     public final static class HistoryItem implements Parcelable {
+        static final String TAG = "HistoryItem";
+        static final boolean DEBUG = false;
+        
         public HistoryItem next;
         
         public long time;
         
-        public static final byte CMD_UPDATE = 0;
-        public static final byte CMD_START = 1;
-        public static final byte CMD_OVERFLOW = 2;
+        public static final byte CMD_NULL = 0;
+        public static final byte CMD_UPDATE = 1;
+        public static final byte CMD_START = 2;
+        public static final byte CMD_OVERFLOW = 3;
         
-        public byte cmd;
+        public byte cmd = CMD_NULL;
         
         public byte batteryLevel;
         public byte batteryStatus;
@@ -427,33 +432,38 @@
         public char batteryVoltage;
         
         // Constants from SCREEN_BRIGHTNESS_*
-        public static final int STATE_BRIGHTNESS_MASK = 0x000000f;
+        public static final int STATE_BRIGHTNESS_MASK = 0x0000000f;
         public static final int STATE_BRIGHTNESS_SHIFT = 0;
         // Constants from SIGNAL_STRENGTH_*
-        public static final int STATE_SIGNAL_STRENGTH_MASK = 0x00000f0;
+        public static final int STATE_SIGNAL_STRENGTH_MASK = 0x000000f0;
         public static final int STATE_SIGNAL_STRENGTH_SHIFT = 4;
         // Constants from ServiceState.STATE_*
-        public static final int STATE_PHONE_STATE_MASK = 0x0000f00;
+        public static final int STATE_PHONE_STATE_MASK = 0x00000f00;
         public static final int STATE_PHONE_STATE_SHIFT = 8;
         // Constants from DATA_CONNECTION_*
-        public static final int STATE_DATA_CONNECTION_MASK = 0x000f000;
+        public static final int STATE_DATA_CONNECTION_MASK = 0x0000f000;
         public static final int STATE_DATA_CONNECTION_SHIFT = 12;
         
-        public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<30;
-        public static final int STATE_SCREEN_ON_FLAG = 1<<29;
+        // These states always appear directly in the first int token
+        // of a delta change; they should be ones that change relatively
+        // frequently.
+        public static final int STATE_WAKE_LOCK_FLAG = 1<<30;
+        public static final int STATE_SENSOR_ON_FLAG = 1<<29;
         public static final int STATE_GPS_ON_FLAG = 1<<28;
-        public static final int STATE_PHONE_IN_CALL_FLAG = 1<<27;
-        public static final int STATE_PHONE_SCANNING_FLAG = 1<<26;
-        public static final int STATE_WIFI_ON_FLAG = 1<<25;
-        public static final int STATE_WIFI_RUNNING_FLAG = 1<<24;
-        public static final int STATE_WIFI_FULL_LOCK_FLAG = 1<<23;
-        public static final int STATE_WIFI_SCAN_LOCK_FLAG = 1<<22;
-        public static final int STATE_WIFI_MULTICAST_ON_FLAG = 1<<21;
-        public static final int STATE_BLUETOOTH_ON_FLAG = 1<<20;
-        public static final int STATE_AUDIO_ON_FLAG = 1<<19;
-        public static final int STATE_VIDEO_ON_FLAG = 1<<18;
-        public static final int STATE_WAKE_LOCK_FLAG = 1<<17;
-        public static final int STATE_SENSOR_ON_FLAG = 1<<16;
+        public static final int STATE_PHONE_SCANNING_FLAG = 1<<27;
+        public static final int STATE_WIFI_RUNNING_FLAG = 1<<26;
+        public static final int STATE_WIFI_FULL_LOCK_FLAG = 1<<25;
+        public static final int STATE_WIFI_SCAN_LOCK_FLAG = 1<<24;
+        public static final int STATE_WIFI_MULTICAST_ON_FLAG = 1<<23;
+        // These are on the lower bits used for the command; if they change
+        // we need to write another int of data.
+        public static final int STATE_AUDIO_ON_FLAG = 1<<22;
+        public static final int STATE_VIDEO_ON_FLAG = 1<<21;
+        public static final int STATE_SCREEN_ON_FLAG = 1<<20;
+        public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<19;
+        public static final int STATE_PHONE_IN_CALL_FLAG = 1<<18;
+        public static final int STATE_WIFI_ON_FLAG = 1<<17;
+        public static final int STATE_BLUETOOTH_ON_FLAG = 1<<16;
         
         public static final int MOST_INTERESTING_STATES =
             STATE_BATTERY_PLUGGED_FLAG | STATE_SCREEN_ON_FLAG
@@ -466,16 +476,7 @@
         
         public HistoryItem(long time, Parcel src) {
             this.time = time;
-            int bat = src.readInt();
-            cmd = (byte)(bat&0xff);
-            batteryLevel = (byte)((bat>>8)&0xff);
-            batteryStatus = (byte)((bat>>16)&0xf);
-            batteryHealth = (byte)((bat>>20)&0xf);
-            batteryPlugType = (byte)((bat>>24)&0xf);
-            bat = src.readInt();
-            batteryTemperature = (char)(bat&0xffff);
-            batteryVoltage = (char)((bat>>16)&0xffff);
-            states = src.readInt();
+            readFromParcel(src);
         }
         
         public int describeContents() {
@@ -495,6 +496,174 @@
             dest.writeInt(bat);
             dest.writeInt(states);
         }
+
+        private void readFromParcel(Parcel src) {
+            int bat = src.readInt();
+            cmd = (byte)(bat&0xff);
+            batteryLevel = (byte)((bat>>8)&0xff);
+            batteryStatus = (byte)((bat>>16)&0xf);
+            batteryHealth = (byte)((bat>>20)&0xf);
+            batteryPlugType = (byte)((bat>>24)&0xf);
+            bat = src.readInt();
+            batteryTemperature = (char)(bat&0xffff);
+            batteryVoltage = (char)((bat>>16)&0xffff);
+            states = src.readInt();
+        }
+
+        // Part of initial delta int that specifies the time delta.
+        static final int DELTA_TIME_MASK = 0x3ffff;
+        static final int DELTA_TIME_ABS = 0x3fffd;    // Following is an entire abs update.
+        static final int DELTA_TIME_INT = 0x3fffe;    // The delta is a following int
+        static final int DELTA_TIME_LONG = 0x3ffff;   // The delta is a following long
+        // Part of initial delta int holding the command code.
+        static final int DELTA_CMD_MASK = 0x3;
+        static final int DELTA_CMD_SHIFT = 18;
+        // Flag in delta int: a new battery level int follows.
+        static final int DELTA_BATTERY_LEVEL_FLAG = 1<<20;
+        // Flag in delta int: a new full state and battery status int follows.
+        static final int DELTA_STATE_FLAG = 1<<21;
+        static final int DELTA_STATE_MASK = 0xffc00000;
+        
+        public void writeDelta(Parcel dest, HistoryItem last) {
+            if (last == null || last.cmd != CMD_UPDATE) {
+                dest.writeInt(DELTA_TIME_ABS);
+                writeToParcel(dest, 0);
+                return;
+            }
+            
+            final long deltaTime = time - last.time;
+            final int lastBatteryLevelInt = last.buildBatteryLevelInt();
+            final int lastStateInt = last.buildStateInt();
+            
+            int deltaTimeToken;
+            if (deltaTime < 0 || deltaTime > Integer.MAX_VALUE) {
+                deltaTimeToken = DELTA_TIME_LONG;
+            } else if (deltaTime >= DELTA_TIME_ABS) {
+                deltaTimeToken = DELTA_TIME_INT;
+            } else {
+                deltaTimeToken = (int)deltaTime;
+            }
+            int firstToken = deltaTimeToken
+                    | (cmd<<DELTA_CMD_SHIFT)
+                    | (states&DELTA_STATE_MASK);
+            final int batteryLevelInt = buildBatteryLevelInt();
+            final boolean batteryLevelIntChanged = batteryLevelInt != lastBatteryLevelInt;
+            if (batteryLevelIntChanged) {
+                firstToken |= DELTA_BATTERY_LEVEL_FLAG;
+            }
+            final int stateInt = buildStateInt();
+            final boolean stateIntChanged = stateInt != lastStateInt;
+            if (stateIntChanged) {
+                firstToken |= DELTA_STATE_FLAG;
+            }
+            dest.writeInt(firstToken);
+            if (DEBUG) Slog.i(TAG, "WRITE DELTA: firstToken=0x" + Integer.toHexString(firstToken)
+                    + " deltaTime=" + deltaTime);
+            
+            if (deltaTimeToken >= DELTA_TIME_INT) {
+                if (deltaTimeToken == DELTA_TIME_INT) {
+                    if (DEBUG) Slog.i(TAG, "WRITE DELTA: int deltaTime=" + (int)deltaTime);
+                    dest.writeInt((int)deltaTime);
+                } else {
+                    if (DEBUG) Slog.i(TAG, "WRITE DELTA: long deltaTime=" + deltaTime);
+                    dest.writeLong(deltaTime);
+                }
+            }
+            if (batteryLevelIntChanged) {
+                dest.writeInt(batteryLevelInt);
+                if (DEBUG) Slog.i(TAG, "WRITE DELTA: batteryToken=0x"
+                        + Integer.toHexString(batteryLevelInt)
+                        + " batteryLevel=" + batteryLevel
+                        + " batteryTemp=" + (int)batteryTemperature
+                        + " batteryVolt=" + (int)batteryVoltage);
+            }
+            if (stateIntChanged) {
+                dest.writeInt(stateInt);
+                if (DEBUG) Slog.i(TAG, "WRITE DELTA: stateToken=0x"
+                        + Integer.toHexString(stateInt)
+                        + " batteryStatus=" + batteryStatus
+                        + " batteryHealth=" + batteryHealth
+                        + " batteryPlugType=" + batteryPlugType
+                        + " states=0x" + Integer.toHexString(states));
+            }
+        }
+        
+        private int buildBatteryLevelInt() {
+            return ((((int)batteryLevel)<<24)&0xff000000)
+                    | ((((int)batteryTemperature)<<14)&0x00ffc000)
+                    | (((int)batteryVoltage)&0x00003fff);
+        }
+        
+        private int buildStateInt() {
+            return ((((int)batteryStatus)<<28)&0xf0000000)
+                    | ((((int)batteryHealth)<<24)&0x0f000000)
+                    | ((((int)batteryPlugType)<<22)&0x00c00000)
+                    | (states&(~DELTA_STATE_MASK));
+        }
+        
+        public void readDelta(Parcel src) {
+            int firstToken = src.readInt();
+            int deltaTimeToken = firstToken&DELTA_TIME_MASK;
+            cmd = (byte)((firstToken>>DELTA_CMD_SHIFT)&DELTA_CMD_MASK);
+            if (DEBUG) Slog.i(TAG, "READ DELTA: firstToken=0x" + Integer.toHexString(firstToken)
+                    + " deltaTimeToken=" + deltaTimeToken);
+            
+            if (deltaTimeToken < DELTA_TIME_ABS) {
+                time += deltaTimeToken;
+            } else if (deltaTimeToken == DELTA_TIME_ABS) {
+                time = src.readLong();
+                readFromParcel(src);
+                return;
+            } else if (deltaTimeToken == DELTA_TIME_INT) {
+                int delta = src.readInt();
+                time += delta;
+                if (DEBUG) Slog.i(TAG, "READ DELTA: time delta=" + delta + " new time=" + time);
+            } else {
+                long delta = src.readLong();
+                if (DEBUG) Slog.i(TAG, "READ DELTA: time delta=" + delta + " new time=" + time);
+                time += delta;
+            }
+            
+            if ((firstToken&DELTA_BATTERY_LEVEL_FLAG) != 0) {
+                int batteryLevelInt = src.readInt();
+                batteryLevel = (byte)((batteryLevelInt>>24)&0xff);
+                batteryTemperature = (char)((batteryLevelInt>>14)&0x3ff);
+                batteryVoltage = (char)(batteryLevelInt&0x3fff);
+                if (DEBUG) Slog.i(TAG, "READ DELTA: batteryToken=0x"
+                        + Integer.toHexString(batteryLevelInt)
+                        + " batteryLevel=" + batteryLevel
+                        + " batteryTemp=" + (int)batteryTemperature
+                        + " batteryVolt=" + (int)batteryVoltage);
+            }
+            
+            if ((firstToken&DELTA_STATE_FLAG) != 0) {
+                int stateInt = src.readInt();
+                states = (firstToken&DELTA_STATE_MASK) | (stateInt&(~DELTA_STATE_MASK));
+                batteryStatus = (byte)((stateInt>>28)&0xf);
+                batteryHealth = (byte)((stateInt>>24)&0xf);
+                batteryPlugType = (byte)((stateInt>>22)&0x3);
+                if (DEBUG) Slog.i(TAG, "READ DELTA: stateToken=0x"
+                        + Integer.toHexString(stateInt)
+                        + " batteryStatus=" + batteryStatus
+                        + " batteryHealth=" + batteryHealth
+                        + " batteryPlugType=" + batteryPlugType
+                        + " states=0x" + Integer.toHexString(states));
+            } else {
+                states = (firstToken&DELTA_STATE_MASK) | (states&(~DELTA_STATE_MASK));
+            }
+        }
+
+        public void clear() {
+            time = 0;
+            cmd = CMD_NULL;
+            batteryLevel = 0;
+            batteryStatus = 0;
+            batteryHealth = 0;
+            batteryPlugType = 0;
+            batteryTemperature = 0;
+            batteryVoltage = 0;
+            states = 0;
+        }
         
         public void setTo(HistoryItem o) {
             time = o.time;
@@ -556,11 +725,14 @@
 
     public abstract boolean getNextHistoryLocked(HistoryItem out);
 
-    /**
-     * Return the current history of battery state changes.
-     */
-    public abstract HistoryItem getHistory();
-    
+    public abstract void finishIteratingHistoryLocked();
+
+    public abstract boolean startIteratingOldHistoryLocked();
+
+    public abstract boolean getNextOldHistoryLocked(HistoryItem out);
+
+    public abstract void finishIteratingOldHistoryLocked();
+
     /**
      * Return the base time offset for the battery history.
      */
@@ -1729,7 +1901,7 @@
         }
     }
 
-    void printBitDescriptions(PrintWriter pw, int oldval, int newval, BitDescription[] descriptions) {
+    static void printBitDescriptions(PrintWriter pw, int oldval, int newval, BitDescription[] descriptions) {
         int diff = oldval ^ newval;
         if (diff == 0) return;
         for (int i=0; i<descriptions.length; i++) {
@@ -1753,6 +1925,125 @@
         }
     }
     
+    public void prepareForDumpLocked() {
+    }
+
+    public static class HistoryPrinter {
+        int oldState = 0;
+        int oldStatus = -1;
+        int oldHealth = -1;
+        int oldPlug = -1;
+        int oldTemp = -1;
+        int oldVolt = -1;
+
+        public void printNextItem(PrintWriter pw, HistoryItem rec, long now) {
+            pw.print("  ");
+            TimeUtils.formatDuration(rec.time-now, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
+            pw.print(" ");
+            if (rec.cmd == HistoryItem.CMD_START) {
+                pw.println(" START");
+            } else if (rec.cmd == HistoryItem.CMD_OVERFLOW) {
+                pw.println(" *OVERFLOW*");
+            } else {
+                if (rec.batteryLevel < 10) pw.print("00");
+                else if (rec.batteryLevel < 100) pw.print("0");
+                pw.print(rec.batteryLevel);
+                pw.print(" ");
+                if (rec.states < 0x10) pw.print("0000000");
+                else if (rec.states < 0x100) pw.print("000000");
+                else if (rec.states < 0x1000) pw.print("00000");
+                else if (rec.states < 0x10000) pw.print("0000");
+                else if (rec.states < 0x100000) pw.print("000");
+                else if (rec.states < 0x1000000) pw.print("00");
+                else if (rec.states < 0x10000000) pw.print("0");
+                pw.print(Integer.toHexString(rec.states));
+                if (oldStatus != rec.batteryStatus) {
+                    oldStatus = rec.batteryStatus;
+                    pw.print(" status=");
+                    switch (oldStatus) {
+                        case BatteryManager.BATTERY_STATUS_UNKNOWN:
+                            pw.print("unknown");
+                            break;
+                        case BatteryManager.BATTERY_STATUS_CHARGING:
+                            pw.print("charging");
+                            break;
+                        case BatteryManager.BATTERY_STATUS_DISCHARGING:
+                            pw.print("discharging");
+                            break;
+                        case BatteryManager.BATTERY_STATUS_NOT_CHARGING:
+                            pw.print("not-charging");
+                            break;
+                        case BatteryManager.BATTERY_STATUS_FULL:
+                            pw.print("full");
+                            break;
+                        default:
+                            pw.print(oldStatus);
+                            break;
+                    }
+                }
+                if (oldHealth != rec.batteryHealth) {
+                    oldHealth = rec.batteryHealth;
+                    pw.print(" health=");
+                    switch (oldHealth) {
+                        case BatteryManager.BATTERY_HEALTH_UNKNOWN:
+                            pw.print("unknown");
+                            break;
+                        case BatteryManager.BATTERY_HEALTH_GOOD:
+                            pw.print("good");
+                            break;
+                        case BatteryManager.BATTERY_HEALTH_OVERHEAT:
+                            pw.print("overheat");
+                            break;
+                        case BatteryManager.BATTERY_HEALTH_DEAD:
+                            pw.print("dead");
+                            break;
+                        case BatteryManager.BATTERY_HEALTH_OVER_VOLTAGE:
+                            pw.print("over-voltage");
+                            break;
+                        case BatteryManager.BATTERY_HEALTH_UNSPECIFIED_FAILURE:
+                            pw.print("failure");
+                            break;
+                        default:
+                            pw.print(oldHealth);
+                            break;
+                    }
+                }
+                if (oldPlug != rec.batteryPlugType) {
+                    oldPlug = rec.batteryPlugType;
+                    pw.print(" plug=");
+                    switch (oldPlug) {
+                        case 0:
+                            pw.print("none");
+                            break;
+                        case BatteryManager.BATTERY_PLUGGED_AC:
+                            pw.print("ac");
+                            break;
+                        case BatteryManager.BATTERY_PLUGGED_USB:
+                            pw.print("usb");
+                            break;
+                        default:
+                            pw.print(oldPlug);
+                            break;
+                    }
+                }
+                if (oldTemp != rec.batteryTemperature) {
+                    oldTemp = rec.batteryTemperature;
+                    pw.print(" temp=");
+                    pw.print(oldTemp);
+                }
+                if (oldVolt != rec.batteryVoltage) {
+                    oldVolt = rec.batteryVoltage;
+                    pw.print(" volt=");
+                    pw.print(oldVolt);
+                }
+                printBitDescriptions(pw, oldState, rec.states,
+                        HISTORY_STATE_DESCRIPTIONS);
+                pw.println();
+            }
+            oldState = rec.states;
+        }
+    }
+
     /**
      * Dumps a human-readable summary of the battery statistics to the given PrintWriter.
      *
@@ -1760,122 +2051,28 @@
      */
     @SuppressWarnings("unused")
     public void dumpLocked(PrintWriter pw) {
+        prepareForDumpLocked();
+
+        long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
+
         final HistoryItem rec = new HistoryItem();
         if (startIteratingHistoryLocked()) {
             pw.println("Battery History:");
-            long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
-            int oldState = 0;
-            int oldStatus = -1;
-            int oldHealth = -1;
-            int oldPlug = -1;
-            int oldTemp = -1;
-            int oldVolt = -1;
+            HistoryPrinter hprinter = new HistoryPrinter();
             while (getNextHistoryLocked(rec)) {
-                pw.print("  ");
-                TimeUtils.formatDuration(rec.time-now, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
-                pw.print(" ");
-                if (rec.cmd == HistoryItem.CMD_START) {
-                    pw.println(" START");
-                } else if (rec.cmd == HistoryItem.CMD_OVERFLOW) {
-                    pw.println(" *OVERFLOW*");
-                } else {
-                    if (rec.batteryLevel < 10) pw.print("00");
-                    else if (rec.batteryLevel < 100) pw.print("0");
-                    pw.print(rec.batteryLevel);
-                    pw.print(" ");
-                    if (rec.states < 0x10) pw.print("0000000");
-                    else if (rec.states < 0x100) pw.print("000000");
-                    else if (rec.states < 0x1000) pw.print("00000");
-                    else if (rec.states < 0x10000) pw.print("0000");
-                    else if (rec.states < 0x100000) pw.print("000");
-                    else if (rec.states < 0x1000000) pw.print("00");
-                    else if (rec.states < 0x10000000) pw.print("0");
-                    pw.print(Integer.toHexString(rec.states));
-                    if (oldStatus != rec.batteryStatus) {
-                        oldStatus = rec.batteryStatus;
-                        pw.print(" status=");
-                        switch (oldStatus) {
-                            case BatteryManager.BATTERY_STATUS_UNKNOWN:
-                                pw.print("unknown");
-                                break;
-                            case BatteryManager.BATTERY_STATUS_CHARGING:
-                                pw.print("charging");
-                                break;
-                            case BatteryManager.BATTERY_STATUS_DISCHARGING:
-                                pw.print("discharging");
-                                break;
-                            case BatteryManager.BATTERY_STATUS_NOT_CHARGING:
-                                pw.print("not-charging");
-                                break;
-                            case BatteryManager.BATTERY_STATUS_FULL:
-                                pw.print("full");
-                                break;
-                            default:
-                                pw.print(oldStatus);
-                                break;
-                        }
-                    }
-                    if (oldHealth != rec.batteryHealth) {
-                        oldHealth = rec.batteryHealth;
-                        pw.print(" health=");
-                        switch (oldHealth) {
-                            case BatteryManager.BATTERY_HEALTH_UNKNOWN:
-                                pw.print("unknown");
-                                break;
-                            case BatteryManager.BATTERY_HEALTH_GOOD:
-                                pw.print("good");
-                                break;
-                            case BatteryManager.BATTERY_HEALTH_OVERHEAT:
-                                pw.print("overheat");
-                                break;
-                            case BatteryManager.BATTERY_HEALTH_DEAD:
-                                pw.print("dead");
-                                break;
-                            case BatteryManager.BATTERY_HEALTH_OVER_VOLTAGE:
-                                pw.print("over-voltage");
-                                break;
-                            case BatteryManager.BATTERY_HEALTH_UNSPECIFIED_FAILURE:
-                                pw.print("failure");
-                                break;
-                            default:
-                                pw.print(oldHealth);
-                                break;
-                        }
-                    }
-                    if (oldPlug != rec.batteryPlugType) {
-                        oldPlug = rec.batteryPlugType;
-                        pw.print(" plug=");
-                        switch (oldPlug) {
-                            case 0:
-                                pw.print("none");
-                                break;
-                            case BatteryManager.BATTERY_PLUGGED_AC:
-                                pw.print("ac");
-                                break;
-                            case BatteryManager.BATTERY_PLUGGED_USB:
-                                pw.print("usb");
-                                break;
-                            default:
-                                pw.print(oldPlug);
-                                break;
-                        }
-                    }
-                    if (oldTemp != rec.batteryTemperature) {
-                        oldTemp = rec.batteryTemperature;
-                        pw.print(" temp=");
-                        pw.print(oldTemp);
-                    }
-                    if (oldVolt != rec.batteryVoltage) {
-                        oldVolt = rec.batteryVoltage;
-                        pw.print(" volt=");
-                        pw.print(oldVolt);
-                    }
-                    printBitDescriptions(pw, oldState, rec.states,
-                            HISTORY_STATE_DESCRIPTIONS);
-                    pw.println();
-                }
-                oldState = rec.states;
+                hprinter.printNextItem(pw, rec, now);
             }
+            finishIteratingHistoryLocked();
+            pw.println("");
+        }
+
+        if (startIteratingOldHistoryLocked()) {
+            pw.println("Old battery History:");
+            HistoryPrinter hprinter = new HistoryPrinter();
+            while (getNextOldHistoryLocked(rec)) {
+                hprinter.printNextItem(pw, rec, now);
+            }
+            finishIteratingOldHistoryLocked();
             pw.println("");
         }
         
@@ -1918,6 +2115,8 @@
     
     @SuppressWarnings("unused")
     public void dumpCheckinLocked(PrintWriter pw, String[] args, List<ApplicationInfo> apps) {
+        prepareForDumpLocked();
+
         boolean isUnpluggedOnly = false;
         
         for (String arg : args) {
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index aa959b4..f7661b6 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -35,64 +35,64 @@
     //consider ParcelFileDescriptor A(fileDescriptor fd),  ParcelFileDescriptor B(A)
     //in this particular case fd.close might be invoked twice.
     private final ParcelFileDescriptor mParcelDescriptor;
-    
+
     /**
      * For use with {@link #open}: if {@link #MODE_CREATE} has been supplied
      * and this file doesn't already exist, then create the file with
      * permissions such that any application can read it.
      */
     public static final int MODE_WORLD_READABLE = 0x00000001;
-    
+
     /**
      * For use with {@link #open}: if {@link #MODE_CREATE} has been supplied
      * and this file doesn't already exist, then create the file with
      * permissions such that any application can write it.
      */
     public static final int MODE_WORLD_WRITEABLE = 0x00000002;
-    
+
     /**
      * For use with {@link #open}: open the file with read-only access.
      */
     public static final int MODE_READ_ONLY = 0x10000000;
-    
+
     /**
      * For use with {@link #open}: open the file with write-only access.
      */
     public static final int MODE_WRITE_ONLY = 0x20000000;
-    
+
     /**
      * For use with {@link #open}: open the file with read and write access.
      */
     public static final int MODE_READ_WRITE = 0x30000000;
-    
+
     /**
      * For use with {@link #open}: create the file if it doesn't already exist.
      */
     public static final int MODE_CREATE = 0x08000000;
-    
+
     /**
      * For use with {@link #open}: erase contents of file when opening.
      */
     public static final int MODE_TRUNCATE = 0x04000000;
-    
+
     /**
      * For use with {@link #open}: append to end of file while writing.
      */
     public static final int MODE_APPEND = 0x02000000;
-    
+
     /**
      * Create a new ParcelFileDescriptor accessing a given file.
-     * 
+     *
      * @param file The file to be opened.
      * @param mode The desired access mode, must be one of
      * {@link #MODE_READ_ONLY}, {@link #MODE_WRITE_ONLY}, or
      * {@link #MODE_READ_WRITE}; may also be any combination of
      * {@link #MODE_CREATE}, {@link #MODE_TRUNCATE},
      * {@link #MODE_WORLD_READABLE}, and {@link #MODE_WORLD_WRITEABLE}.
-     * 
+     *
      * @return Returns a new ParcelFileDescriptor pointing to the given
      * file.
-     * 
+     *
      * @throws FileNotFoundException Throws FileNotFoundException if the given
      * file does not exist or can not be opened with the requested mode.
      */
@@ -106,12 +106,12 @@
                 security.checkWrite(path);
             }
         }
-        
+
         if ((mode&MODE_READ_WRITE) == 0) {
             throw new IllegalArgumentException(
                     "Must specify MODE_READ_ONLY, MODE_WRITE_ONLY, or MODE_READ_WRITE");
         }
-        
+
         FileDescriptor fd = Parcel.openFileDescriptor(path, mode);
         return fd != null ? new ParcelFileDescriptor(fd) : null;
     }
@@ -137,13 +137,10 @@
      *         specified Socket.
      */
     public static ParcelFileDescriptor fromSocket(Socket socket) {
-        FileDescriptor fd = getFileDescriptorFromSocket(socket);
+        FileDescriptor fd = socket.getFileDescriptor$();
         return fd != null ? new ParcelFileDescriptor(fd) : null;
     }
 
-    // Extracts the file descriptor from the specified socket and returns it untouched
-    private static native FileDescriptor getFileDescriptorFromSocket(Socket socket);
-
     /**
      * Create two ParcelFileDescriptors structured as a data pipe.  The first
      * ParcelFileDescriptor in the returned array is the read side; the second
@@ -187,26 +184,26 @@
 
     /**
      * Retrieve the actual FileDescriptor associated with this object.
-     * 
+     *
      * @return Returns the FileDescriptor associated with this object.
      */
     public FileDescriptor getFileDescriptor() {
         return mFileDescriptor;
     }
-    
+
     /**
      * Return the total size of the file representing this fd, as determined
      * by stat().  Returns -1 if the fd is not a file.
      */
     public native long getStatSize();
-    
+
     /**
      * This is needed for implementing AssetFileDescriptor.AutoCloseOutputStream,
      * and I really don't think we want it to be public.
      * @hide
      */
     public native long seekTo(long pos);
-    
+
     /**
      * Return the native fd int for this ParcelFileDescriptor.  The
      * ParcelFileDescriptor still owns the fd, and it still must be closed
@@ -218,9 +215,9 @@
         }
         return getFdNative();
     }
-    
+
     private native int getFdNative();
-    
+
     /**
      * Return the native fd int for this ParcelFileDescriptor and detach it
      * from the object here.  You are now responsible for closing the fd in
@@ -240,11 +237,11 @@
         Parcel.clearFileDescriptor(mFileDescriptor);
         return fd;
     }
-    
+
     /**
      * Close the ParcelFileDescriptor. This implementation closes the underlying
      * OS resources allocated to represent this stream.
-     * 
+     *
      * @throws IOException
      *             If an error occurs attempting to close this ParcelFileDescriptor.
      */
@@ -261,7 +258,7 @@
             Parcel.closeFileDescriptor(mFileDescriptor);
         }
     }
-    
+
     /**
      * An InputStream you can create on a ParcelFileDescriptor, which will
      * take care of calling {@link ParcelFileDescriptor#close
@@ -269,7 +266,7 @@
      */
     public static class AutoCloseInputStream extends FileInputStream {
         private final ParcelFileDescriptor mFd;
-        
+
         public AutoCloseInputStream(ParcelFileDescriptor fd) {
             super(fd.getFileDescriptor());
             mFd = fd;
@@ -284,7 +281,7 @@
             }
         }
     }
-    
+
     /**
      * An OutputStream you can create on a ParcelFileDescriptor, which will
      * take care of calling {@link ParcelFileDescriptor#close
@@ -292,7 +289,7 @@
      */
     public static class AutoCloseOutputStream extends FileOutputStream {
         private final ParcelFileDescriptor mFd;
-        
+
         public AutoCloseOutputStream(ParcelFileDescriptor fd) {
             super(fd.getFileDescriptor());
             mFd = fd;
@@ -307,12 +304,12 @@
             }
         }
     }
-    
+
     @Override
     public String toString() {
         return "{ParcelFileDescriptor: " + mFileDescriptor + "}";
     }
-    
+
     @Override
     protected void finalize() throws Throwable {
         try {
@@ -323,13 +320,13 @@
             super.finalize();
         }
     }
-    
+
     public ParcelFileDescriptor(ParcelFileDescriptor descriptor) {
         super();
         mParcelDescriptor = descriptor;
         mFileDescriptor = mParcelDescriptor.mFileDescriptor;
     }
-    
+
     /*package */ParcelFileDescriptor(FileDescriptor descriptor) {
         super();
         if (descriptor == null) {
@@ -338,7 +335,7 @@
         mFileDescriptor = descriptor;
         mParcelDescriptor = null;
     }
-    
+
     /* Parcelable interface */
     public int describeContents() {
         return Parcelable.CONTENTS_FILE_DESCRIPTOR;
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 7151ac2..f85df6c 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -101,7 +101,7 @@
      * Defines the UID/GID for the NFC service process.
      * @hide
      */
-    public static final int NFC_UID = 1023;
+    public static final int NFC_UID = 1025;
 
     /**
      * Defines the GID for the group that allows write access to the internal media storage.
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index c1dd911..ae605fb 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -70,7 +70,7 @@
     private static File RECOVERY_DIR = new File("/cache/recovery");
     private static File COMMAND_FILE = new File(RECOVERY_DIR, "command");
     private static File LOG_FILE = new File(RECOVERY_DIR, "log");
-    private static String LAST_LOG_FILENAME = "last_log";
+    private static String LAST_PREFIX = "last_";
 
     // Length limits for reading files.
     private static int LOG_FILE_MAX_LENGTH = 64 * 1024;
@@ -415,10 +415,11 @@
             Log.e(TAG, "Error reading recovery log", e);
         }
 
-        // Delete everything in RECOVERY_DIR except LAST_LOG_FILENAME
+        // Delete everything in RECOVERY_DIR except those beginning
+        // with LAST_PREFIX
         String[] names = RECOVERY_DIR.list();
         for (int i = 0; names != null && i < names.length; i++) {
-            if (names[i].equals(LAST_LOG_FILENAME)) continue;
+            if (names[i].startsWith(LAST_PREFIX)) continue;
             File f = new File(RECOVERY_DIR, names[i]);
             if (!f.delete()) {
                 Log.e(TAG, "Can't delete: " + f);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ccab8e5..2f4a4bb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1505,6 +1505,13 @@
         public static final Uri DEFAULT_ALARM_ALERT_URI = getUriFor(ALARM_ALERT);
 
         /**
+         * Persistent store for the system default media button event receiver.
+         *
+         * @hide
+         */
+        public static final String MEDIA_BUTTON_RECEIVER = "media_button_receiver";
+
+        /**
          * Setting to enable Auto Replace (AutoText) in text editors. 1 = On, 0 = Off
          */
         public static final String TEXT_AUTO_REPLACE = "auto_replace";
@@ -3707,6 +3714,13 @@
                 "inet_condition_debounce_down_delay";
 
         /**
+         * URL to open browser on to allow user to manage a prepay account
+         * @hide
+         */
+        public static final String SETUP_PREPAID_DATA_SERVICE_URL =
+                "setup_prepaid_data_service_url";
+
+        /**
          * @hide
          */
         public static final String[] SETTINGS_TO_BACKUP = {
diff --git a/core/java/android/speech/tts/BlockingMediaPlayer.java b/core/java/android/speech/tts/BlockingMediaPlayer.java
new file mode 100644
index 0000000..3cf60dd
--- /dev/null
+++ b/core/java/android/speech/tts/BlockingMediaPlayer.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.speech.tts;
+
+import android.content.Context;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.util.Log;
+
+/**
+ * A media player that allows blocking to wait for it to finish.
+ */
+class BlockingMediaPlayer {
+
+    private static final String TAG = "BlockMediaPlayer";
+
+    private static final String MEDIA_PLAYER_THREAD_NAME = "TTS-MediaPlayer";
+
+    private final Context mContext;
+    private final Uri mUri;
+    private final int mStreamType;
+    private final ConditionVariable mDone;
+    // Only accessed on the Handler thread
+    private MediaPlayer mPlayer;
+    private volatile boolean mFinished;
+
+    /**
+     * Creates a new blocking media player.
+     * Creating a blocking media player is a cheap operation.
+     *
+     * @param context
+     * @param uri
+     * @param streamType
+     */
+    public BlockingMediaPlayer(Context context, Uri uri, int streamType) {
+        mContext = context;
+        mUri = uri;
+        mStreamType = streamType;
+        mDone = new ConditionVariable();
+
+    }
+
+    /**
+     * Starts playback and waits for it to finish.
+     * Can be called from any thread.
+     *
+     * @return {@code true} if the playback finished normally, {@code false} if the playback
+     *         failed or {@link #stop} was called before the playback finished.
+     */
+    public boolean startAndWait() {
+        HandlerThread thread = new HandlerThread(MEDIA_PLAYER_THREAD_NAME);
+        thread.start();
+        Handler handler = new Handler(thread.getLooper());
+        mFinished = false;
+        handler.post(new Runnable() {
+            @Override
+            public void run() {
+                startPlaying();
+            }
+        });
+        mDone.block();
+        handler.post(new Runnable() {
+            @Override
+            public void run() {
+                finish();
+                // No new messages should get posted to the handler thread after this
+                Looper.myLooper().quit();
+            }
+        });
+        return mFinished;
+    }
+
+    /**
+     * Stops playback. Can be called multiple times.
+     * Can be called from any thread.
+     */
+    public void stop() {
+        mDone.open();
+    }
+
+    /**
+     * Starts playback.
+     * Called on the handler thread.
+     */
+    private void startPlaying() {
+        mPlayer = MediaPlayer.create(mContext, mUri);
+        if (mPlayer == null) {
+            Log.w(TAG, "Failed to play " + mUri);
+            mDone.open();
+            return;
+        }
+        try {
+            mPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+                @Override
+                public boolean onError(MediaPlayer mp, int what, int extra) {
+                    Log.w(TAG, "Audio playback error: " + what + ", " + extra);
+                    mDone.open();
+                    return true;
+                }
+            });
+            mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+                @Override
+                public void onCompletion(MediaPlayer mp) {
+                    mFinished = true;
+                    mDone.open();
+                }
+            });
+            mPlayer.setAudioStreamType(mStreamType);
+            mPlayer.start();
+        } catch (IllegalArgumentException ex) {
+            Log.w(TAG, "MediaPlayer failed", ex);
+            mDone.open();
+        }
+    }
+
+    /**
+     * Stops playback and release the media player.
+     * Called on the handler thread.
+     */
+    private void finish() {
+        try {
+            mPlayer.stop();
+        } catch (IllegalStateException ex) {
+            // Do nothing, the player is already stopped
+        }
+        mPlayer.release();
+    }
+
+}
\ No newline at end of file
diff --git a/core/java/android/speech/tts/FileSynthesisRequest.java b/core/java/android/speech/tts/FileSynthesisRequest.java
new file mode 100644
index 0000000..6a9b2dc
--- /dev/null
+++ b/core/java/android/speech/tts/FileSynthesisRequest.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.speech.tts;
+
+import android.media.AudioFormat;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Speech synthesis request that writes the audio to a WAV file.
+ */
+class FileSynthesisRequest extends SynthesisRequest {
+
+    private static final String TAG = "FileSynthesisRequest";
+    private static final boolean DBG = false;
+
+    private static final int MAX_AUDIO_BUFFER_SIZE = 8192;
+
+    private static final int WAV_HEADER_LENGTH = 44;
+    private static final short WAV_FORMAT_PCM = 0x0001;
+
+    private final Object mStateLock = new Object();
+    private final File mFileName;
+    private int mSampleRateInHz;
+    private int mAudioFormat;
+    private int mChannelCount;
+    private RandomAccessFile mFile;
+    private boolean mStopped = false;
+
+    FileSynthesisRequest(String text, File fileName) {
+        super(text);
+        mFileName = fileName;
+    }
+
+    @Override
+    void stop() {
+        synchronized (mStateLock) {
+            mStopped = true;
+            cleanUp();
+        }
+    }
+
+    /**
+     * Must be called while holding the monitor on {@link #mStateLock}.
+     */
+    private void cleanUp() {
+        closeFile();
+        if (mFile != null) {
+            mFileName.delete();
+        }
+    }
+
+    /**
+     * Must be called while holding the monitor on {@link #mStateLock}.
+     */
+    private void closeFile() {
+        try {
+            if (mFile != null) {
+                mFile.close();
+                mFile = null;
+            }
+        } catch (IOException ex) {
+            Log.e(TAG, "Failed to close " + mFileName + ": " + ex);
+        }
+    }
+
+    @Override
+    public int getMaxBufferSize() {
+        return MAX_AUDIO_BUFFER_SIZE;
+    }
+
+    @Override
+    public int start(int sampleRateInHz, int audioFormat, int channelCount) {
+        if (DBG) {
+            Log.d(TAG, "FileSynthesisRequest.start(" + sampleRateInHz + "," + audioFormat
+                    + "," + channelCount + ")");
+        }
+        synchronized (mStateLock) {
+            if (mStopped) {
+                if (DBG) Log.d(TAG, "Request has been aborted.");
+                return TextToSpeech.ERROR;
+            }
+            if (mFile != null) {
+                cleanUp();
+                throw new IllegalArgumentException("FileSynthesisRequest.start() called twice");
+            }
+            mSampleRateInHz = sampleRateInHz;
+            mAudioFormat = audioFormat;
+            mChannelCount = channelCount;
+            try {
+                mFile = new RandomAccessFile(mFileName, "rw");
+                // Reserve space for WAV header
+                mFile.write(new byte[WAV_HEADER_LENGTH]);
+                return TextToSpeech.SUCCESS;
+            } catch (IOException ex) {
+                Log.e(TAG, "Failed to open " + mFileName + ": " + ex);
+                cleanUp();
+                return TextToSpeech.ERROR;
+            }
+        }
+    }
+
+    @Override
+    public int audioAvailable(byte[] buffer, int offset, int length) {
+        if (DBG) {
+            Log.d(TAG, "FileSynthesisRequest.audioAvailable(" + buffer + "," + offset
+                    + "," + length + ")");
+        }
+        synchronized (mStateLock) {
+            if (mStopped) {
+                if (DBG) Log.d(TAG, "Request has been aborted.");
+                return TextToSpeech.ERROR;
+            }
+            if (mFile == null) {
+                Log.e(TAG, "File not open");
+                return TextToSpeech.ERROR;
+            }
+            try {
+                mFile.write(buffer, offset, length);
+                return TextToSpeech.SUCCESS;
+            } catch (IOException ex) {
+                Log.e(TAG, "Failed to write to " + mFileName + ": " + ex);
+                cleanUp();
+                return TextToSpeech.ERROR;
+            }
+        }
+    }
+
+    @Override
+    public int done() {
+        if (DBG) Log.d(TAG, "FileSynthesisRequest.done()");
+        synchronized (mStateLock) {
+            if (mStopped) {
+                if (DBG) Log.d(TAG, "Request has been aborted.");
+                return TextToSpeech.ERROR;
+            }
+            if (mFile == null) {
+                Log.e(TAG, "File not open");
+                return TextToSpeech.ERROR;
+            }
+            try {
+                // Write WAV header at start of file
+                mFile.seek(0);
+                int dataLength = (int) (mFile.length() - WAV_HEADER_LENGTH);
+                mFile.write(
+                        makeWavHeader(mSampleRateInHz, mAudioFormat, mChannelCount, dataLength));
+                closeFile();
+                return TextToSpeech.SUCCESS;
+            } catch (IOException ex) {
+                Log.e(TAG, "Failed to write to " + mFileName + ": " + ex);
+                cleanUp();
+                return TextToSpeech.ERROR;
+            }
+        }
+    }
+
+    @Override
+    public int completeAudioAvailable(int sampleRateInHz, int audioFormat, int channelCount,
+            byte[] buffer, int offset, int length) {
+        synchronized (mStateLock) {
+            if (mStopped) {
+                if (DBG) Log.d(TAG, "Request has been aborted.");
+                return TextToSpeech.ERROR;
+            }
+        }
+        FileOutputStream out = null;
+        try {
+            out = new FileOutputStream(mFileName);
+            out.write(makeWavHeader(sampleRateInHz, audioFormat, channelCount, length));
+            out.write(buffer, offset, length);
+            return TextToSpeech.SUCCESS;
+        } catch (IOException ex) {
+            Log.e(TAG, "Failed to write to " + mFileName + ": " + ex);
+            return TextToSpeech.ERROR;
+        } finally {
+            try {
+                if (out != null) {
+                    out.close();
+                }
+            } catch (IOException ex) {
+                Log.e(TAG, "Failed to close " + mFileName + ": " + ex);
+            }
+        }
+    }
+
+    private byte[] makeWavHeader(int sampleRateInHz, int audioFormat, int channelCount,
+            int dataLength) {
+        // TODO: is AudioFormat.ENCODING_DEFAULT always the same as ENCODING_PCM_16BIT?
+        int sampleSizeInBytes = (audioFormat == AudioFormat.ENCODING_PCM_8BIT ? 1 : 2);
+        int byteRate = sampleRateInHz * sampleSizeInBytes * channelCount;
+        short blockAlign = (short) (sampleSizeInBytes * channelCount);
+        short bitsPerSample = (short) (sampleSizeInBytes * 8);
+
+        byte[] headerBuf = new byte[WAV_HEADER_LENGTH];
+        ByteBuffer header = ByteBuffer.wrap(headerBuf);
+        header.order(ByteOrder.LITTLE_ENDIAN);
+
+        header.put(new byte[]{ 'R', 'I', 'F', 'F' });
+        header.putInt(dataLength + WAV_HEADER_LENGTH - 8);  // RIFF chunk size
+        header.put(new byte[]{ 'W', 'A', 'V', 'E' });
+        header.put(new byte[]{ 'f', 'm', 't', ' ' });
+        header.putInt(16);  // size of fmt chunk
+        header.putShort(WAV_FORMAT_PCM);
+        header.putShort((short) channelCount);
+        header.putInt(sampleRateInHz);
+        header.putInt(byteRate);
+        header.putShort(blockAlign);
+        header.putShort(bitsPerSample);
+        header.put(new byte[]{ 'd', 'a', 't', 'a' });
+        header.putInt(dataLength);
+
+        return headerBuf;
+    }
+
+}
diff --git a/core/java/android/speech/tts/ITtsCallback.aidl b/core/java/android/speech/tts/ITextToSpeechCallback.aidl
similarity index 78%
rename from core/java/android/speech/tts/ITtsCallback.aidl
rename to core/java/android/speech/tts/ITextToSpeechCallback.aidl
index c9898eb..40902ae 100755
--- a/core/java/android/speech/tts/ITtsCallback.aidl
+++ b/core/java/android/speech/tts/ITextToSpeechCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2011 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,15 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package android.speech.tts;
 
 /**
- * AIDL for the callback from the TTS Service
- * ITtsCallback.java is autogenerated from this.
+ * Interface for callbacks from TextToSpeechService
  *
  * {@hide}
  */
-oneway interface ITtsCallback {
+oneway interface ITextToSpeechCallback {
     void utteranceCompleted(String utteranceId);
 }
diff --git a/core/java/android/speech/tts/ITextToSpeechService.aidl b/core/java/android/speech/tts/ITextToSpeechService.aidl
new file mode 100644
index 0000000..ff3fa11
--- /dev/null
+++ b/core/java/android/speech/tts/ITextToSpeechService.aidl
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.speech.tts;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.speech.tts.ITextToSpeechCallback;
+
+/**
+ * Interface for TextToSpeech to talk to TextToSpeechService.
+ *
+ * {@hide}
+ */
+interface ITextToSpeechService {
+
+    /**
+     * Tells the engine to synthesize some speech and play it back.
+     *
+     * @param callingApp The package name of the calling app. Used to connect requests
+     *         callbacks and to clear requests when the calling app is stopping.
+     * @param text The text to synthesize.
+     * @param queueMode Determines what to do to requests already in the queue.
+     * @param param Request parameters.
+     */
+    int speak(in String callingApp, in String text, in int queueMode, in Bundle params);
+
+    /**
+     * Tells the engine to synthesize some speech and write it to a file.
+     *
+     * @param callingApp The package name of the calling app. Used to connect requests
+     *         callbacks and to clear requests when the calling app is stopping.
+     * @param text The text to synthesize.
+     * @param filename The file to write the synthesized audio to.
+     * @param param Request parameters.
+     */
+    int synthesizeToFile(in String callingApp, in String text,
+        in String filename, in Bundle params);
+
+    /**
+     * Plays an existing audio resource.
+     *
+     * @param callingApp The package name of the calling app. Used to connect requests
+     *         callbacks and to clear requests when the calling app is stopping.
+     * @param audioUri URI for the audio resource (a file or android.resource URI)
+     * @param queueMode Determines what to do to requests already in the queue.
+     * @param param Request parameters.
+     */
+    int playAudio(in String callingApp, in Uri audioUri, in int queueMode, in Bundle params);
+
+    /**
+     * Plays silence.
+     *
+     * @param callingApp The package name of the calling app. Used to connect requests
+     *         callbacks and to clear requests when the calling app is stopping.
+     * @param duration Number of milliseconds of silence to play.
+     * @param queueMode Determines what to do to requests already in the queue.
+     * @param param Request parameters.
+     */
+    int playSilence(in String callingApp, in long duration, in int queueMode, in Bundle params);
+
+    /**
+     * Checks whether the service is currently playing some audio.
+     */
+    boolean isSpeaking();
+
+    /**
+     * Interrupts the current utterance (if from the given app) and removes any utterances
+     * in the queue that are from the given app.
+     *
+     * @param callingApp Package name of the app whose utterances
+     *        should be interrupted and cleared.
+     */
+    int stop(in String callingApp);
+
+    /**
+     * Returns the language, country and variant currently being used by the TTS engine.
+     *
+     * Can be called from multiple threads.
+     *
+     * @return A 3-element array, containing language (ISO 3-letter code),
+     *         country (ISO 3-letter code) and variant used by the engine.
+     *         The country and variant may be {@code ""}. If country is empty, then variant must
+     *         be empty too.
+     */
+    String[] getLanguage();
+
+    /**
+     * Checks whether the engine supports a given language.
+     *
+     * @param lang ISO-3 language code.
+     * @param country ISO-3 country code. May be empty or null.
+     * @param variant Language variant. May be empty or null.
+     * @return Code indicating the support status for the locale.
+     *         One of {@link TextToSpeech#LANG_AVAILABLE},
+     *         {@link TextToSpeech#LANG_COUNTRY_AVAILABLE},
+     *         {@link TextToSpeech#LANG_COUNTRY_VAR_AVAILABLE},
+     *         {@link TextToSpeech#LANG_MISSING_DATA}
+     *         {@link TextToSpeech#LANG_NOT_SUPPORTED}.
+     */
+    int isLanguageAvailable(in String lang, in String country, in String variant);
+
+    /**
+     * Notifies the engine that it should load a speech synthesis language.
+     *
+     * @param lang ISO-3 language code.
+     * @param country ISO-3 country code. May be empty or null.
+     * @param variant Language variant. May be empty or null.
+     * @return Code indicating the support status for the locale.
+     *         One of {@link TextToSpeech#LANG_AVAILABLE},
+     *         {@link TextToSpeech#LANG_COUNTRY_AVAILABLE},
+     *         {@link TextToSpeech#LANG_COUNTRY_VAR_AVAILABLE},
+     *         {@link TextToSpeech#LANG_MISSING_DATA}
+     *         {@link TextToSpeech#LANG_NOT_SUPPORTED}.
+     */
+    int loadLanguage(in String lang, in String country, in String variant);
+
+    /**
+     * Sets the callback that will be notified when playback of utterance from the
+     * given app are completed.
+     *
+     * @param callingApp Package name for the app whose utterance the callback will handle.
+     * @param cb The callback.
+     */
+    void setCallback(in String callingApp, ITextToSpeechCallback cb);
+
+}
diff --git a/core/java/android/speech/tts/ITts.aidl b/core/java/android/speech/tts/ITts.aidl
deleted file mode 100755
index c1051c4..0000000
--- a/core/java/android/speech/tts/ITts.aidl
+++ /dev/null
@@ -1,69 +0,0 @@
-/*

- * Copyright (C) 2009 The Android Open Source Project

- *

- * Licensed under the Apache License, Version 2.0 (the "License");

- * you may not use this file except in compliance with the License.

- * You may obtain a copy of the License at

- *

- *      http://www.apache.org/licenses/LICENSE-2.0

- *

- * Unless required by applicable law or agreed to in writing, software

- * distributed under the License is distributed on an "AS IS" BASIS,

- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

- * See the License for the specific language governing permissions and

- * limitations under the License.

- */

-

-package android.speech.tts;

-

-import android.speech.tts.ITtsCallback;

-

-import android.content.Intent;

-

-/**

- * AIDL for the TTS Service

- * ITts.java is autogenerated from this.

- *

- * {@hide}

- */

-interface ITts {

-    int setSpeechRate(in String callingApp, in int speechRate);

-

-    int setPitch(in String callingApp, in int pitch);

-

-    int speak(in String callingApp, in String text, in int queueMode, in String[] params);

-

-    boolean isSpeaking();

-

-    int stop(in String callingApp);

-

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

-

-    void addSpeechFile(in String callingApp, in String text, in String filename);

-

-    String[] getLanguage();

-

-    int isLanguageAvailable(in String language, in String country, in String variant, in String[] params);

-

-    int setLanguage(in String callingApp, in String language, in String country, in String variant);

-

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

-

-    int playEarcon(in String callingApp, in String earcon, in int queueMode, in String[] params);

-

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

-

-    void addEarconFile(in String callingApp, in String earcon, in String filename);

-

-    int registerCallback(in String callingApp, ITtsCallback cb);

-

-    int unregisterCallback(in String callingApp, ITtsCallback cb);

-

-    int playSilence(in String callingApp, in long duration, in int queueMode, in String[] params);
-

-    int setEngineByPackageName(in String enginePackageName);
-
-    String getDefaultEngine();
-
-    boolean areDefaultsEnforced();

-}

diff --git a/core/java/android/speech/tts/PlaybackSynthesisRequest.java b/core/java/android/speech/tts/PlaybackSynthesisRequest.java
new file mode 100644
index 0000000..2267015
--- /dev/null
+++ b/core/java/android/speech/tts/PlaybackSynthesisRequest.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.speech.tts;
+
+import android.media.AudioFormat;
+import android.media.AudioTrack;
+import android.util.Log;
+
+/**
+ * Speech synthesis request that plays the audio as it is received.
+ */
+class PlaybackSynthesisRequest extends SynthesisRequest {
+
+    private static final String TAG = "PlaybackSynthesisRequest";
+    private static final boolean DBG = false;
+
+    private static final int MIN_AUDIO_BUFFER_SIZE = 8192;
+
+    /**
+     * Audio stream type. Must be one of the STREAM_ contants defined in
+     * {@link android.media.AudioManager}.
+     */
+    private final int mStreamType;
+
+    /**
+     * Volume, in the range [0.0f, 1.0f]. The default value is
+     * {@link TextToSpeech.Engine#DEFAULT_VOLUME} (1.0f).
+     */
+    private final float mVolume;
+
+    /**
+     * Left/right position of the audio, in the range [-1.0f, 1.0f].
+     * The default value is {@link TextToSpeech.Engine#DEFAULT_PAN} (0.0f).
+     */
+    private final float mPan;
+
+    private final Object mStateLock = new Object();
+    private AudioTrack mAudioTrack = null;
+    private boolean mStopped = false;
+
+    PlaybackSynthesisRequest(String text, int streamType, float volume, float pan) {
+        super(text);
+        mStreamType = streamType;
+        mVolume = volume;
+        mPan = pan;
+    }
+
+    @Override
+    void stop() {
+        if (DBG) Log.d(TAG, "stop()");
+        synchronized (mStateLock) {
+            mStopped = true;
+            cleanUp();
+        }
+    }
+
+    private void cleanUp() {
+        if (DBG) Log.d(TAG, "cleanUp()");
+        if (mAudioTrack != null) {
+            mAudioTrack.flush();
+            mAudioTrack.stop();
+            // TODO: do we need to wait for playback to finish before releasing?
+            mAudioTrack.release();
+            mAudioTrack = null;
+        }
+    }
+
+    @Override
+    public int getMaxBufferSize() {
+        // The AudioTrack buffer will be at least MIN_AUDIO_BUFFER_SIZE, so that should always be
+        // a safe buffer size to pass in.
+        return MIN_AUDIO_BUFFER_SIZE;
+    }
+
+    // TODO: add a thread that writes to the AudioTrack?
+    @Override
+    public int start(int sampleRateInHz, int audioFormat, int channelCount) {
+        if (DBG) {
+            Log.d(TAG, "start(" + sampleRateInHz + "," + audioFormat
+                    + "," + channelCount + ")");
+        }
+
+        synchronized (mStateLock) {
+            if (mStopped) {
+                if (DBG) Log.d(TAG, "Request has been aborted.");
+                return TextToSpeech.ERROR;
+            }
+            if (mAudioTrack != null) {
+                Log.e(TAG, "start() called twice");
+                cleanUp();
+                return TextToSpeech.ERROR;
+            }
+
+            mAudioTrack = createAudioTrack(sampleRateInHz, audioFormat, channelCount,
+                    AudioTrack.MODE_STREAM);
+            if (mAudioTrack == null) {
+                return TextToSpeech.ERROR;
+            }
+        }
+
+        return TextToSpeech.SUCCESS;
+    }
+
+    private void setupVolume(AudioTrack audioTrack, float volume, float pan) {
+        float vol = clip(volume, 0.0f, 1.0f);
+        float panning = clip(pan, -1.0f, 1.0f);
+        float volLeft = vol;
+        float volRight = vol;
+        if (panning > 0.0f) {
+            volLeft *= (1.0f - panning);
+        } else if (panning < 0.0f) {
+            volRight *= (1.0f + panning);
+        }
+        if (DBG) Log.d(TAG, "volLeft=" + volLeft + ",volRight=" + volRight);
+        if (audioTrack.setStereoVolume(volLeft, volRight) != AudioTrack.SUCCESS) {
+            Log.e(TAG, "Failed to set volume");
+        }
+    }
+
+    private float clip(float value, float min, float max) {
+        return value > max ? max : (value < min ? min : value);
+    }
+
+    @Override
+    public int audioAvailable(byte[] buffer, int offset, int length) {
+        if (DBG) {
+            Log.d(TAG, "audioAvailable(byte[" + buffer.length + "],"
+                    + offset + "," + length + ")");
+        }
+        if (length > getMaxBufferSize()) {
+            throw new IllegalArgumentException("buffer is too large (" + length + " bytes)");
+        }
+        synchronized (mStateLock) {
+            if (mStopped) {
+                if (DBG) Log.d(TAG, "Request has been aborted.");
+                return TextToSpeech.ERROR;
+            }
+            if (mAudioTrack == null) {
+                Log.e(TAG, "audioAvailable(): Not started");
+                return TextToSpeech.ERROR;
+            }
+            int playState = mAudioTrack.getPlayState();
+            if (playState == AudioTrack.PLAYSTATE_STOPPED) {
+                if (DBG) Log.d(TAG, "AudioTrack stopped, restarting");
+                mAudioTrack.play();
+            }
+            // TODO: loop until all data is written?
+            if (DBG) Log.d(TAG, "AudioTrack.write()");
+            int count = mAudioTrack.write(buffer, offset, length);
+            if (DBG) Log.d(TAG, "AudioTrack.write() returned " + count);
+            if (count < 0) {
+                Log.e(TAG, "Writing to AudioTrack failed: " + count);
+                cleanUp();
+                return TextToSpeech.ERROR;
+            } else {
+                return TextToSpeech.SUCCESS;
+            }
+        }
+    }
+
+    @Override
+    public int done() {
+        if (DBG) Log.d(TAG, "done()");
+        synchronized (mStateLock) {
+            if (mStopped) {
+                if (DBG) Log.d(TAG, "Request has been aborted.");
+                return TextToSpeech.ERROR;
+            }
+            if (mAudioTrack == null) {
+                Log.e(TAG, "done(): Not started");
+                return TextToSpeech.ERROR;
+            }
+            cleanUp();
+        }
+        return TextToSpeech.SUCCESS;
+    }
+
+    @Override
+    public int completeAudioAvailable(int sampleRateInHz, int audioFormat, int channelCount,
+            byte[] buffer, int offset, int length) {
+        if (DBG) {
+            Log.d(TAG, "completeAudioAvailable(" + sampleRateInHz + "," + audioFormat
+                    + "," + channelCount + "byte[" + buffer.length + "],"
+                    + offset + "," + length + ")");
+        }
+
+        synchronized (mStateLock) {
+            if (mStopped) {
+                if (DBG) Log.d(TAG, "Request has been aborted.");
+                return TextToSpeech.ERROR;
+            }
+            if (mAudioTrack != null) {
+                Log.e(TAG, "start() called before completeAudioAvailable()");
+                cleanUp();
+                return TextToSpeech.ERROR;
+            }
+
+            mAudioTrack = createAudioTrack(sampleRateInHz, audioFormat, channelCount,
+                    AudioTrack.MODE_STATIC);
+            if (mAudioTrack == null) {
+                return TextToSpeech.ERROR;
+            }
+
+            try {
+                mAudioTrack.write(buffer, offset, length);
+                mAudioTrack.play();
+            } catch (IllegalStateException ex) {
+                Log.e(TAG, "Playback error", ex);
+                return TextToSpeech.ERROR;
+            } finally {
+                cleanUp();
+            }
+        }
+
+        return TextToSpeech.SUCCESS;
+    }
+
+    private AudioTrack createAudioTrack(int sampleRateInHz, int audioFormat, int channelCount,
+            int mode) {
+        int channelConfig;
+        if (channelCount == 1) {
+            channelConfig = AudioFormat.CHANNEL_OUT_MONO;
+        } else if (channelCount == 2){
+            channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
+        } else {
+            Log.e(TAG, "Unsupported number of channels: " + channelCount);
+            return null;
+        }
+
+        int minBufferSizeInBytes
+                = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
+        int bufferSizeInBytes = Math.max(MIN_AUDIO_BUFFER_SIZE, minBufferSizeInBytes);
+        AudioTrack audioTrack = new AudioTrack(mStreamType, sampleRateInHz, channelConfig,
+                audioFormat, bufferSizeInBytes, mode);
+        if (audioTrack == null) {
+            return null;
+        }
+        if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
+            audioTrack.release();
+            return null;
+        }
+        setupVolume(audioTrack, mVolume, mPan);
+        return audioTrack;
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/speech/tts/SynthesisRequest.java b/core/java/android/speech/tts/SynthesisRequest.java
new file mode 100644
index 0000000..f4bb852
--- /dev/null
+++ b/core/java/android/speech/tts/SynthesisRequest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.speech.tts;
+
+/**
+ * A request for speech synthesis given to a TTS engine for processing.
+ *
+ * The engine can provide streaming audio by calling
+ * {@link #start}, then {@link #audioAvailable} until all audio has been provided, then finally
+ * {@link #done}.
+ *
+ * Alternatively, the engine can provide all the audio at once, by using
+ * {@link #completeAudioAvailable}.
+ *
+ * @hide Pending approval
+ */
+public abstract class SynthesisRequest {
+
+    private final String mText;
+    private String mLanguage;
+    private String mCountry;
+    private String mVariant;
+    private int mSpeechRate;
+    private int mPitch;
+
+    public SynthesisRequest(String text) {
+        mText = text;
+    }
+
+    /**
+     * Sets the locale for the request.
+     */
+    void setLanguage(String language, String country, String variant) {
+        mLanguage = language;
+        mCountry = country;
+        mVariant = variant;
+    }
+
+    /**
+     * Sets the speech rate.
+     */
+    void setSpeechRate(int speechRate) {
+        mSpeechRate = speechRate;
+    }
+
+    /**
+     * Sets the pitch.
+     */
+    void setPitch(int pitch) {
+        mPitch = pitch;
+    }
+
+    /**
+     * Gets the text which should be synthesized.
+     */
+    public String getText() {
+        return mText;
+    }
+
+    /**
+     * Gets the ISO 3-letter language code for the language to use.
+     */
+    public String getLanguage() {
+        return mLanguage;
+    }
+
+    /**
+     * Gets the ISO 3-letter country code for the language to use.
+     */
+    public String getCountry() {
+        return mCountry;
+    }
+
+    /**
+     * Gets the language variant to use.
+     */
+    public String getVariant() {
+        return mVariant;
+    }
+
+    /**
+     * Gets the speech rate to use. {@link TextToSpeech.Engine#DEFAULT_RATE} (100)
+     * is the normal rate.
+     */
+    public int getSpeechRate() {
+        return mSpeechRate;
+    }
+
+    /**
+     * Gets the pitch to use. {@link TextToSpeech.Engine#DEFAULT_PITCH} (100)
+     * is the normal pitch.
+     */
+    public int getPitch() {
+        return mPitch;
+    }
+
+    /**
+     * Gets the maximum number of bytes that the TTS engine can pass in a single call of
+     * {@link #audioAvailable}. This does not apply to {@link #completeAudioAvailable}.
+     */
+    public abstract int getMaxBufferSize();
+
+    /**
+     * Aborts the speech request.
+     *
+     * Can be called from multiple threads.
+     */
+    abstract void stop();
+
+    /**
+     * The service should call this when it starts to synthesize audio for this
+     * request.
+     *
+     * This method should only be called on the synthesis thread,
+     * while in {@link TextToSpeechService#onSynthesizeText}.
+     *
+     * @param sampleRateInHz Sample rate in HZ of the generated audio.
+     * @param audioFormat Audio format of the generated audio. Must be one of
+     *         the ENCODING_ constants defined in {@link android.media.AudioFormat}.
+     * @param channelCount The number of channels. Must be {@code 1} or {@code 2}.
+     * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+     */
+    public abstract int start(int sampleRateInHz, int audioFormat, int channelCount);
+
+    /**
+     * The service should call this method when synthesized audio is ready for consumption.
+     *
+     * This method should only be called on the synthesis thread,
+     * while in {@link TextToSpeechService#onSynthesizeText}.
+     *
+     * @param buffer The generated audio data. This method will not hold on to {@code buffer},
+     *         so the caller is free to modify it after this method returns.
+     * @param offset The offset into {@code buffer} where the audio data starts.
+     * @param length The number of bytes of audio data in {@code buffer}. This must be
+     *         less than or equal to the return value of {@link #getMaxBufferSize}.
+     * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+     */
+    public abstract int audioAvailable(byte[] buffer, int offset, int length);
+
+    /**
+     * The service should call this method when all the synthesized audio for a request has
+     * been passed to {@link #audioAvailable}.
+     *
+     * This method should only be called on the synthesis thread,
+     * while in {@link TextToSpeechService#onSynthesizeText}.
+     *
+     * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+     */
+    public abstract int done();
+
+    /**
+     * The service can call this method instead of using {@link #start}, {@link #audioAvailable}
+     * and {@link #done} if all the audio data is available in a single buffer.
+     *
+     * @param sampleRateInHz Sample rate in HZ of the generated audio.
+     * @param audioFormat Audio format of the generated audio. Must be one of
+     *         the ENCODING_ constants defined in {@link android.media.AudioFormat}.
+     * @param channelCount The number of channels. Must be {@code 1} or {@code 2}.
+     * @param buffer The generated audio data. This method will not hold on to {@code buffer},
+     *         so the caller is free to modify it after this method returns.
+     * @param offset The offset into {@code buffer} where the audio data starts.
+     * @param length The number of bytes of audio data in {@code buffer}.
+     * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+     */
+    public abstract int completeAudioAvailable(int sampleRateInHz, int audioFormat,
+            int channelCount, byte[] buffer, int offset, int length);
+}
\ No newline at end of file
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 95830ec..e247df8 100755
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 Google Inc.
+ * 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
@@ -15,22 +15,31 @@
  */
 package android.speech.tts;
 
-import android.speech.tts.ITts;
-import android.speech.tts.ITtsCallback;
-
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
 import android.media.AudioManager;
+import android.net.Uri;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.Log;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 
 /**
  *
@@ -49,11 +58,11 @@
     /**
      * Denotes a successful operation.
      */
-    public static final int SUCCESS                = 0;
+    public static final int SUCCESS = 0;
     /**
      * Denotes a generic operation failure.
      */
-    public static final int ERROR                  = -1;
+    public static final int ERROR = -1;
 
     /**
      * Queue mode where all entries in the playback queue (media to be played
@@ -65,20 +74,17 @@
      */
     public static final int QUEUE_ADD = 1;
 
-
     /**
      * Denotes the language is available exactly as specified by the locale.
      */
     public static final int LANG_COUNTRY_VAR_AVAILABLE = 2;
 
-
     /**
      * Denotes the language is available for the language and country specified 
      * by the locale, but not the variant.
      */
     public static final int LANG_COUNTRY_AVAILABLE = 1;
 
-
     /**
      * Denotes the language is available for the language by the locale, 
      * but not the country and variant.
@@ -95,7 +101,6 @@
      */
     public static final int LANG_NOT_SUPPORTED = -2;
 
-
     /**
      * Broadcast Action: The TextToSpeech synthesizer has completed processing
      * of all the text in the speech queue.
@@ -104,7 +109,6 @@
     public static final String ACTION_TTS_QUEUE_PROCESSING_COMPLETED =
             "android.speech.tts.TTS_QUEUE_PROCESSING_COMPLETED";
 
-
     /**
      * Interface definition of a callback to be invoked indicating the completion of the
      * TextToSpeech engine initialization.
@@ -112,103 +116,117 @@
     public interface OnInitListener {
         /**
          * Called to signal the completion of the TextToSpeech engine initialization.
+         *
          * @param status {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
          */
         public void onInit(int status);
     }
 
     /**
-     * Interface definition of a callback to be invoked indicating the TextToSpeech engine has
-     * completed synthesizing an utterance with an utterance ID set.
-     *
+     * Listener that will be called when the TTS service has
+     * completed synthesizing an utterance. This is only called if the utterance
+     * has an utterance ID (see {@link TextToSpeech.Engine#KEY_PARAM_UTTERANCE_ID}).
      */
     public interface OnUtteranceCompletedListener {
         /**
-         * Called to signal the completion of the synthesis of the utterance that was identified
-         * with the string parameter. This identifier is the one originally passed in the
-         * parameter hashmap of the synthesis request in
-         * {@link TextToSpeech#speak(String, int, HashMap)} or
-         * {@link TextToSpeech#synthesizeToFile(String, HashMap, String)} with the
-         * {@link TextToSpeech.Engine#KEY_PARAM_UTTERANCE_ID} key.
+         * Called when an utterance has been synthesized.
+         *
          * @param utteranceId the identifier of the utterance.
          */
         public void onUtteranceCompleted(String utteranceId);
     }
 
-
     /**
-     * Internal constants for the TextToSpeech functionality
-     *
+     * Constants and parameter names for controlling text-to-speech.
      */
     public class Engine {
-        // default values for a TTS engine when settings are not found in the provider
+
         /**
-         * {@hide}
+         * Default speech rate.
+         * @hide
          */
-        public static final int DEFAULT_RATE = 100; // 1x
+        public static final int DEFAULT_RATE = 100;
+
         /**
-         * {@hide}
+         * Default pitch.
+         * @hide
          */
-        public static final int DEFAULT_PITCH = 100;// 1x
+        public static final int DEFAULT_PITCH = 100;
+
         /**
-         * {@hide}
+         * Default volume.
+         * @hide
          */
         public static final float DEFAULT_VOLUME = 1.0f;
+
         /**
-         * {@hide}
-         */
-        protected static final String DEFAULT_VOLUME_STRING = "1.0";
-        /**
-         * {@hide}
+         * Default pan (centered).
+         * @hide
          */
         public static final float DEFAULT_PAN = 0.0f;
-        /**
-         * {@hide}
-         */
-        protected static final String DEFAULT_PAN_STRING = "0.0";
 
         /**
-         * {@hide}
+         * Default value for {@link Settings.Secure#TTS_USE_DEFAULTS}.
+         * @hide
          */
         public static final int USE_DEFAULTS = 0; // false
-        /**
-         * {@hide}
-         */
-        public static final String DEFAULT_SYNTH = "com.svox.pico";
 
-        // default values for rendering
+        /**
+         * Package name of the default TTS engine.
+         *
+         * TODO: This should come from a system property
+         *
+         * @hide
+         */
+        public static final String DEFAULT_ENGINE = "com.svox.pico";
+
         /**
          * Default audio stream used when playing synthesized speech.
          */
         public static final int DEFAULT_STREAM = AudioManager.STREAM_MUSIC;
 
-        // return codes for a TTS engine's check data activity
         /**
          * Indicates success when checking the installation status of the resources used by the
          * TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
          */
         public static final int CHECK_VOICE_DATA_PASS = 1;
+
         /**
          * Indicates failure when checking the installation status of the resources used by the
          * TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
          */
         public static final int CHECK_VOICE_DATA_FAIL = 0;
+
         /**
          * Indicates erroneous data when checking the installation status of the resources used by
          * the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
          */
         public static final int CHECK_VOICE_DATA_BAD_DATA = -1;
+
         /**
          * Indicates missing resources when checking the installation status of the resources used
          * by the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
          */
         public static final int CHECK_VOICE_DATA_MISSING_DATA = -2;
+
         /**
          * Indicates missing storage volume when checking the installation status of the resources
          * used by the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
          */
         public static final int CHECK_VOICE_DATA_MISSING_VOLUME = -3;
 
+        /**
+         * Intent for starting a TTS service. Services that handle this intent must
+         * extend {@link TextToSpeechService}. Normal applications should not use this intent
+         * directly, instead they should talk to the TTS service using the the methods in this
+         * class.
+         *
+         * @hide Pending API council approval
+         */
+        @SdkConstant(SdkConstantType.SERVICE_ACTION)
+        public static final String INTENT_ACTION_TTS_SERVICE =
+                "android.intent.action.TTS_SERVICE";
+
         // intents to ask engine to install data or check its data
         /**
          * Activity Action: Triggers the platform TextToSpeech engine to
@@ -231,6 +249,7 @@
         @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
         public static final String ACTION_TTS_DATA_INSTALLED =
                 "android.speech.tts.engine.TTS_DATA_INSTALLED";
+
         /**
          * Activity Action: Starts the activity from the platform TextToSpeech
          * engine to verify the proper installation and availability of the
@@ -258,23 +277,36 @@
         public static final String ACTION_CHECK_TTS_DATA =
                 "android.speech.tts.engine.CHECK_TTS_DATA";
 
+        /**
+         * Activity intent for getting some sample text to use for demonstrating TTS.
+         *
+         * @hide This intent was used by engines written against the old API.
+         * Not sure if it should be exposed.
+         */
+        @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+        public static final String ACTION_GET_SAMPLE_TEXT =
+                "android.speech.tts.engine.GET_SAMPLE_TEXT";
+
         // extras for a TTS engine's check data activity
         /**
          * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
          * the TextToSpeech engine specifies the path to its resources.
          */
         public static final String EXTRA_VOICE_DATA_ROOT_DIRECTORY = "dataRoot";
+
         /**
          * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
          * the TextToSpeech engine specifies the file names of its resources under the
          * resource path.
          */
         public static final String EXTRA_VOICE_DATA_FILES = "dataFiles";
+
         /**
          * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
          * the TextToSpeech engine specifies the locale associated with each resource file.
          */
         public static final String EXTRA_VOICE_DATA_FILES_INFO = "dataFilesInfo";
+
         /**
          * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
          * the TextToSpeech engine returns an ArrayList<String> of all the available voices.
@@ -282,6 +314,7 @@
          * optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE").
          */
         public static final String EXTRA_AVAILABLE_VOICES = "availableVoices";
+
         /**
          * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
          * the TextToSpeech engine returns an ArrayList<String> of all the unavailable voices.
@@ -289,6 +322,7 @@
          * optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE").
          */
         public static final String EXTRA_UNAVAILABLE_VOICES = "unavailableVoices";
+
         /**
          * Extra information sent with the {@link #ACTION_CHECK_TTS_DATA} intent where the
          * caller indicates to the TextToSpeech engine which specific sets of voice data to
@@ -311,134 +345,87 @@
         // keys for the parameters passed with speak commands. Hidden keys are used internally
         // to maintain engine state for each TextToSpeech instance.
         /**
-         * {@hide}
+         * @hide
          */
         public static final String KEY_PARAM_RATE = "rate";
+
         /**
-         * {@hide}
+         * @hide
          */
         public static final String KEY_PARAM_LANGUAGE = "language";
+
         /**
-         * {@hide}
+         * @hide
          */
         public static final String KEY_PARAM_COUNTRY = "country";
+
         /**
-         * {@hide}
+         * @hide
          */
         public static final String KEY_PARAM_VARIANT = "variant";
+
         /**
-         * {@hide}
+         * @hide
          */
         public static final String KEY_PARAM_ENGINE = "engine";
+
         /**
-         * {@hide}
+         * @hide
          */
         public static final String KEY_PARAM_PITCH = "pitch";
+
         /**
          * Parameter key to specify the audio stream type to be used when speaking text
-         * or playing back a file.
+         * or playing back a file. The value should be one of the STREAM_ constants
+         * defined in {@link AudioManager}.
+         *
          * @see TextToSpeech#speak(String, int, HashMap)
          * @see TextToSpeech#playEarcon(String, int, HashMap)
          */
         public static final String KEY_PARAM_STREAM = "streamType";
+
         /**
          * Parameter key to identify an utterance in the
          * {@link TextToSpeech.OnUtteranceCompletedListener} after text has been
          * spoken, a file has been played back or a silence duration has elapsed.
+         *
          * @see TextToSpeech#speak(String, int, HashMap)
          * @see TextToSpeech#playEarcon(String, int, HashMap)
          * @see TextToSpeech#synthesizeToFile(String, HashMap, String)
          */
         public static final String KEY_PARAM_UTTERANCE_ID = "utteranceId";
+
         /**
          * Parameter key to specify the speech volume relative to the current stream type
          * volume used when speaking text. Volume is specified as a float ranging from 0 to 1
          * where 0 is silence, and 1 is the maximum volume (the default behavior).
+         *
          * @see TextToSpeech#speak(String, int, HashMap)
          * @see TextToSpeech#playEarcon(String, int, HashMap)
          */
         public static final String KEY_PARAM_VOLUME = "volume";
+
         /**
          * Parameter key to specify how the speech is panned from left to right when speaking text.
          * Pan is specified as a float ranging from -1 to +1 where -1 maps to a hard-left pan,
          * 0 to center (the default behavior), and +1 to hard-right.
+         *
          * @see TextToSpeech#speak(String, int, HashMap)
          * @see TextToSpeech#playEarcon(String, int, HashMap)
          */
         public static final String KEY_PARAM_PAN = "pan";
 
-        // key positions in the array of cached parameters
-        /**
-         * {@hide}
-         */
-        protected static final int PARAM_POSITION_RATE = 0;
-        /**
-         * {@hide}
-         */
-        protected static final int PARAM_POSITION_LANGUAGE = 2;
-        /**
-         * {@hide}
-         */
-        protected static final int PARAM_POSITION_COUNTRY = 4;
-        /**
-         * {@hide}
-         */
-        protected static final int PARAM_POSITION_VARIANT = 6;
-        /**
-         * {@hide}
-         */
-        protected static final int PARAM_POSITION_STREAM = 8;
-        /**
-         * {@hide}
-         */
-        protected static final int PARAM_POSITION_UTTERANCE_ID = 10;
-
-        /**
-         * {@hide}
-         */
-        protected static final int PARAM_POSITION_ENGINE = 12;
-
-        /**
-         * {@hide}
-         */
-        protected static final int PARAM_POSITION_PITCH = 14;
-
-        /**
-         * {@hide}
-         */
-        protected static final int PARAM_POSITION_VOLUME = 16;
-
-        /**
-         * {@hide}
-         */
-        protected static final int PARAM_POSITION_PAN = 18;
-
-
-        /**
-         * {@hide}
-         * Total number of cached speech parameters.
-         * This number should be equal to (max param position/2) + 1.
-         */
-        protected static final int NB_CACHED_PARAMS = 10;
     }
 
-    /**
-     * Connection needed for the TTS.
-     */
-    private ServiceConnection mServiceConnection;
-
-    private ITts mITts = null;
-    private ITtsCallback mITtscallback = null;
-    private Context mContext = null;
-    private String mPackageName = "";
-    private OnInitListener mInitListener = null;
-    private boolean mStarted = false;
+    private final Context mContext;
+    private Connection mServiceConnection;
+    private OnInitListener mInitListener;
     private final Object mStartLock = new Object();
-    /**
-     * Used to store the cached parameters sent along with each synthesis request to the
-     * TTS service.
-     */
-    private String[] mCachedParams;
+
+    private String mRequestedEngine;
+    private final Map<String, Uri> mEarcons;
+    private final Map<String, Uri> mUtterances;
+    private final Bundle mParams = new Bundle();
 
     /**
      * The constructor for the TextToSpeech class.
@@ -451,84 +438,98 @@
      *            TextToSpeech engine has initialized.
      */
     public TextToSpeech(Context context, OnInitListener listener) {
+        this(context, listener, null);
+    }
+
+    /**
+     * @hide pending approval
+     */
+    public TextToSpeech(Context context, OnInitListener listener, String engine) {
         mContext = context;
-        mPackageName = mContext.getPackageName();
         mInitListener = listener;
+        mRequestedEngine = engine;
 
-        mCachedParams = new String[2*Engine.NB_CACHED_PARAMS]; // store key and value
-        mCachedParams[Engine.PARAM_POSITION_RATE] = Engine.KEY_PARAM_RATE;
-        mCachedParams[Engine.PARAM_POSITION_LANGUAGE] = Engine.KEY_PARAM_LANGUAGE;
-        mCachedParams[Engine.PARAM_POSITION_COUNTRY] = Engine.KEY_PARAM_COUNTRY;
-        mCachedParams[Engine.PARAM_POSITION_VARIANT] = Engine.KEY_PARAM_VARIANT;
-        mCachedParams[Engine.PARAM_POSITION_STREAM] = Engine.KEY_PARAM_STREAM;
-        mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID] = Engine.KEY_PARAM_UTTERANCE_ID;
-        mCachedParams[Engine.PARAM_POSITION_ENGINE] = Engine.KEY_PARAM_ENGINE;
-        mCachedParams[Engine.PARAM_POSITION_PITCH] = Engine.KEY_PARAM_PITCH;
-        mCachedParams[Engine.PARAM_POSITION_VOLUME] = Engine.KEY_PARAM_VOLUME;
-        mCachedParams[Engine.PARAM_POSITION_PAN] = Engine.KEY_PARAM_PAN;
-
-        // Leave all defaults that are shown in Settings uninitialized/at the default
-        // so that the values set in Settings will take effect if the application does
-        // not try to change these settings itself.
-        mCachedParams[Engine.PARAM_POSITION_RATE + 1] = "";
-        mCachedParams[Engine.PARAM_POSITION_LANGUAGE + 1] = "";
-        mCachedParams[Engine.PARAM_POSITION_COUNTRY + 1] = "";
-        mCachedParams[Engine.PARAM_POSITION_VARIANT + 1] = "";
-        mCachedParams[Engine.PARAM_POSITION_STREAM + 1] =
-                String.valueOf(Engine.DEFAULT_STREAM);
-        mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID + 1] = "";
-        mCachedParams[Engine.PARAM_POSITION_ENGINE + 1] = "";
-        mCachedParams[Engine.PARAM_POSITION_PITCH + 1] = "100";
-        mCachedParams[Engine.PARAM_POSITION_VOLUME + 1] = Engine.DEFAULT_VOLUME_STRING;
-        mCachedParams[Engine.PARAM_POSITION_PAN + 1] = Engine.DEFAULT_PAN_STRING;
+        mEarcons = new HashMap<String, Uri>();
+        mUtterances = new HashMap<String, Uri>();
 
         initTts();
     }
 
-
-    private void initTts() {
-        mStarted = false;
-
-        // Initialize the TTS, run the callback after the binding is successful
-        mServiceConnection = new ServiceConnection() {
-            public void onServiceConnected(ComponentName name, IBinder service) {
-                synchronized(mStartLock) {
-                    mITts = ITts.Stub.asInterface(service);
-                    mStarted = true;
-                    // Cache the default engine and current language
-                    setEngineByPackageName(getDefaultEngine());
-                    setLanguage(getLanguage());
-                    if (mInitListener != null) {
-                        // TODO manage failures and missing resources
-                        mInitListener.onInit(SUCCESS);
-                    }
-                }
-            }
-
-            public void onServiceDisconnected(ComponentName name) {
-                synchronized(mStartLock) {
-                    mITts = null;
-                    mInitListener = null;
-                    mStarted = false;
-                }
-            }
-        };
-
-        Intent intent = new Intent("android.intent.action.START_TTS_SERVICE");
-        intent.addCategory("android.intent.category.TTS");
-        boolean bound = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
-        if (!bound) {
-            Log.e("TextToSpeech.java", "initTts() failed to bind to service");
-            if (mInitListener != null) {
-                mInitListener.onInit(ERROR);
-            }
-        } else {
-            // initialization listener will be called inside ServiceConnection
-            Log.i("TextToSpeech.java", "initTts() successfully bound to service");
-        }
-        // TODO handle plugin failures
+    private String getPackageName() {
+        return mContext.getPackageName();
     }
 
+    private <R> R runActionNoReconnect(Action<R> action, R errorResult, String method) {
+        return runAction(action, errorResult, method, false);
+    }
+
+    private <R> R runAction(Action<R> action, R errorResult, String method) {
+        return runAction(action, errorResult, method, true);
+    }
+
+    private <R> R runAction(Action<R> action, R errorResult, String method, boolean reconnect) {
+        synchronized (mStartLock) {
+            if (mServiceConnection == null) {
+                Log.w(TAG, method + " failed: not bound to TTS engine");
+                return errorResult;
+            }
+            return mServiceConnection.runAction(action, errorResult, method, reconnect);
+        }
+    }
+
+    private int initTts() {
+        String defaultEngine = getDefaultEngine();
+        String engine = defaultEngine;
+        if (!areDefaultsEnforced() && !TextUtils.isEmpty(mRequestedEngine)
+                && isEngineEnabled(engine)) {
+            engine = mRequestedEngine;
+        }
+
+        // Try requested engine
+        if (connectToEngine(engine)) {
+            return SUCCESS;
+        }
+
+        // Fall back to user's default engine if different from the already tested one
+        if (!engine.equals(defaultEngine)) {
+            if (connectToEngine(defaultEngine)) {
+                return SUCCESS;
+            }
+        }
+
+        // Fall back to the hardcoded default if different from the two above
+        if (!defaultEngine.equals(Engine.DEFAULT_ENGINE)
+                && !engine.equals(Engine.DEFAULT_ENGINE)) {
+            if (connectToEngine(Engine.DEFAULT_ENGINE)) {
+                return SUCCESS;
+            }
+        }
+
+        return ERROR;
+    }
+
+    private boolean connectToEngine(String engine) {
+        Connection connection = new Connection();
+        Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE);
+        intent.setPackage(engine);
+        boolean bound = mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE);
+        if (!bound) {
+            Log.e(TAG, "Failed to bind to " + engine);
+            dispatchOnInit(ERROR);
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    private void dispatchOnInit(int result) {
+        synchronized (mStartLock) {
+            if (mInitListener != null) {
+                mInitListener.onInit(result);
+                mInitListener = null;
+            }
+        }
+    }
 
     /**
      * Releases the resources used by the TextToSpeech engine.
@@ -536,15 +537,17 @@
      * so the TextToSpeech engine can be cleanly stopped.
      */
     public void shutdown() {
-        try {
-            mContext.unbindService(mServiceConnection);
-        } catch (IllegalArgumentException e) {
-            // Do nothing and fail silently since an error here indicates that
-            // binding never succeeded in the first place.
-        }
+        runActionNoReconnect(new Action<Void>() {
+            @Override
+            public Void run(ITextToSpeechService service) throws RemoteException {
+                service.setCallback(getPackageName(), null);
+                service.stop(getPackageName());
+                mServiceConnection.disconnect();
+                return null;
+            }
+        }, null, "shutdown");
     }
 
-
     /**
      * Adds a mapping between a string of text and a sound resource in a
      * package. After a call to this method, subsequent calls to
@@ -573,21 +576,9 @@
      * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
      */
     public int addSpeech(String text, String packagename, int resourceId) {
-        synchronized(mStartLock) {
-            if (!mStarted) {
-                return ERROR;
-            }
-            try {
-                mITts.addSpeech(mPackageName, text, packagename, resourceId);
-                return SUCCESS;
-            } catch (RemoteException e) {
-                restart("addSpeech", e);
-            } catch (NullPointerException e) {
-                restart("addSpeech", e);
-            } catch (IllegalStateException e) {
-                restart("addSpeech", e);
-            }
-            return ERROR;
+        synchronized (mStartLock) {
+            mUtterances.put(text, makeResourceUri(packagename, resourceId));
+            return SUCCESS;
         }
     }
 
@@ -608,20 +599,8 @@
      */
     public int addSpeech(String text, String filename) {
         synchronized (mStartLock) {
-            if (!mStarted) {
-                return ERROR;
-            }
-            try {
-                mITts.addSpeechFile(mPackageName, text, filename);
-                return SUCCESS;
-            } catch (RemoteException e) {
-                restart("addSpeech", e);
-            } catch (NullPointerException e) {
-                restart("addSpeech", e);
-            } catch (IllegalStateException e) {
-                restart("addSpeech", e);
-            }
-            return ERROR;
+            mUtterances.put(text, Uri.parse(filename));
+            return SUCCESS;
         }
     }
 
@@ -653,24 +632,11 @@
      */
     public int addEarcon(String earcon, String packagename, int resourceId) {
         synchronized(mStartLock) {
-            if (!mStarted) {
-                return ERROR;
-            }
-            try {
-                mITts.addEarcon(mPackageName, earcon, packagename, resourceId);
-                return SUCCESS;
-            } catch (RemoteException e) {
-                restart("addEarcon", e);
-            } catch (NullPointerException e) {
-                restart("addEarcon", e);
-            } catch (IllegalStateException e) {
-                restart("addEarcon", e);
-            }
-            return ERROR;
+            mEarcons.put(earcon, makeResourceUri(packagename, resourceId));
+            return SUCCESS;
         }
     }
 
-
     /**
      * Adds a mapping between a string of text and a sound file.
      * Use this to add custom earcons.
@@ -687,312 +653,199 @@
      * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
      */
     public int addEarcon(String earcon, String filename) {
-        synchronized (mStartLock) {
-            if (!mStarted) {
-                return ERROR;
-            }
-            try {
-                mITts.addEarconFile(mPackageName, earcon, filename);
-                return SUCCESS;
-            } catch (RemoteException e) {
-                restart("addEarcon", e);
-            } catch (NullPointerException e) {
-                restart("addEarcon", e);
-            } catch (IllegalStateException e) {
-                restart("addEarcon", e);
-            }
-            return ERROR;
+        synchronized(mStartLock) {
+            mEarcons.put(earcon, Uri.parse(filename));
+            return SUCCESS;
         }
     }
 
+    private Uri makeResourceUri(String packageName, int resourceId) {
+        return new Uri.Builder()
+                .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
+                .encodedAuthority(packageName)
+                .appendEncodedPath(String.valueOf(resourceId))
+                .build();
+    }
 
     /**
      * Speaks the string using the specified queuing strategy and speech
      * parameters.
      *
-     * @param text
-     *            The string of text to be spoken.
-     * @param queueMode
-     *            The queuing strategy to use.
-     *            {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
-     * @param params
-     *            The list of parameters to be used. Can be null if no parameters are given.
-     *            They are specified using a (key, value) pair, where the key can be
-     *            {@link Engine#KEY_PARAM_STREAM} or
-     *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
+     * @param text The string of text to be spoken.
+     * @param queueMode The queuing strategy to use, {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
+     * @param params Parameters for the request. Can be null.
+     *            Supported parameter names:
+     *            {@link Engine#KEY_PARAM_STREAM},
+     *            {@link Engine#KEY_PARAM_UTTERANCE_ID},
+     *            {@link Engine#KEY_PARAM_VOLUME},
+     *            {@link Engine#KEY_PARAM_PAN}.
      *
-     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+     * @return {@link #ERROR} or {@link #SUCCESS}.
      */
-    public int speak(String text, int queueMode, HashMap<String,String> params)
-    {
-        synchronized (mStartLock) {
-            int result = ERROR;
-            Log.i("TextToSpeech.java - speak", "speak text of length " + text.length());
-            if (!mStarted) {
-                Log.e("TextToSpeech.java - speak", "service isn't started");
-                return result;
-            }
-            try {
-                if ((params != null) && (!params.isEmpty())) {
-                    setCachedParam(params, Engine.KEY_PARAM_STREAM, Engine.PARAM_POSITION_STREAM);
-                    setCachedParam(params, Engine.KEY_PARAM_UTTERANCE_ID,
-                            Engine.PARAM_POSITION_UTTERANCE_ID);
-                    setCachedParam(params, Engine.KEY_PARAM_ENGINE, Engine.PARAM_POSITION_ENGINE);
-                    setCachedParam(params, Engine.KEY_PARAM_VOLUME, Engine.PARAM_POSITION_VOLUME);
-                    setCachedParam(params, Engine.KEY_PARAM_PAN, Engine.PARAM_POSITION_PAN);
+    public int speak(final String text, final int queueMode, final HashMap<String, String> params) {
+        return runAction(new Action<Integer>() {
+            @Override
+            public Integer run(ITextToSpeechService service) throws RemoteException {
+                Uri utteranceUri = mUtterances.get(text);
+                if (utteranceUri != null) {
+                    return service.playAudio(getPackageName(), utteranceUri, queueMode,
+                            getParams(params));
+                } else {
+                    return service.speak(getPackageName(), text, queueMode, getParams(params));
                 }
-                result = mITts.speak(mPackageName, text, queueMode, mCachedParams);
-            } catch (RemoteException e) {
-                restart("speak", e);
-            } catch (NullPointerException e) {
-                restart("speak", e);
-            } catch (IllegalStateException e) {
-                restart("speak", e);
-            } finally {
-                resetCachedParams();
             }
-            return result;
-        }
+        }, ERROR, "speak");
     }
 
-
     /**
      * Plays the earcon using the specified queueing mode and parameters.
+     * The earcon must already have been added with {@link #addEarcon(String, String)} or
+     * {@link #addEarcon(String, String, int)}.
      *
-     * @param earcon
-     *            The earcon that should be played
-     * @param queueMode
-     *            {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
-     * @param params
-     *            The list of parameters to be used. Can be null if no parameters are given.
-     *            They are specified using a (key, value) pair, where the key can be
-     *            {@link Engine#KEY_PARAM_STREAM} or
+     * @param earcon The earcon that should be played
+     * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
+     * @param params Parameters for the request. Can be null.
+     *            Supported parameter names:
+     *            {@link Engine#KEY_PARAM_STREAM},
      *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
      *
-     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+     * @return {@link #ERROR} or {@link #SUCCESS}.
      */
-    public int playEarcon(String earcon, int queueMode,
-            HashMap<String,String> params) {
-        synchronized (mStartLock) {
-            int result = ERROR;
-            if (!mStarted) {
-                return result;
-            }
-            try {
-                if ((params != null) && (!params.isEmpty())) {
-                    String extra = params.get(Engine.KEY_PARAM_STREAM);
-                    if (extra != null) {
-                        mCachedParams[Engine.PARAM_POSITION_STREAM + 1] = extra;
-                    }
-                    setCachedParam(params, Engine.KEY_PARAM_STREAM, Engine.PARAM_POSITION_STREAM);
-                    setCachedParam(params, Engine.KEY_PARAM_UTTERANCE_ID,
-                            Engine.PARAM_POSITION_UTTERANCE_ID);
+    public int playEarcon(final String earcon, final int queueMode,
+            final HashMap<String, String> params) {
+        return runAction(new Action<Integer>() {
+            @Override
+            public Integer run(ITextToSpeechService service) throws RemoteException {
+                Uri earconUri = mEarcons.get(earcon);
+                if (earconUri == null) {
+                    return ERROR;
                 }
-                result = mITts.playEarcon(mPackageName, earcon, queueMode, null);
-            } catch (RemoteException e) {
-                restart("playEarcon", e);
-            } catch (NullPointerException e) {
-                restart("playEarcon", e);
-            } catch (IllegalStateException e) {
-                restart("playEarcon", e);
-            } finally {
-                resetCachedParams();
+                return service.playAudio(getPackageName(), earconUri, queueMode,
+                        getParams(params));
             }
-            return result;
-        }
+        }, ERROR, "playEarcon");
     }
 
     /**
      * Plays silence for the specified amount of time using the specified
      * queue mode.
      *
-     * @param durationInMs
-     *            A long that indicates how long the silence should last.
-     * @param queueMode
-     *            {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
-     * @param params
-     *            The list of parameters to be used. Can be null if no parameters are given.
-     *            They are specified using a (key, value) pair, where the key can be
+     * @param durationInMs The duration of the silence.
+     * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
+     * @param params Parameters for the request. Can be null.
+     *            Supported parameter names:
      *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
      *
-     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+     * @return {@link #ERROR} or {@link #SUCCESS}.
      */
-    public int playSilence(long durationInMs, int queueMode, HashMap<String,String> params) {
-        synchronized (mStartLock) {
-            int result = ERROR;
-            if (!mStarted) {
-                return result;
+    public int playSilence(final long durationInMs, final int queueMode,
+            final HashMap<String, String> params) {
+        return runAction(new Action<Integer>() {
+            @Override
+            public Integer run(ITextToSpeechService service) throws RemoteException {
+                return service.playSilence(getPackageName(), durationInMs, queueMode,
+                        getParams(params));
             }
-            try {
-                if ((params != null) && (!params.isEmpty())) {
-                    setCachedParam(params, Engine.KEY_PARAM_UTTERANCE_ID,
-                            Engine.PARAM_POSITION_UTTERANCE_ID);
-                }
-                result = mITts.playSilence(mPackageName, durationInMs, queueMode, mCachedParams);
-            } catch (RemoteException e) {
-                restart("playSilence", e);
-            } catch (NullPointerException e) {
-                restart("playSilence", e);
-            } catch (IllegalStateException e) {
-                restart("playSilence", e);
-            } finally {
-                resetCachedParams();
-            }
-            return result;
-        }
+        }, ERROR, "playSilence");
     }
 
-
     /**
-     * Returns whether or not the TextToSpeech engine is busy speaking.
+     * Checks whether the TTS engine is busy speaking.
      *
-     * @return Whether or not the TextToSpeech engine is busy speaking.
+     * @return {@code true} if the TTS engine is speaking.
      */
     public boolean isSpeaking() {
-        synchronized (mStartLock) {
-            if (!mStarted) {
-                return false;
+        return runAction(new Action<Boolean>() {
+            @Override
+            public Boolean run(ITextToSpeechService service) throws RemoteException {
+                return service.isSpeaking();
             }
-            try {
-                return mITts.isSpeaking();
-            } catch (RemoteException e) {
-                restart("isSpeaking", e);
-            } catch (NullPointerException e) {
-                restart("isSpeaking", e);
-            } catch (IllegalStateException e) {
-                restart("isSpeaking", e);
-            }
-            return false;
-        }
+        }, false, "isSpeaking");
     }
 
-
     /**
      * Interrupts the current utterance (whether played or rendered to file) and discards other
      * utterances in the queue.
      *
-     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+     * @return {@link #ERROR} or {@link #SUCCESS}.
      */
     public int stop() {
-        synchronized (mStartLock) {
-            int result = ERROR;
-            if (!mStarted) {
-                return result;
+        return runAction(new Action<Integer>() {
+            @Override
+            public Integer run(ITextToSpeechService service) throws RemoteException {
+                return service.stop(getPackageName());
             }
-            try {
-                result = mITts.stop(mPackageName);
-            } catch (RemoteException e) {
-                restart("stop", e);
-            } catch (NullPointerException e) {
-                restart("stop", e);
-            } catch (IllegalStateException e) {
-                restart("stop", e);
-            }
-            return result;
-        }
+        }, ERROR, "stop");
     }
 
-
     /**
-     * Sets the speech rate for the TextToSpeech engine.
+     * Sets the speech rate.
      *
      * This has no effect on any pre-recorded speech.
      *
-     * @param speechRate
-     *            The speech rate for the TextToSpeech engine. 1 is the normal speed,
-     *            lower values slow down the speech (0.5 is half the normal speech rate),
-     *            greater values accelerate it (2 is twice the normal speech rate).
+     * @param speechRate Speech rate. {@code 1.0} is the normal speech rate,
+     *            lower values slow down the speech ({@code 0.5} is half the normal speech rate),
+     *            greater values accelerate it ({@code 2.0} is twice the normal speech rate).
      *
-     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+     * @return {@link #ERROR} or {@link #SUCCESS}.
      */
     public int setSpeechRate(float speechRate) {
-        synchronized (mStartLock) {
-            int result = ERROR;
-            if (!mStarted) {
-                return result;
-            }
-            try {
-                if (speechRate > 0) {
-                    int rate = (int)(speechRate*100);
-                    mCachedParams[Engine.PARAM_POSITION_RATE + 1] = String.valueOf(rate);
-                    // the rate is not set here, instead it is cached so it will be associated
-                    // with all upcoming utterances.
-                    if (speechRate > 0.0f) {
-                        result = SUCCESS;
-                    } else {
-                        result = ERROR;
-                    }
+        if (speechRate > 0.0f) {
+            int intRate = (int)(speechRate * 100);
+            if (intRate > 0) {
+                synchronized (mStartLock) {
+                    mParams.putInt(Engine.KEY_PARAM_RATE, intRate);
                 }
-            } catch (NullPointerException e) {
-                restart("setSpeechRate", e);
-            } catch (IllegalStateException e) {
-                restart("setSpeechRate", e);
+                return SUCCESS;
             }
-            return result;
         }
+        return ERROR;
     }
 
-
     /**
      * Sets the speech pitch for the TextToSpeech engine.
      *
      * This has no effect on any pre-recorded speech.
      *
-     * @param pitch
-     *            The pitch for the TextToSpeech engine. 1 is the normal pitch,
+     * @param pitch Speech pitch. {@code 1.0} is the normal pitch,
      *            lower values lower the tone of the synthesized voice,
      *            greater values increase it.
      *
-     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+     * @return {@link #ERROR} or {@link #SUCCESS}.
      */
     public int setPitch(float pitch) {
-        synchronized (mStartLock) {
-            int result = ERROR;
-            if (!mStarted) {
-                return result;
-            }
-            try {
-                // the pitch is not set here, instead it is cached so it will be associated
-                // with all upcoming utterances.
-                if (pitch > 0) {
-                    int p = (int)(pitch*100);
-                    mCachedParams[Engine.PARAM_POSITION_PITCH + 1] = String.valueOf(p);
-                    result = SUCCESS;
+        if (pitch > 0.0f) {
+            int intPitch = (int)(pitch * 100);
+            if (intPitch > 0) {
+                synchronized (mStartLock) {
+                    mParams.putInt(Engine.KEY_PARAM_PITCH, intPitch);
                 }
-            } catch (NullPointerException e) {
-                restart("setPitch", e);
-            } catch (IllegalStateException e) {
-                restart("setPitch", e);
+                return SUCCESS;
             }
-            return result;
         }
+        return ERROR;
     }
 
-
     /**
-     * Sets the language for the TextToSpeech engine.
-     * The TextToSpeech engine will try to use the closest match to the specified
+     * Sets the text-to-speech language.
+     * The TTS engine will try to use the closest match to the specified
      * language as represented by the Locale, but there is no guarantee that the exact same Locale
      * will be used. Use {@link #isLanguageAvailable(Locale)} to check the level of support
      * before choosing the language to use for the next utterances.
      *
-     * @param loc
-     *            The locale describing the language to be used.
+     * @param loc The locale describing the language to be used.
      *
-     * @return code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
+     * @return Code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
      *         {@link #LANG_COUNTRY_AVAILABLE}, {@link #LANG_COUNTRY_VAR_AVAILABLE},
      *         {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}.
      */
-    public int setLanguage(Locale loc) {
-        synchronized (mStartLock) {
-            int result = LANG_NOT_SUPPORTED;
-            if (!mStarted) {
-                return result;
-            }
-            if (loc == null) {
-                return result;
-            }
-            try {
+    public int setLanguage(final Locale loc) {
+        return runAction(new Action<Integer>() {
+            @Override
+            public Integer run(ITextToSpeechService service) throws RemoteException {
+                if (loc == null) {
+                    return LANG_NOT_SUPPORTED;
+                }
                 String language = loc.getISO3Language();
                 String country = loc.getISO3Country();
                 String variant = loc.getVariant();
@@ -1000,294 +853,317 @@
                 // the available parts.
                 // Note that the language is not actually set here, instead it is cached so it
                 // will be associated with all upcoming utterances.
-                result = mITts.isLanguageAvailable(language, country, variant, mCachedParams);
+                int result = service.loadLanguage(language, country, variant);
                 if (result >= LANG_AVAILABLE){
-                    mCachedParams[Engine.PARAM_POSITION_LANGUAGE + 1] = language;
-                    if (result >= LANG_COUNTRY_AVAILABLE){
-                        mCachedParams[Engine.PARAM_POSITION_COUNTRY + 1] = country;
-                    } else {
-                        mCachedParams[Engine.PARAM_POSITION_COUNTRY + 1] = "";
+                    if (result < LANG_COUNTRY_VAR_AVAILABLE) {
+                        variant = "";
+                        if (result < LANG_COUNTRY_AVAILABLE) {
+                            country = "";
+                        }
                     }
-                    if (result >= LANG_COUNTRY_VAR_AVAILABLE){
-                        mCachedParams[Engine.PARAM_POSITION_VARIANT + 1] = variant;
-                    } else {
-                        mCachedParams[Engine.PARAM_POSITION_VARIANT + 1] = "";
-                    }
+                    mParams.putString(Engine.KEY_PARAM_LANGUAGE, language);
+                    mParams.putString(Engine.KEY_PARAM_COUNTRY, country);
+                    mParams.putString(Engine.KEY_PARAM_VARIANT, variant);
                 }
-            } catch (RemoteException e) {
-                restart("setLanguage", e);
-            } catch (NullPointerException e) {
-                restart("setLanguage", e);
-            } catch (IllegalStateException e) {
-                restart("setLanguage", e);
+                return result;
             }
-            return result;
-        }
+        }, LANG_NOT_SUPPORTED, "setLanguage");
     }
 
-
     /**
      * Returns a Locale instance describing the language currently being used by the TextToSpeech
      * engine.
+     *
      * @return language, country (if any) and variant (if any) used by the engine stored in a Locale
-     *     instance, or null is the TextToSpeech engine has failed.
+     *     instance, or {@code null} on error.
      */
     public Locale getLanguage() {
-        synchronized (mStartLock) {
-            if (!mStarted) {
+        return runAction(new Action<Locale>() {
+            @Override
+            public Locale run(ITextToSpeechService service) throws RemoteException {
+                String[] locStrings = service.getLanguage();
+                if (locStrings != null && locStrings.length == 3) {
+                    return new Locale(locStrings[0], locStrings[1], locStrings[2]);
+                }
                 return null;
             }
-            try {
-                // Only do a call to the native synth if there is nothing in the cached params
-                if (mCachedParams[Engine.PARAM_POSITION_LANGUAGE + 1].length() < 1){
-                    String[] locStrings = mITts.getLanguage();
-                    if ((locStrings != null) && (locStrings.length == 3)) {
-                        return new Locale(locStrings[0], locStrings[1], locStrings[2]);
-                    } else {
-                        return null;
-                    }
-                } else {
-                    return new Locale(mCachedParams[Engine.PARAM_POSITION_LANGUAGE + 1],
-                            mCachedParams[Engine.PARAM_POSITION_COUNTRY + 1],
-                            mCachedParams[Engine.PARAM_POSITION_VARIANT + 1]);
-                }
-            } catch (RemoteException e) {
-                restart("getLanguage", e);
-            } catch (NullPointerException e) {
-                restart("getLanguage", e);
-            } catch (IllegalStateException e) {
-                restart("getLanguage", e);
-            }
-            return null;
-        }
+        }, null, "getLanguage");
     }
 
     /**
      * Checks if the specified language as represented by the Locale is available and supported.
      *
-     * @param loc
-     *            The Locale describing the language to be used.
+     * @param loc The Locale describing the language to be used.
      *
-     * @return code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
+     * @return Code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
      *         {@link #LANG_COUNTRY_AVAILABLE}, {@link #LANG_COUNTRY_VAR_AVAILABLE},
      *         {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}.
      */
-    public int isLanguageAvailable(Locale loc) {
-        synchronized (mStartLock) {
-            int result = LANG_NOT_SUPPORTED;
-            if (!mStarted) {
-                return result;
+    public int isLanguageAvailable(final Locale loc) {
+        return runAction(new Action<Integer>() {
+            @Override
+            public Integer run(ITextToSpeechService service) throws RemoteException {
+                return service.isLanguageAvailable(loc.getISO3Language(),
+                        loc.getISO3Country(), loc.getVariant());
             }
-            try {
-                result = mITts.isLanguageAvailable(loc.getISO3Language(),
-                        loc.getISO3Country(), loc.getVariant(), mCachedParams);
-            } catch (RemoteException e) {
-                restart("isLanguageAvailable", e);
-            } catch (NullPointerException e) {
-                restart("isLanguageAvailable", e);
-            } catch (IllegalStateException e) {
-                restart("isLanguageAvailable", e);
-            }
-            return result;
-        }
+        }, LANG_NOT_SUPPORTED, "isLanguageAvailable");
     }
 
-
     /**
      * Synthesizes the given text to a file using the specified parameters.
      *
-     * @param text
-     *            The String of text that should be synthesized
-     * @param params
-     *            The list of parameters to be used. Can be null if no parameters are given.
-     *            They are specified using a (key, value) pair, where the key can be
+     * @param text Thetext that should be synthesized
+     * @param params Parameters for the request. Can be null.
+     *            Supported parameter names:
      *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
-     * @param filename
-     *            The string that gives the full output filename; it should be
+     * @param filename Absolute file filename to write the generated audio data to.It should be
      *            something like "/sdcard/myappsounds/mysound.wav".
      *
-     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+     * @return {@link #ERROR} or {@link #SUCCESS}.
      */
-    public int synthesizeToFile(String text, HashMap<String,String> params,
-            String filename) {
-        Log.i("TextToSpeech.java", "synthesizeToFile()");
-        synchronized (mStartLock) {
-            int result = ERROR;
-            Log.i("TextToSpeech.java - synthesizeToFile", "synthesizeToFile text of length "
-                    + text.length());
-            if (!mStarted) {
-                Log.e("TextToSpeech.java - synthesizeToFile", "service isn't started");
-                return result;
+    public int synthesizeToFile(final String text, final HashMap<String, String> params,
+            final String filename) {
+        return runAction(new Action<Integer>() {
+            @Override
+            public Integer run(ITextToSpeechService service) throws RemoteException {
+                return service.synthesizeToFile(getPackageName(), text, filename,
+                        getParams(params));
             }
+        }, ERROR, "synthesizeToFile");
+    }
+
+    private Bundle getParams(HashMap<String, String> params) {
+        if (params != null && !params.isEmpty()) {
+            Bundle bundle = new Bundle(mParams);
+            copyIntParam(bundle, params, Engine.KEY_PARAM_STREAM);
+            copyStringParam(bundle, params, Engine.KEY_PARAM_UTTERANCE_ID);
+            copyFloatParam(bundle, params, Engine.KEY_PARAM_VOLUME);
+            copyFloatParam(bundle, params, Engine.KEY_PARAM_PAN);
+            return bundle;
+        } else {
+            return mParams;
+        }
+    }
+
+    private void copyStringParam(Bundle bundle, HashMap<String, String> params, String key) {
+        String value = params.get(key);
+        if (value != null) {
+            bundle.putString(key, value);
+        }
+    }
+
+    private void copyIntParam(Bundle bundle, HashMap<String, String> params, String key) {
+        String valueString = params.get(key);
+        if (!TextUtils.isEmpty(valueString)) {
             try {
-                if ((params != null) && (!params.isEmpty())) {
-                    // no need to read the stream type here
-                    setCachedParam(params, Engine.KEY_PARAM_UTTERANCE_ID,
-                            Engine.PARAM_POSITION_UTTERANCE_ID);
-                    setCachedParam(params, Engine.KEY_PARAM_ENGINE, Engine.PARAM_POSITION_ENGINE);
-                }
-                result = mITts.synthesizeToFile(mPackageName, text, mCachedParams, filename) ?
-                        SUCCESS : ERROR;
-            } catch (RemoteException e) {
-                restart("synthesizeToFile", e);
-            } catch (NullPointerException e) {
-                restart("synthesizeToFile", e);
-            } catch (IllegalStateException e) {
-                restart("synthesizeToFile", e);
-            } finally {
-                resetCachedParams();
+                int value = Integer.parseInt(valueString);
+                bundle.putInt(key, value);
+            } catch (NumberFormatException ex) {
+                // don't set the value in the bundle
             }
-            return result;
         }
     }
 
-
-    /**
-     * Convenience method to reset the cached parameters to the current default values
-     * if they are not persistent between calls to the service.
-     */
-    private void resetCachedParams() {
-        mCachedParams[Engine.PARAM_POSITION_STREAM + 1] =
-                String.valueOf(Engine.DEFAULT_STREAM);
-        mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID+ 1] = "";
-        mCachedParams[Engine.PARAM_POSITION_VOLUME + 1] = Engine.DEFAULT_VOLUME_STRING;
-        mCachedParams[Engine.PARAM_POSITION_PAN + 1] = Engine.DEFAULT_PAN_STRING;
-    }
-
-    /**
-     * Convenience method to save a parameter in the cached parameter array, at the given index,
-     * for a property saved in the given hashmap.
-     */
-    private void setCachedParam(HashMap<String,String> params, String key, int keyIndex) {
-        String extra = params.get(key);
-        if (extra != null) {
-            mCachedParams[keyIndex+1] = extra;
+    private void copyFloatParam(Bundle bundle, HashMap<String, String> params, String key) {
+        String valueString = params.get(key);
+        if (!TextUtils.isEmpty(valueString)) {
+            try {
+                float value = Float.parseFloat(valueString);
+                bundle.putFloat(key, value);
+            } catch (NumberFormatException ex) {
+                // don't set the value in the bundle
+            }
         }
     }
 
     /**
-     * Sets the OnUtteranceCompletedListener that will fire when an utterance completes.
+     * Sets the listener that will be notified when synthesis of an utterance completes.
      *
-     * @param listener
-     *            The OnUtteranceCompletedListener
+     * @param listener The listener to use.
      *
-     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+     * @return {@link #ERROR} or {@link #SUCCESS}.
      */
-    public int setOnUtteranceCompletedListener(
-            final OnUtteranceCompletedListener listener) {
-        synchronized (mStartLock) {
-            int result = ERROR;
-            if (!mStarted) {
-                return result;
-            }
-            mITtscallback = new ITtsCallback.Stub() {
-                public void utteranceCompleted(String utteranceId) throws RemoteException {
-                    if (listener != null) {
-                        listener.onUtteranceCompleted(utteranceId);
+    public int setOnUtteranceCompletedListener(final OnUtteranceCompletedListener listener) {
+        return runAction(new Action<Integer>() {
+            @Override
+            public Integer run(ITextToSpeechService service) throws RemoteException {
+                ITextToSpeechCallback.Stub callback = new ITextToSpeechCallback.Stub() {
+                    public void utteranceCompleted(String utteranceId) {
+                        if (listener != null) {
+                            listener.onUtteranceCompleted(utteranceId);
+                        }
                     }
-                }
-            };
-            try {
-                result = mITts.registerCallback(mPackageName, mITtscallback);
-            } catch (RemoteException e) {
-                restart("registerCallback", e);
-            } catch (NullPointerException e) {
-                restart("registerCallback", e);
-            } catch (IllegalStateException e) {
-                restart("registerCallback", e);
+                };
+                service.setCallback(getPackageName(), callback);
+                return SUCCESS;
             }
-            return result;
-        }
+        }, ERROR, "setOnUtteranceCompletedListener");
     }
 
     /**
-     * Sets the speech synthesis engine to be used by its packagename.
+     * Sets the TTS engine to use.
      *
-     * @param enginePackageName
-     *            The packagename for the synthesis engine (ie, "com.svox.pico")
+     * @param enginePackageName The package name for the synthesis engine (e.g. "com.svox.pico")
      *
-     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+     * @return {@link #ERROR} or {@link #SUCCESS}.
      */
+    // TODO: add @Deprecated{This method does not tell the caller when the new engine
+    // has been initialized. You should create a new TextToSpeech object with the new
+    // engine instead.}
     public int setEngineByPackageName(String enginePackageName) {
-        synchronized (mStartLock) {
-            int result = TextToSpeech.ERROR;
-            if (!mStarted) {
-                return result;
-            }
-            try {
-                result = mITts.setEngineByPackageName(enginePackageName);
-                if (result == TextToSpeech.SUCCESS){
-                    mCachedParams[Engine.PARAM_POSITION_ENGINE + 1] = enginePackageName;
-                }
-            } catch (RemoteException e) {
-                restart("setEngineByPackageName", e);
-            } catch (NullPointerException e) {
-                restart("setEngineByPackageName", e);
-            } catch (IllegalStateException e) {
-                restart("setEngineByPackageName", e);
-            }
-            return result;
-        }
+        mRequestedEngine = enginePackageName;
+        return initTts();
     }
 
-
     /**
-     * Gets the packagename of the default speech synthesis engine.
+     * Gets the package name of the default speech synthesis engine.
      *
-     * @return Packagename of the TTS engine that the user has chosen as their default.
+     * @return Package name of the TTS engine that the user has chosen as their default.
      */
     public String getDefaultEngine() {
-        synchronized (mStartLock) {
-            String engineName = "";
-            if (!mStarted) {
-                return engineName;
-            }
-            try {
-                engineName = mITts.getDefaultEngine();
-            } catch (RemoteException e) {
-                restart("getDefaultEngine", e);
-            } catch (NullPointerException e) {
-                restart("getDefaultEngine", e);
-            } catch (IllegalStateException e) {
-                restart("getDefaultEngine", e);
-            }
-            return engineName;
-        }
+        String engine = Settings.Secure.getString(mContext.getContentResolver(),
+                Settings.Secure.TTS_DEFAULT_SYNTH);
+        return engine != null ? engine : Engine.DEFAULT_ENGINE;
     }
 
-
     /**
-     * Returns whether or not the user is forcing their defaults to override the
-     * Text-To-Speech settings set by applications.
-     *
-     * @return Whether or not defaults are enforced.
+     * Checks whether the user's settings should override settings requested by the calling
+     * application.
      */
     public boolean areDefaultsEnforced() {
-        synchronized (mStartLock) {
-            boolean defaultsEnforced = false;
-            if (!mStarted) {
-                return defaultsEnforced;
-            }
-            try {
-                defaultsEnforced = mITts.areDefaultsEnforced();
-            } catch (RemoteException e) {
-                restart("areDefaultsEnforced", e);
-            } catch (NullPointerException e) {
-                restart("areDefaultsEnforced", e);
-            } catch (IllegalStateException e) {
-                restart("areDefaultsEnforced", e);
-            }
-            return defaultsEnforced;
+        return Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.TTS_USE_DEFAULTS, Engine.USE_DEFAULTS) == 1;
+    }
+
+    private boolean isEngineEnabled(String engine) {
+        if (Engine.DEFAULT_ENGINE.equals(engine)) {
+            return true;
         }
+        for (String enabled : getEnabledEngines()) {
+            if (engine.equals(enabled)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private String[] getEnabledEngines() {
+        String str = Settings.Secure.getString(mContext.getContentResolver(),
+                Settings.Secure.TTS_ENABLED_PLUGINS);
+        if (TextUtils.isEmpty(str)) {
+            return new String[0];
+        }
+        return str.split(" ");
     }
 
     /**
-     * Restarts the TTS after a failure.
+     * Gets a list of all installed TTS engines.
+     *
+     * @return A list of engine info objects. The list can be empty, but will never by {@code null}.
+     *
+     * @hide Pending approval
      */
-    private void restart(String method, Exception e) {
-        // TTS died; restart it.
-        Log.e(TAG, method, e);
-        mStarted = false;
-        initTts();
+    public List<EngineInfo> getEngines() {
+        PackageManager pm = mContext.getPackageManager();
+        Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE);
+        List<ResolveInfo> resolveInfos =
+                pm.queryIntentServices(intent, PackageManager.MATCH_DEFAULT_ONLY);
+        if (resolveInfos == null) return Collections.emptyList();
+        List<EngineInfo> engines = new ArrayList<EngineInfo>(resolveInfos.size());
+        for (ResolveInfo resolveInfo : resolveInfos) {
+            ServiceInfo service = resolveInfo.serviceInfo;
+            if (service != null) {
+                EngineInfo engine = new EngineInfo();
+                // Using just the package name isn't great, since it disallows having
+                // multiple engines in the same package, but that's what the existing API does.
+                engine.name = service.packageName;
+                CharSequence label = service.loadLabel(pm);
+                engine.label = TextUtils.isEmpty(label) ? engine.name : label.toString();
+                engine.icon = service.getIconResource();
+                engines.add(engine);
+            }
+        }
+        return engines;
+    }
+
+    private class Connection implements ServiceConnection {
+        private ITextToSpeechService mService;
+
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            Log.i(TAG, "Connected to " + name);
+            synchronized(mStartLock) {
+                if (mServiceConnection != null) {
+                    // Disconnect any previous service connection
+                    mServiceConnection.disconnect();
+                }
+                mServiceConnection = this;
+                mService = ITextToSpeechService.Stub.asInterface(service);
+                dispatchOnInit(SUCCESS);
+            }
+        }
+
+        public void onServiceDisconnected(ComponentName name) {
+            synchronized(mStartLock) {
+                mService = null;
+                // If this is the active connection, clear it
+                if (mServiceConnection == this) {
+                    mServiceConnection = null;
+                }
+            }
+        }
+
+        public void disconnect() {
+            mContext.unbindService(this);
+        }
+
+        public <R> R runAction(Action<R> action, R errorResult, String method, boolean reconnect) {
+            try {
+                synchronized (mStartLock) {
+                    if (mService == null) {
+                        Log.w(TAG, method + " failed: not connected to TTS engine");
+                        return errorResult;
+                    }
+                    return action.run(mService);
+                }
+            } catch (RemoteException ex) {
+                Log.e(TAG, method + " failed", ex);
+                if (reconnect) {
+                    disconnect();
+                    initTts();
+                }
+                return errorResult;
+            }
+        }
+    }
+
+    private interface Action<R> {
+        R run(ITextToSpeechService service) throws RemoteException;
+    }
+
+    /**
+     * Information about an installed text-to-speech engine.
+     *
+     * @see TextToSpeech#getEngines
+     * @hide Pending approval
+     */
+    public static class EngineInfo {
+        /**
+         * Engine package name..
+         */
+        public String name;
+        /**
+         * Localized label for the engine.
+         */
+        public String label;
+        /**
+         * Icon for the engine.
+         */
+        public int icon;
+
+        @Override
+        public String toString() {
+            return "EngineInfo{name=" + name + "}";
+        }
+
     }
 }
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
new file mode 100644
index 0000000..a408ea2
--- /dev/null
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -0,0 +1,715 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.speech.tts;
+
+import android.app.Service;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.speech.tts.TextToSpeech.Engine;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Locale;
+
+
+/**
+ * Abstract base class for TTS engine implementations.
+ *
+ * @hide Pending approval
+ */
+public abstract class TextToSpeechService extends Service {
+
+    private static final boolean DBG = false;
+    private static final String TAG = "TextToSpeechService";
+
+    private static final int MAX_SPEECH_ITEM_CHAR_LENGTH = 4000;
+    private static final String SYNTH_THREAD_NAME = "SynthThread";
+
+    private SynthHandler mSynthHandler;
+
+    private CallbackMap mCallbacks;
+
+    @Override
+    public void onCreate() {
+        if (DBG) Log.d(TAG, "onCreate()");
+        super.onCreate();
+
+        SynthThread synthThread = new SynthThread();
+        synthThread.start();
+        mSynthHandler = new SynthHandler(synthThread.getLooper());
+
+        mCallbacks = new CallbackMap();
+
+        // Load default language
+        onLoadLanguage(getDefaultLanguage(), getDefaultCountry(), getDefaultVariant());
+    }
+
+    @Override
+    public void onDestroy() {
+        if (DBG) Log.d(TAG, "onDestroy()");
+
+        // Tell the synthesizer to stop
+        mSynthHandler.quit();
+
+        // Unregister all callbacks.
+        mCallbacks.kill();
+
+        super.onDestroy();
+    }
+
+    /**
+     * Checks whether the engine supports a given language.
+     *
+     * Can be called on multiple threads.
+     *
+     * @param lang ISO-3 language code.
+     * @param country ISO-3 country code. May be empty or null.
+     * @param variant Language variant. May be empty or null.
+     * @return Code indicating the support status for the locale.
+     *         One of {@link TextToSpeech#LANG_AVAILABLE},
+     *         {@link TextToSpeech#LANG_COUNTRY_AVAILABLE},
+     *         {@link TextToSpeech#LANG_COUNTRY_VAR_AVAILABLE},
+     *         {@link TextToSpeech#LANG_MISSING_DATA}
+     *         {@link TextToSpeech#LANG_NOT_SUPPORTED}.
+     */
+    protected abstract int onIsLanguageAvailable(String lang, String country, String variant);
+
+    /**
+     * Returns the language, country and variant currently being used by the TTS engine.
+     *
+     * Can be called on multiple threads.
+     *
+     * @return A 3-element array, containing language (ISO 3-letter code),
+     *         country (ISO 3-letter code) and variant used by the engine.
+     *         The country and variant may be {@code ""}. If country is empty, then variant must
+     *         be empty too.
+     * @see Locale#getISO3Language()
+     * @see Locale#getISO3Country()
+     * @see Locale#getVariant()
+     */
+    protected abstract String[] onGetLanguage();
+
+    /**
+     * Notifies the engine that it should load a speech synthesis language. There is no guarantee
+     * that this method is always called before the language is used for synthesis. It is merely
+     * a hint to the engine that it will probably get some synthesis requests for this language
+     * at some point in the future.
+     *
+     * Can be called on multiple threads.
+     *
+     * @param lang ISO-3 language code.
+     * @param country ISO-3 country code. May be empty or null.
+     * @param variant Language variant. May be empty or null.
+     * @return Code indicating the support status for the locale.
+     *         One of {@link TextToSpeech#LANG_AVAILABLE},
+     *         {@link TextToSpeech#LANG_COUNTRY_AVAILABLE},
+     *         {@link TextToSpeech#LANG_COUNTRY_VAR_AVAILABLE},
+     *         {@link TextToSpeech#LANG_MISSING_DATA}
+     *         {@link TextToSpeech#LANG_NOT_SUPPORTED}.
+     */
+    protected abstract int onLoadLanguage(String lang, String country, String variant);
+
+    /**
+     * Notifies the service that it should stop any in-progress speech synthesis.
+     * This method can be called even if no speech synthesis is currently in progress.
+     *
+     * Can be called on multiple threads, but not on the synthesis thread.
+     */
+    protected abstract void onStop();
+
+    /**
+     * Tells the service to synthesize speech from the given text. This method should
+     * block until the synthesis is finished.
+     *
+     * Called on the synthesis thread.
+     *
+     * @param request The synthesis request. The method should
+     *         call {@link SynthesisRequest#start}, {@link SynthesisRequest#audioAvailable},
+     *         and {@link SynthesisRequest#done} on this request.
+     * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+     */
+    protected abstract int onSynthesizeText(SynthesisRequest request);
+
+    private boolean areDefaultsEnforced() {
+        return getSecureSettingInt(Settings.Secure.TTS_USE_DEFAULTS,
+                TextToSpeech.Engine.USE_DEFAULTS) == 1;
+    }
+
+    private int getDefaultSpeechRate() {
+        return getSecureSettingInt(Settings.Secure.TTS_DEFAULT_RATE, Engine.DEFAULT_RATE);
+    }
+
+    private String getDefaultLanguage() {
+        return getSecureSettingString(Settings.Secure.TTS_DEFAULT_LANG,
+                Locale.getDefault().getISO3Language());
+    }
+
+    private String getDefaultCountry() {
+        return getSecureSettingString(Settings.Secure.TTS_DEFAULT_COUNTRY,
+                Locale.getDefault().getISO3Country());
+    }
+
+    private String getDefaultVariant() {
+        return getSecureSettingString(Settings.Secure.TTS_DEFAULT_VARIANT,
+                Locale.getDefault().getVariant());
+    }
+
+    private int getSecureSettingInt(String name, int defaultValue) {
+        return Settings.Secure.getInt(getContentResolver(), name, defaultValue);
+    }
+
+    private String getSecureSettingString(String name, String defaultValue) {
+        String value = Settings.Secure.getString(getContentResolver(), name);
+        return value != null ? value : defaultValue;
+    }
+
+    /**
+     * Synthesizer thread. This thread is used to run {@link SynthHandler}.
+     */
+    private class SynthThread extends HandlerThread implements MessageQueue.IdleHandler {
+
+        private boolean mFirstIdle = true;
+
+        public SynthThread() {
+            super(SYNTH_THREAD_NAME, android.os.Process.THREAD_PRIORITY_AUDIO);
+        }
+
+        @Override
+        protected void onLooperPrepared() {
+            getLooper().getQueue().addIdleHandler(this);
+        }
+
+        @Override
+        public boolean queueIdle() {
+            if (mFirstIdle) {
+                mFirstIdle = false;
+            } else {
+                broadcastTtsQueueProcessingCompleted();
+            }
+            return true;
+        }
+
+        private void broadcastTtsQueueProcessingCompleted() {
+            Intent i = new Intent(TextToSpeech.ACTION_TTS_QUEUE_PROCESSING_COMPLETED);
+            if (DBG) Log.d(TAG, "Broadcasting: " + i);
+            sendBroadcast(i);
+        }
+    }
+
+    private class SynthHandler extends Handler {
+
+        private SpeechItem mCurrentSpeechItem = null;
+
+        public SynthHandler(Looper looper) {
+            super(looper);
+        }
+
+        private void dispatchUtteranceCompleted(SpeechItem item) {
+            String utteranceId = item.getUtteranceId();
+            if (!TextUtils.isEmpty(utteranceId)) {
+                mCallbacks.dispatchUtteranceCompleted(item.getCallingApp(), utteranceId);
+            }
+        }
+
+        private synchronized SpeechItem getCurrentSpeechItem() {
+            return mCurrentSpeechItem;
+        }
+
+        private synchronized SpeechItem setCurrentSpeechItem(SpeechItem speechItem) {
+            SpeechItem old = mCurrentSpeechItem;
+            mCurrentSpeechItem = speechItem;
+            return old;
+        }
+
+        public boolean isSpeaking() {
+            return getCurrentSpeechItem() != null;
+        }
+
+        public void quit() {
+            // Don't process any more speech items
+            getLooper().quit();
+            // Stop the current speech item
+            SpeechItem current = setCurrentSpeechItem(null);
+            if (current != null) {
+                current.stop();
+            }
+        }
+
+        /**
+         * Adds a speech item to the queue.
+         *
+         * Called on a service binder thread.
+         */
+        public int enqueueSpeechItem(int queueMode, final SpeechItem speechItem) {
+            if (!speechItem.isValid()) {
+                return TextToSpeech.ERROR;
+            }
+            // TODO: The old code also supported the undocumented queueMode == 2,
+            // which clears out all pending items from the calling app, as well as all
+            // non-file items from other apps.
+            if (queueMode == TextToSpeech.QUEUE_FLUSH) {
+                stop(speechItem.getCallingApp());
+            }
+            Runnable runnable = new Runnable() {
+                @Override
+                public void run() {
+                    setCurrentSpeechItem(speechItem);
+                    if (speechItem.play() == TextToSpeech.SUCCESS) {
+                        dispatchUtteranceCompleted(speechItem);
+                    }
+                    setCurrentSpeechItem(null);
+                }
+            };
+            Message msg = Message.obtain(this, runnable);
+            // The obj is used to remove all callbacks from the given app in stop(String).
+            msg.obj = speechItem.getCallingApp();
+            if (sendMessage(msg)) {
+                return TextToSpeech.SUCCESS;
+            } else {
+                Log.w(TAG, "SynthThread has quit");
+                return TextToSpeech.ERROR;
+            }
+        }
+
+        /**
+         * Stops all speech output and removes any utterances still in the queue for
+         * the calling app.
+         *
+         * Called on a service binder thread.
+         */
+        public int stop(String callingApp) {
+            if (TextUtils.isEmpty(callingApp)) {
+                return TextToSpeech.ERROR;
+            }
+            removeCallbacksAndMessages(callingApp);
+            SpeechItem current = setCurrentSpeechItem(null);
+            if (current != null && TextUtils.equals(callingApp, current.getCallingApp())) {
+                current.stop();
+            }
+            return TextToSpeech.SUCCESS;
+        }
+    }
+
+    /**
+     * An item in the synth thread queue.
+     */
+    private static abstract class SpeechItem {
+        private final String mCallingApp;
+        private final Bundle mParams;
+        private boolean mStarted = false;
+        private boolean mStopped = false;
+
+        public SpeechItem(String callingApp, Bundle params) {
+            mCallingApp = callingApp;
+            mParams = params;
+        }
+
+        public String getCallingApp() {
+            return mCallingApp;
+        }
+
+        /**
+         * Checker whether the item is valid. If this method returns false, the item should not
+         * be played.
+         */
+        public abstract boolean isValid();
+
+        /**
+         * Plays the speech item. Blocks until playback is finished.
+         * Must not be called more than once.
+         *
+         * Only called on the synthesis thread.
+         *
+         * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+         */
+        public int play() {
+            synchronized (this) {
+                if (mStarted) {
+                    throw new IllegalStateException("play() called twice");
+                }
+                mStarted = true;
+            }
+            return playImpl();
+        }
+
+        /**
+         * Stops the speech item.
+         * Must not be called more than once.
+         *
+         * Can be called on multiple threads,  but not on the synthesis thread.
+         */
+        public void stop() {
+            synchronized (this) {
+                if (mStopped) {
+                    throw new IllegalStateException("stop() called twice");
+                }
+                mStopped = true;
+            }
+            stopImpl();
+        }
+
+        protected abstract int playImpl();
+
+        protected abstract void stopImpl();
+
+        public int getStreamType() {
+            return getIntParam(Engine.KEY_PARAM_STREAM, Engine.DEFAULT_STREAM);
+        }
+
+        public float getVolume() {
+            return getFloatParam(Engine.KEY_PARAM_VOLUME, Engine.DEFAULT_VOLUME);
+        }
+
+        public float getPan() {
+            return getFloatParam(Engine.KEY_PARAM_PAN, Engine.DEFAULT_PAN);
+        }
+
+        public String getUtteranceId() {
+            return getStringParam(Engine.KEY_PARAM_UTTERANCE_ID, null);
+        }
+
+        protected String getStringParam(String key, String defaultValue) {
+            return mParams == null ? defaultValue : mParams.getString(key, defaultValue);
+        }
+
+        protected int getIntParam(String key, int defaultValue) {
+            return mParams == null ? defaultValue : mParams.getInt(key, defaultValue);
+        }
+
+        protected float getFloatParam(String key, float defaultValue) {
+            return mParams == null ? defaultValue : mParams.getFloat(key, defaultValue);
+        }
+    }
+
+    private class SynthesisSpeechItem extends SpeechItem {
+        private final String mText;
+        private SynthesisRequest mSynthesisRequest;
+
+        public SynthesisSpeechItem(String callingApp, Bundle params, String text) {
+            super(callingApp, params);
+            mText = text;
+        }
+
+        public String getText() {
+            return mText;
+        }
+
+        @Override
+        public boolean isValid() {
+            if (TextUtils.isEmpty(mText)) {
+                Log.w(TAG, "Got empty text");
+                return false;
+            }
+            if (mText.length() >= MAX_SPEECH_ITEM_CHAR_LENGTH){
+                Log.w(TAG, "Text too long: " + mText.length() + " chars");
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        protected int playImpl() {
+            SynthesisRequest synthesisRequest;
+            synchronized (this) {
+                mSynthesisRequest = createSynthesisRequest();
+                synthesisRequest = mSynthesisRequest;
+            }
+            setRequestParams(synthesisRequest);
+            return TextToSpeechService.this.onSynthesizeText(synthesisRequest);
+        }
+
+        protected SynthesisRequest createSynthesisRequest() {
+            return new PlaybackSynthesisRequest(mText, getStreamType(), getVolume(), getPan());
+        }
+
+        private void setRequestParams(SynthesisRequest request) {
+            if (areDefaultsEnforced()) {
+                request.setLanguage(getDefaultLanguage(), getDefaultCountry(), getDefaultVariant());
+                request.setSpeechRate(getDefaultSpeechRate());
+            } else {
+                request.setLanguage(getLanguage(), getCountry(), getVariant());
+                request.setSpeechRate(getSpeechRate());
+            }
+            request.setPitch(getPitch());
+        }
+
+        @Override
+        protected void stopImpl() {
+            SynthesisRequest synthesisRequest;
+            synchronized (this) {
+                synthesisRequest = mSynthesisRequest;
+            }
+            synthesisRequest.stop();
+            TextToSpeechService.this.onStop();
+        }
+
+        public String getLanguage() {
+            return getStringParam(Engine.KEY_PARAM_LANGUAGE, getDefaultLanguage());
+        }
+
+        private boolean hasLanguage() {
+            return !TextUtils.isEmpty(getStringParam(Engine.KEY_PARAM_LANGUAGE, null));
+        }
+
+        private String getCountry() {
+            if (!hasLanguage()) return getDefaultCountry();
+            return getStringParam(Engine.KEY_PARAM_COUNTRY, "");
+        }
+
+        private String getVariant() {
+            if (!hasLanguage()) return getDefaultVariant();
+            return getStringParam(Engine.KEY_PARAM_VARIANT, "");
+        }
+
+        private int getSpeechRate() {
+            return getIntParam(Engine.KEY_PARAM_RATE, getDefaultSpeechRate());
+        }
+
+        private int getPitch() {
+            return getIntParam(Engine.KEY_PARAM_PITCH, Engine.DEFAULT_PITCH);
+        }
+    }
+
+    private class SynthesisToFileSpeechItem extends SynthesisSpeechItem {
+        private final File mFile;
+
+        public SynthesisToFileSpeechItem(String callingApp, Bundle params, String text,
+                File file) {
+            super(callingApp, params, text);
+            mFile = file;
+        }
+
+        @Override
+        public boolean isValid() {
+            if (!super.isValid()) {
+                return false;
+            }
+            return checkFile(mFile);
+        }
+
+        @Override
+        protected SynthesisRequest createSynthesisRequest() {
+            return new FileSynthesisRequest(getText(), mFile);
+        }
+
+        /**
+         * Checks that the given file can be used for synthesis output.
+         */
+        private boolean checkFile(File file) {
+            try {
+                if (file.exists()) {
+                    Log.v(TAG, "File " + file + " exists, deleting.");
+                    if (!file.delete()) {
+                        Log.e(TAG, "Failed to delete " + file);
+                        return false;
+                    }
+                }
+                if (!file.createNewFile()) {
+                    Log.e(TAG, "Can't create file " + file);
+                    return false;
+                }
+                if (!file.delete()) {
+                    Log.e(TAG, "Failed to delete " + file);
+                    return false;
+                }
+                return true;
+            } catch (IOException e) {
+                Log.e(TAG, "Can't use " + file + " due to exception " + e);
+                return false;
+            }
+        }
+    }
+
+    private class AudioSpeechItem extends SpeechItem {
+
+        private final BlockingMediaPlayer mPlayer;
+
+        public AudioSpeechItem(String callingApp, Bundle params, Uri uri) {
+            super(callingApp, params);
+            mPlayer = new BlockingMediaPlayer(TextToSpeechService.this, uri, getStreamType());
+        }
+
+        @Override
+        public boolean isValid() {
+            return true;
+        }
+
+        @Override
+        protected int playImpl() {
+            return mPlayer.startAndWait() ? TextToSpeech.SUCCESS : TextToSpeech.ERROR;
+        }
+
+        @Override
+        protected void stopImpl() {
+            mPlayer.stop();
+        }
+    }
+
+    private class SilenceSpeechItem extends SpeechItem {
+        private final long mDuration;
+        private final ConditionVariable mDone;
+
+        public SilenceSpeechItem(String callingApp, Bundle params, long duration) {
+            super(callingApp, params);
+            mDuration = duration;
+            mDone = new ConditionVariable();
+        }
+
+        @Override
+        public boolean isValid() {
+            return true;
+        }
+
+        @Override
+        protected int playImpl() {
+            boolean aborted = mDone.block(mDuration);
+            return aborted ? TextToSpeech.ERROR : TextToSpeech.SUCCESS;
+        }
+
+        @Override
+        protected void stopImpl() {
+            mDone.open();
+        }
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (TextToSpeech.Engine.INTENT_ACTION_TTS_SERVICE.equals(intent.getAction())) {
+            return mBinder;
+        }
+        return null;
+    }
+
+    /**
+     * Binder returned from {@code #onBind(Intent)}. The methods in this class can be
+     * called called from several different threads.
+     */
+    private final ITextToSpeechService.Stub mBinder = new ITextToSpeechService.Stub() {
+
+        public int speak(String callingApp, String text, int queueMode, Bundle params) {
+            SpeechItem item = new SynthesisSpeechItem(callingApp, params, text);
+            return mSynthHandler.enqueueSpeechItem(queueMode, item);
+        }
+
+        public int synthesizeToFile(String callingApp, String text, String filename,
+                Bundle params) {
+            File file = new File(filename);
+            SpeechItem item = new SynthesisToFileSpeechItem(callingApp, params, text, file);
+            return mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item);
+        }
+
+        public int playAudio(String callingApp, Uri audioUri, int queueMode, Bundle params) {
+            SpeechItem item = new AudioSpeechItem(callingApp, params, audioUri);
+            return mSynthHandler.enqueueSpeechItem(queueMode, item);
+        }
+
+        public int playSilence(String callingApp, long duration, int queueMode, Bundle params) {
+            SpeechItem item = new SilenceSpeechItem(callingApp, params, duration);
+            return mSynthHandler.enqueueSpeechItem(queueMode, item);
+        }
+
+        public boolean isSpeaking() {
+            return mSynthHandler.isSpeaking();
+        }
+
+        public int stop(String callingApp) {
+            return mSynthHandler.stop(callingApp);
+        }
+
+        public String[] getLanguage() {
+            return onGetLanguage();
+        }
+
+        public int isLanguageAvailable(String lang, String country, String variant) {
+            return onIsLanguageAvailable(lang, country, variant);
+        }
+
+        public int loadLanguage(String lang, String country, String variant) {
+            return onLoadLanguage(lang, country, variant);
+        }
+
+        public void setCallback(String packageName, ITextToSpeechCallback cb) {
+            mCallbacks.setCallback(packageName, cb);
+        }
+    };
+
+    private class CallbackMap extends RemoteCallbackList<ITextToSpeechCallback> {
+
+        private final HashMap<String, ITextToSpeechCallback> mAppToCallback
+                = new HashMap<String, ITextToSpeechCallback>();
+
+        public void setCallback(String packageName, ITextToSpeechCallback cb) {
+            synchronized (mAppToCallback) {
+                ITextToSpeechCallback old;
+                if (cb != null) {
+                    register(cb, packageName);
+                    old = mAppToCallback.put(packageName, cb);
+                } else {
+                    old = mAppToCallback.remove(packageName);
+                }
+                if (old != null && old != cb) {
+                    unregister(old);
+                }
+            }
+        }
+
+        public void dispatchUtteranceCompleted(String packageName, String utteranceId) {
+            ITextToSpeechCallback cb;
+            synchronized (mAppToCallback) {
+                cb = mAppToCallback.get(packageName);
+            }
+            if (cb == null) return;
+            try {
+                cb.utteranceCompleted(utteranceId);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Callback failed: " + e);
+            }
+        }
+
+        @Override
+        public void onCallbackDied(ITextToSpeechCallback callback, Object cookie) {
+            String packageName = (String) cookie;
+            synchronized (mAppToCallback) {
+                mAppToCallback.remove(packageName);
+            }
+            mSynthHandler.stop(packageName);
+        }
+
+        @Override
+        public void kill() {
+            synchronized (mAppToCallback) {
+                mAppToCallback.clear();
+                super.kill();
+            }
+        }
+
+    }
+
+}
diff --git a/core/java/android/text/CharSequenceIterator.java b/core/java/android/text/CharSequenceIterator.java
index 4946406..4b8ac10 100644
--- a/core/java/android/text/CharSequenceIterator.java
+++ b/core/java/android/text/CharSequenceIterator.java
@@ -16,22 +16,18 @@
 
 package android.text;
 
-import android.util.MathUtils;
-
 import java.text.CharacterIterator;
 
 /** {@hide} */
 public class CharSequenceIterator implements CharacterIterator {
     private final CharSequence mValue;
 
-    private final int mStart;
-    private final int mEnd;
+    private final int mLength;
     private int mIndex;
 
     public CharSequenceIterator(CharSequence value) {
         mValue = value;
-        mStart = 0;
-        mEnd = value.length();
+        mLength = value.length();
         mIndex = 0;
     }
 
@@ -46,7 +42,7 @@
 
     /** {@inheritDoc} */
     public char current() {
-        if (mIndex == mEnd) {
+        if (mIndex == mLength) {
             return DONE;
         }
         return mValue.charAt(mIndex);
@@ -54,12 +50,12 @@
 
     /** {@inheritDoc} */
     public int getBeginIndex() {
-        return mStart;
+        return 0;
     }
 
     /** {@inheritDoc} */
     public int getEndIndex() {
-        return mEnd;
+        return mLength;
     }
 
     /** {@inheritDoc} */
@@ -69,27 +65,36 @@
 
     /** {@inheritDoc} */
     public char first() {
-        return setIndex(mStart);
+        return setIndex(0);
     }
 
     /** {@inheritDoc} */
     public char last() {
-        return setIndex(mEnd - 1);
+        return setIndex(mLength - 1);
     }
 
     /** {@inheritDoc} */
     public char next() {
+        if (mIndex == mLength) {
+            return DONE;
+        }
         return setIndex(mIndex + 1);
     }
 
     /** {@inheritDoc} */
     public char previous() {
+        if (mIndex == 0) {
+            return DONE;
+        }
         return setIndex(mIndex - 1);
     }
 
     /** {@inheritDoc} */
     public char setIndex(int index) {
-        mIndex = MathUtils.constrain(index, mStart, mEnd);
+        if ((index < 0) || (index > mLength)) {
+            throw new IllegalArgumentException("Valid range is [" + 0 + "..." + mLength + "]");
+        }
+        mIndex = index;
         return current();
     }
 }
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index ee6342a..ac5db62 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -37,6 +37,7 @@
 import android.text.style.StrikethroughSpan;
 import android.text.style.StyleSpan;
 import android.text.style.SubscriptSpan;
+import android.text.style.SuggestionSpan;
 import android.text.style.SuperscriptSpan;
 import android.text.style.TextAppearanceSpan;
 import android.text.style.TypefaceSpan;
@@ -566,7 +567,7 @@
     /** @hide */
     public static final int ANNOTATION = 18;
     /** @hide */
-    public static final int CORRECTION_SPAN = 19;
+    public static final int SUGGESTION_SPAN = 19;
 
     /**
      * Flatten a CharSequence and whatever styles can be copied across processes
@@ -712,6 +713,10 @@
                     readSpan(p, sp, new Annotation(p));
                     break;
 
+                case SUGGESTION_SPAN:
+                    readSpan(p, sp, new SuggestionSpan(p));
+                    break;
+
                 default:
                     throw new RuntimeException("bogus span encoding " + kind);
                 }
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 80c0106..b25ba8d 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -390,7 +390,7 @@
         }
 
         private boolean isValidOffset(int offset) {
-            return offset >= 0 && offset < mCurrent.length();
+            return offset >= 0 && offset <= mCurrent.length();
         }
 
         private boolean isLetterOrDigit(int offset) {
@@ -404,7 +404,7 @@
         /** {@inheritDoc} */
         public int preceding(int offset) {
             // always round cursor index into valid string index
-            offset = MathUtils.constrain(offset, 0, mCurrent.length() - 1);
+            offset = MathUtils.constrain(offset, 0, mCurrent.length());
 
             do {
                 offset = mIterator.preceding(offset);
@@ -417,7 +417,7 @@
         /** {@inheritDoc} */
         public int following(int offset) {
             // always round cursor index into valid string index
-            offset = MathUtils.constrain(offset, 0, mCurrent.length() - 1);
+            offset = MathUtils.constrain(offset, 0, mCurrent.length());
 
             do {
                 offset = mIterator.following(offset);
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index 5091c9e..7083641 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -26,7 +26,7 @@
 import java.util.Locale;
 
 /**
- * Sets correction candidates of words under this span.
+ * Holds suggestion candidates of words under this span.
  */
 public class SuggestionSpan implements ParcelableSpan {
 
@@ -139,7 +139,7 @@
 
     @Override
     public int getSpanTypeId() {
-        return TextUtils.CORRECTION_SPAN;
+        return TextUtils.SUGGESTION_SPAN;
     }
 
     public static final Parcelable.Creator<SuggestionSpan> CREATOR =
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index a39c7c7..eef2a33 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -505,6 +505,13 @@
         }
     }
 
+    /**
+     * Returns a list of enabled input method subtypes for the specified input method info.
+     * @param imi An input method info whose subtypes list will be returned.
+     * @param allowsImplicitlySelectedSubtypes A boolean flag to allow to return the implicitly
+     * selected subtypes. If an input method info doesn't have enabled subtypes, the framework
+     * will implicitly enable subtypes according to the current system language.
+     */
     public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo imi,
             boolean allowsImplicitlySelectedSubtypes) {
         try {
@@ -1429,16 +1436,26 @@
         }
     }
 
-    public void showInputMethodAndSubtypeEnabler(String topId) {
+    /**
+     * Show the settings for enabling subtypes of the specified input method.
+     * @param imiId An input method, whose subtypes settings will be shown. If imiId is null,
+     * subtypes of all input methods will be shown.
+     */
+    public void showInputMethodAndSubtypeEnabler(String imiId) {
         synchronized (mH) {
             try {
-                mService.showInputMethodAndSubtypeEnablerFromClient(mClient, topId);
+                mService.showInputMethodAndSubtypeEnablerFromClient(mClient, imiId);
             } catch (RemoteException e) {
                 Log.w(TAG, "IME died: " + mCurId, e);
             }
         }
     }
 
+    /**
+     * Returns the current input method subtype. This subtype is one of the subtypes in
+     * the current input method. This method returns null when the current input method doesn't
+     * have any input method subtype.
+     */
     public InputMethodSubtype getCurrentInputMethodSubtype() {
         synchronized (mH) {
             try {
@@ -1450,6 +1467,12 @@
         }
     }
 
+    /**
+     * Switch to a new input method subtype of the current input method.
+     * @param subtype A new input method subtype to switch.
+     * @return true if the current subtype was successfully switched. When the specified subtype is
+     * null, this method returns false.
+     */
     public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
         synchronized (mH) {
             try {
@@ -1461,6 +1484,9 @@
         }
     }
 
+    /**
+     * Returns a map of all shortcut input method info and their subtypes.
+     */
     public Map<InputMethodInfo, List<InputMethodSubtype>> getShortcutInputMethodsAndSubtypes() {
         synchronized (mH) {
             HashMap<InputMethodInfo, List<InputMethodSubtype>> ret =
@@ -1493,6 +1519,15 @@
         }
     }
 
+    /**
+     * Force switch to the last used input method and subtype. If the last input method didn't have
+     * any subtypes, the framework will simply switch to the last input method with no subtype
+     * specified.
+     * @param imeToken Supplies the identifying token given to an input method when it was started,
+     * which allows it to perform this operation on itself.
+     * @return true if the current input method and subtype was successfully switched to the last
+     * used input method and subtype.
+     */
     public boolean switchToLastInputMethod(IBinder imeToken) {
         synchronized (mH) {
             try {
@@ -1504,6 +1539,17 @@
         }
     }
 
+    public InputMethodSubtype getLastInputMethodSubtype() {
+        synchronized (mH) {
+            try {
+                return mService.getLastInputMethodSubtype();
+            } catch (RemoteException e) {
+                Log.w(TAG, "IME died: " + mCurId, e);
+                return null;
+            }
+        }
+    }
+
     void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
         final Printer p = new PrintWriterPrinter(fout);
         p.println("Input method client state for " + this + ":");
diff --git a/core/java/android/webkit/webdriver/By.java b/core/java/android/webkit/webdriver/By.java
new file mode 100644
index 0000000..b40351d
--- /dev/null
+++ b/core/java/android/webkit/webdriver/By.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit.webdriver;
+
+/**
+ * Mechanism to locate elements within the DOM of the page.
+ * @hide
+ */
+public abstract class By {
+    public abstract WebElement findElement(WebElement element);
+
+    /**
+     * Locates an element by its HTML id attribute.
+     *
+     * @param id The HTML id attribute to look for.
+     * @return A By instance that locates elements by their HTML id attributes.
+     */
+    public static By id(final String id) {
+        throwIfNull(id);
+        return new By() {
+            @Override
+            public WebElement findElement(WebElement element) {
+                return element.findElementById(id);
+            }
+
+            @Override
+            public String toString() {
+                return "By.id: " + id;
+            }
+        };
+    }
+
+    /**
+     * Locates an element by the matching the exact text on the HTML link.
+     *
+     * @param linkText The exact text to match against.
+     * @return A By instance that locates elements by the text displayed by
+     * the link.
+     */
+    public static By linkText(final String linkText) {
+        throwIfNull(linkText);
+        return new By() {
+            @Override
+            public WebElement findElement(WebElement element) {
+                return element.findElementByLinkText(linkText);
+            }
+
+            @Override
+            public String toString() {
+                return "By.linkText: " + linkText;
+            }
+        };
+    }
+
+    /**
+     * Locates an element by matching partial part of the text displayed by an
+     * HTML link.
+     *
+     * @param linkText The text that should be contained by the text displayed
+     * on the link.
+     * @return A By instance that locates elements that contain the given link
+     * text.
+     */
+    public static By partialLinkText(final String linkText) {
+        throwIfNull(linkText);
+        return new By() {
+            @Override
+            public WebElement findElement(WebElement element) {
+                return element.findElementByPartialLinkText(linkText);
+            }
+
+            @Override
+            public String toString() {
+                return "By.partialLinkText: " + linkText;
+            }
+        };
+    }
+
+    /**
+     * Locates an element by matching its HTML name attribute.
+     *
+     * @param name The value of the HTML name attribute.
+     * @return A By instance that locates elements by the HTML name attribute.
+     */
+    public static By name(final String name) {
+        throwIfNull(name);
+        return new By() {
+            @Override
+            public WebElement findElement(WebElement element) {
+                return element.findElementByName(name);
+            }
+
+            @Override
+            public String toString() {
+                return "By.name: " + name;
+            }
+        };
+    }
+
+    /**
+     * Locates an element by matching its class name.
+     * @param className The class name
+     * @return A By instance that locates elements by their class name attribute.
+     */
+    public static By className(final String className) {
+        throwIfNull(className);
+        return new By() {
+            @Override
+            public WebElement findElement(WebElement element) {
+                return element.findElementByClassName(className);
+            }
+
+            @Override
+            public String toString() {
+                return "By.className: " + className;
+            }
+        };
+    }
+
+    /**
+     * Locates an element by matching its css property.
+     *
+     * @param css The css property.
+     * @return A By instance that locates elements by their css property.
+     */
+    public static By css(final String css) {
+        throwIfNull(css);
+        return new By() {
+            @Override
+            public WebElement findElement(WebElement element) {
+                return element.findElementByCss(css);
+            }
+
+            @Override
+            public String toString() {
+                return "By.css: " + css;
+            }
+        };
+    }
+
+    /**
+     * Locates an element by matching its HTML tag name.
+     *
+     * @param tagName The HTML tag name to look for.
+     * @return A By instance that locates elements using the name of the
+     * HTML tag.
+     */
+    public static By tagName(final String tagName) {
+        throwIfNull(tagName);
+        return new By() {
+            @Override
+            public WebElement findElement(WebElement element) {
+                return element.findElementByTagName(tagName);
+            }
+
+            @Override
+            public String toString() {
+                return "By.tagName: " + tagName;
+            }
+        };
+    }
+
+    /**
+     * Locates an element using an XPath expression.
+     *
+     * <p>When using XPath, be aware that this follows standard conventions: a
+     * search prefixed with "//" will search the entire document, not just the
+     * children of the current node. Use ".//" to limit your search to the
+     * children of this {@link android.webkit.webdriver.WebElement}.
+     *
+     * @param xpath The XPath expression to use.
+     * @return A By instance that locates elements using the given XPath.
+     */
+    public static By xpath(final String xpath) {
+        throwIfNull(xpath);
+        return new By() {
+            @Override
+            public WebElement findElement(WebElement element) {
+                return element.findElementByXPath(xpath);
+            }
+
+            @Override
+            public String toString() {
+                return "By.xpath: " + xpath;
+            }
+        };
+    }
+
+    private static void throwIfNull(String argument) {
+        if (argument == null) {
+            throw new IllegalArgumentException(
+                    "Cannot find elements with null locator.");
+        }
+    }
+}
diff --git a/core/java/android/webkit/webdriver/WebDriver.java b/core/java/android/webkit/webdriver/WebDriver.java
new file mode 100644
index 0000000..90e701f
--- /dev/null
+++ b/core/java/android/webkit/webdriver/WebDriver.java
@@ -0,0 +1,526 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit.webdriver;
+
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
+import com.android.internal.R;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.os.Handler;
+import android.os.Message;
+import android.webkit.WebView;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Drives a web application by controlling the WebView. This class
+ * provides a DOM-like API allowing to get information about the page,
+ * navigate, and interact with the web application. This is particularly useful
+ * for testing a web application.
+ *
+ * <p/>{@link android.webkit.webdriver.WebDriver} should be created in the main
+ * thread, and invoked from another thread. Here is a sample usage:
+ *
+ * public class WebDriverStubActivity extends Activity {
+ *   private WebDriver mDriver;
+ *
+ *   public void onCreate(Bundle savedInstanceState) {
+ *       super.onCreate(savedInstanceState);
+ *       WebView view = new WebView(this);
+ *       mDriver = new WebDriver(view);
+ *       setContentView(view);
+ *   }
+ *
+ *
+ *   public WebDriver getDriver() {
+ *       return mDriver;
+ *   }
+ *}
+ *
+ * public class WebDriverTest extends
+ *       ActivityInstrumentationTestCase2<WebDriverStubActivity>{
+ *   private WebDriver mDriver;
+ *
+ *   public WebDriverTest() {
+ *       super(WebDriverStubActivity.class);
+ *   }
+ *
+ *   protected void setUp() throws Exception {
+ *       super.setUp();
+ *       mDriver = getActivity().getDriver();
+ *   }
+ *
+ *   public void testGoogle() {
+ *       mDriver.get("http://google.com");
+ *       WebElement searchBox = mDriver.findElement(By.name("q"));
+ *       q.sendKeys("Cheese!");
+ *       q.submit();
+ *       assertTrue(mDriver.findElements(By.partialLinkText("Cheese")).size() > 0);
+ *   }
+ *}
+ *
+ * @hide
+ */
+public class WebDriver {
+    // Timeout for page load in milliseconds.
+    private static final int LOADING_TIMEOUT = 30000;
+    // Timeout for executing JavaScript in the WebView in milliseconds.
+    private static final int JS_EXECUTION_TIMEOUT = 10000;
+
+    // Commands posted to the handler
+    private static final int CMD_GET_URL = 1;
+    private static final int CMD_EXECUTE_SCRIPT = 2;
+
+    private static final String ELEMENT_KEY = "ELEMENT";
+    private static final String STATUS = "status";
+    private static final String VALUE = "value";
+
+    private static final long MAIN_THREAD = Thread.currentThread().getId();
+
+    // This is updated by a callabck from JavaScript when the result is ready.
+    private String mJsResult;
+
+    // Used for synchronization
+    private final Object mSyncObject;
+
+    // Updated when the command is done executing in the main thread.
+    private volatile boolean mCommandDone;
+
+    private WebView mWebView;
+
+    // This WebElement represents the object document.documentElement
+    private WebElement mDocumentElement;
+
+    // This Handler runs in the main UI thread.
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.what == CMD_GET_URL) {
+                final String url = (String) msg.obj;
+                mWebView.loadUrl(url);
+            } else if (msg.what == CMD_EXECUTE_SCRIPT) {
+                mWebView.loadUrl("javascript:" + (String) msg.obj);
+            }
+        }
+    };
+
+    /**
+     * Error codes from the WebDriver wire protocol
+     * http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes
+     */
+    private enum ErrorCode {
+        SUCCESS(0),
+        NO_SUCH_ELEMENT(7),
+        NO_SUCH_FRAME(8),
+        UNKNOWN_COMMAND(9),
+        UNSUPPORTED_OPERATION(9),  // Alias
+        STALE_ELEMENT_REFERENCE(10),
+        ELEMENT_NOT_VISISBLE(11),
+        INVALID_ELEMENT_STATE(12),
+        UNKNOWN_ERROR(13),
+        ELEMENT_NOT_SELECTABLE(15),
+        XPATH_LOOKUP_ERROR(19),
+        NO_SUCH_WINDOW(23),
+        INVALID_COOKIE_DOMAIN(24),
+        UNABLE_TO_SET_COOKIE(25),
+        MODAL_DIALOG_OPENED(26),
+        MODAL_DIALOG_OPEN(27),
+        SCRIPT_TIMEOUT(28);
+
+        private final int mCode;
+        private static ErrorCode[] values = ErrorCode.values();
+
+        ErrorCode(int code) {
+            this.mCode = code;
+        }
+
+        public int getCode() {
+            return mCode;
+        }
+
+        public static ErrorCode get(final int intValue) {
+            for (int i = 0; i < values.length; i++) {
+                if (values[i].getCode() == intValue) {
+                    return values[i];
+                }
+            }
+            throw new IllegalArgumentException(intValue
+                    + " does not map to any ErrorCode.");
+        }
+    }
+
+    public WebDriver(WebView webview) {
+        this.mWebView = webview;
+        if (mWebView == null) {
+            throw new IllegalArgumentException("WebView cannot be null");
+        }
+        if (!mWebView.getSettings().getJavaScriptEnabled()) {
+            throw new RuntimeException("Javascript is disabled in the WebView. "
+                    + "Enable it to use WebDriver");
+        }
+        shouldRunInMainThread(true);
+
+        mSyncObject = new Object();
+        this.mWebView = webview;
+        WebchromeClientWrapper chromeWrapper = new WebchromeClientWrapper(
+                webview.getWebChromeClient(), this);
+        mWebView.setWebChromeClient(chromeWrapper);
+        mDocumentElement = new WebElement(this, "");
+        mWebView.addJavascriptInterface(new JavascriptResultReady(),
+                "webdriver");
+    }
+
+    /**
+     * Loads a URL in the WebView. This function is blocking and will return
+     * when the page has finished loading.
+     *
+     * @param url The URL to load.
+     */
+    public void get(String url) {
+        executeCommand(CMD_GET_URL, url, LOADING_TIMEOUT);
+    }
+
+    /**
+     * @return The source page of the currently loaded page in WebView.
+     */
+    public String getPageSource() {
+        return (String) executeScript("return new XMLSerializer()."
+                + "serializeToString(document);");
+    }
+
+    /**
+     * Find the first {@link android.webkit.webdriver.WebElement} using the
+     * given method.
+     *
+     * @param by The locating mechanism to use.
+     * @return The first matching element on the current context.
+     * @throws {@link android.webkit.webdriver.WebElementNotFoundException} if
+     * no matching element was found.
+     */
+    public WebElement findElement(By by) {
+        return by.findElement(mDocumentElement);
+    }
+
+    /**
+     * Clears the WebView.
+     */
+    public void quit() {
+        mWebView.clearCache(true);
+        mWebView.clearFormData();
+        mWebView.clearHistory();
+        mWebView.clearSslPreferences();
+        mWebView.clearView();
+    }
+
+    /**
+     * Executes javascript in the context of the main frame.
+     *
+     * If the script has a return value the following happens:
+     * <ul>
+     * <li>For an HTML element, this method returns a WebElement</li>
+     * <li>For a decimal, a Double is returned</li>
+     * <li>For non-decimal number, a Long is returned</li>
+     * <li>For a boolean, a Boolean is returned</li>
+     * <li>For all other cases, a String is returned</li>
+     * <li>For an array, this returns a List<Object> with each object
+     * following the rules above.</li>
+     * <li>For an object literal this returns a Map<String, Object>. Note that
+     * Object literals keys can only be Strings. Non Strings keys will
+     * be filtered out.</li>
+     * </ul>
+     *
+     * <p> Arguments must be a number, a boolean, a string a WebElement or
+     * a list of any combination of the above. The arguments will be made
+     * available to the javascript via the "arguments" magic variable,
+     * as if the function was called via "Function.apply".
+     *
+     * @param script The JavaScript to execute.
+     * @param args The arguments to the script. Can be any of a number, boolean,
+     * string, WebElement or a List of those.
+     * @return A Boolean, Long, Double, String, WebElement, List or null.
+     */
+    public Object executeScript(final String script, final Object... args) {
+        String scriptArgs = "[" + convertToJsArgs(args) + "]";
+        String injectScriptJs = getResourceAsString(R.raw.execute_script_android);
+        return executeRawJavascript("(" + injectScriptJs +
+                ")(" + escapeAndQuote(script) + ", " + scriptArgs + ", true)");
+    }
+
+    /**
+     * Converts the arguments passed to a JavaScript friendly format.
+     *
+     * @param args The arguments to convert.
+     * @return Comma separated Strings containing the arguments.
+     */
+    /*package*/ String convertToJsArgs(final Object... args) {
+        StringBuilder toReturn = new StringBuilder();
+        int length = args.length;
+        for (int i = 0; i < length; i++) {
+            toReturn.append((i > 0) ? "," : "");
+            if (args[i] instanceof List<?>) {
+                toReturn.append("[");
+                List<Object> aList = (List<Object>) args[i];
+                for (int j = 0 ; j < aList.size(); j++) {
+                    String comma = ((j == 0) ? "" : ",");
+                    toReturn.append(comma + convertToJsArgs(aList.get(j)));
+                }
+                toReturn.append("]");
+            } else if (args[i] instanceof Map<?, ?>) {
+                Map<Object, Object> aMap = (Map<Object, Object>) args[i];
+                String toAdd = "{";
+                for (Object key: aMap.keySet()) {
+                    toAdd += key + ":"
+                            + convertToJsArgs(aMap.get(key)) + ",";
+                }
+                toReturn.append(toAdd.substring(0, toAdd.length() -1) + "}");
+            } else if (args[i] instanceof WebElement) {
+                // WebElement are represented in JavaScript by Objects as
+                // follow: {ELEMENT:"id"}
+                toReturn.append("{" + ELEMENT_KEY + ":\""
+                        + ((WebElement) args[i]).getId() + "\"}");
+            } else if (args[i] instanceof Number || args[i] instanceof Boolean) {
+                toReturn.append(String.valueOf(args[i]));
+            } else if (args[i] instanceof String) {
+                toReturn.append(escapeAndQuote((String) args[i]));
+            } else {
+                throw new IllegalArgumentException(
+                        "Javascript arguments can be "
+                            + "a Number, a Boolean, a String, a WebElement, "
+                            + "or a List or a Map of those. Got: "
+                            + ((args[i] == null) ? "null" : args[i].toString()));
+            }
+        }
+        return toReturn.toString();
+    }
+
+    /*package*/ Object executeRawJavascript(final String script) {
+        String result = executeCommand(CMD_EXECUTE_SCRIPT,
+                "window.webdriver.resultReady(" + script + ")",
+                JS_EXECUTION_TIMEOUT);
+        try {
+            JSONObject json = new JSONObject(result);
+            throwIfError(json);
+            Object value = json.get(VALUE);
+            return convertJsonToJavaObject(value);
+        } catch (JSONException e) {
+            throw new RuntimeException("Failed to parse JavaScript result: "
+                    + result.toString(), e);
+        }
+    }
+
+    /*package*/ String getResourceAsString(final int resourceId) {
+        InputStream is = mWebView.getResources().openRawResource(resourceId);
+        BufferedReader br = new BufferedReader(new InputStreamReader(is));
+        StringBuilder sb = new StringBuilder();
+        String line = null;
+        try {
+            while ((line = br.readLine()) != null) {
+                sb.append(line);
+            }
+            br.close();
+            is.close();
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to open JavaScript resource.", e);
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Wraps the given string into quotes and escape existing quotes
+     * and backslashes.
+     * "foo" -> "\"foo\""
+     * "foo\"" -> "\"foo\\\"\""
+     * "fo\o" -> "\"fo\\o\""
+     *
+     * @param toWrap The String to wrap in quotes
+     * @return a String wrapping the original String in quotes
+     */
+    private static String escapeAndQuote(final String toWrap) {
+        StringBuilder toReturn = new StringBuilder("\"");
+        for (int i = 0; i < toWrap.length(); i++) {
+            char c = toWrap.charAt(i);
+            if (c == '\"') {
+                toReturn.append("\\\"");
+            } else if (c == '\\') {
+                toReturn.append("\\\\");
+            } else {
+                toReturn.append(c);
+            }
+        }
+        toReturn.append("\"");
+        return toReturn.toString();
+    }
+
+    private Object convertJsonToJavaObject(final Object toConvert) {
+        try {
+            if (toConvert == null
+                    || toConvert.equals(null)
+                    || "undefined".equals(toConvert)
+                    || "null".equals(toConvert)) {
+                return null;
+            } else if (toConvert instanceof Boolean) {
+                return toConvert;
+            } else if (toConvert instanceof Double
+                    || toConvert instanceof Float) {
+                return Double.valueOf(String.valueOf(toConvert));
+            } else if (toConvert instanceof Integer
+                    || toConvert instanceof Long) {
+              return Long.valueOf(String.valueOf(toConvert));
+            } else if (toConvert instanceof JSONArray) { // List
+                return convertJsonArrayToList((JSONArray) toConvert);
+            } else if (toConvert instanceof JSONObject) { // Map or WebElment
+                JSONObject map = (JSONObject) toConvert;
+                if (map.opt(ELEMENT_KEY) != null) { // WebElement
+                    return new WebElement(this, (String) map.get(ELEMENT_KEY));
+                } else { // Map
+                    return convertJsonObjectToMap(map);
+                }
+            } else {
+                return toConvert.toString();
+            }
+        } catch (JSONException e) {
+            throw new RuntimeException("Failed to parse JavaScript result: "
+                    + toConvert.toString(), e);
+        }
+    }
+
+    private List<Object> convertJsonArrayToList(final JSONArray json) {
+        List<Object> toReturn = Lists.newArrayList();
+        for (int i = 0; i < json.length(); i++) {
+            try {
+                toReturn.add(convertJsonToJavaObject(json.get(i)));
+            } catch (JSONException e) {
+                throw new RuntimeException("Failed to parse JSON: "
+                        + json.toString(), e);
+            }
+        }
+        return toReturn;
+    }
+
+    private Map<Object, Object> convertJsonObjectToMap(final JSONObject json) {
+        Map<Object, Object> toReturn = Maps.newHashMap();
+        for (Iterator it = json.keys(); it.hasNext();) {
+            String key = (String) it.next();
+            try {
+                Object value = json.get(key);
+                toReturn.put(convertJsonToJavaObject(key),
+                        convertJsonToJavaObject(value));
+            } catch (JSONException e) {
+                throw new RuntimeException("Failed to parse JSON:"
+                        + json.toString(), e);
+            }
+        }
+        return toReturn;
+    }
+
+    private void throwIfError(final JSONObject jsonObject) {
+        ErrorCode status;
+        String errorMsg;
+        try {
+            status = ErrorCode.get((Integer) jsonObject.get(STATUS));
+            errorMsg  = String.valueOf(jsonObject.get(VALUE));
+        } catch (JSONException e) {
+            throw new RuntimeException("Failed to parse JSON Object: "
+                    + jsonObject, e);
+        }
+        switch (status) {
+            case SUCCESS:
+                return;
+            case NO_SUCH_ELEMENT:
+                throw new WebElementNotFoundException("Could not find "
+                        + "WebElement.");
+            case STALE_ELEMENT_REFERENCE:
+                throw new WebElementStaleException("WebElement is stale.");
+            default:
+                throw new RuntimeException("Error: " + errorMsg);
+        }
+    }
+
+    private void shouldRunInMainThread(boolean value) {
+        assert (value == (MAIN_THREAD == Thread.currentThread().getId()));
+    }
+
+    /**
+     * Interface called from JavaScript when the result is ready.
+     */
+    private class JavascriptResultReady {
+
+        /**
+         * A callback from JavaScript to Java that passes the result as a
+         * parameter. This method is available from the WebView's
+         * JavaScript DOM as window.webdriver.resultReady().
+         *
+         * @param result The result that should be sent to Java from Javascript.
+         */
+        public void resultReady(final String result) {
+            synchronized (mSyncObject) {
+                mJsResult = result;
+                mCommandDone = true;
+                mSyncObject.notify();
+            }
+        }
+    }
+
+    /* package */ void notifyCommandDone() {
+        synchronized (mSyncObject) {
+            mCommandDone = true;
+            mSyncObject.notify();
+        }
+    }
+
+    /**
+     * Executes the given command by posting a message to mHandler. This thread
+     * will block until the command which runs in the main thread is done.
+     *
+     * @param command The command to run.
+     * @param arg The argument for that command.
+     * @param timeout A timeout in milliseconds.
+     */
+    private String executeCommand(int command, final Object arg, long timeout) {
+        shouldRunInMainThread(false);
+        synchronized (mSyncObject) {
+            mCommandDone = false;
+            Message msg = mHandler.obtainMessage(command);
+            msg.obj = arg;
+            mHandler.sendMessage(msg);
+
+            long end = System.currentTimeMillis() + timeout;
+            while (!mCommandDone) {
+                if (System.currentTimeMillis() >= end) {
+                    throw new RuntimeException("Timeout executing command.");
+                }
+                try {
+                    mSyncObject.wait(timeout);
+                } catch (InterruptedException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        }
+        return mJsResult;
+    }
+}
diff --git a/core/java/android/webkit/webdriver/WebElement.java b/core/java/android/webkit/webdriver/WebElement.java
new file mode 100644
index 0000000..384d55f
--- /dev/null
+++ b/core/java/android/webkit/webdriver/WebElement.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit.webdriver;
+
+import com.android.internal.R;
+
+/**
+ * Represents an HTML element. Typically most interactions with a web page
+ * will be performed through this class.
+ *
+ * @hide
+ */
+public class WebElement {
+    private final String mId;
+    private final WebDriver mDriver;
+
+    /**
+     * Package constructor to prevent clients from creating a new WebElement
+     * instance.
+     *
+     * <p> A WebElement represents an HTML element on the page.
+     * The corresponding HTML element is stored in a JS cache in the page
+     * that can be accessed through JavaScript using "bot.inject.cache".
+     *
+     * @param driver The WebDriver instance to use.
+     * @param id The index of the HTML element in the JavaSctipt cache. Pass
+     * an empty String to indicate that this is the
+     * document.documentElement object.
+     */
+    /* Package */ WebElement(final WebDriver driver, final String id) {
+        this.mId = id;
+        this.mDriver = driver;
+    }
+
+    /**
+     * Finds the first {@link android.webkit.webdriver.WebElement} using the
+     * given method.
+     *
+     * @param by The locating mechanism to use.
+     * @return The first matching element on the current context.
+     */
+    public WebElement findElement(final By by) {
+        return by.findElement(this);
+    }
+
+    /**
+     * Gets the visisble (i.e. not hidden by CSS) innerText of this element,
+     * inlcuding sub-elements.
+     *
+     * @return the innerText of this element.
+     * @throws {@link android.webkit.webdriver.WebElementStaleException} if this
+     * element is stale, i.e. not on the current DOM.
+     */
+    public String getText() {
+        String getText = mDriver.getResourceAsString(R.raw.get_text_android);
+        if (mId.equals("")) {
+            return null;
+        }
+        return (String) executeAtom(getText, this);
+    }
+
+    /*package*/ String getId() {
+        return mId;
+    }
+
+    /* package */ WebElement findElementById(final String locator) {
+        return findElement("id", locator);
+    }
+
+    /* package */ WebElement findElementByLinkText(final String linkText) {
+        return findElement("linkText", linkText);
+    }
+
+    /* package */ WebElement findElementByPartialLinkText(
+            final String linkText) {
+        return findElement("partialLinkText", linkText);
+    }
+
+    /* package */ WebElement findElementByName(final String name) {
+        return findElement("name", name);
+    }
+
+    /* package */ WebElement findElementByClassName(final String className) {
+        return findElement("className", className);
+    }
+
+    /* package */ WebElement findElementByCss(final String css) {
+        return findElement("css", css);
+    }
+
+    /* package */ WebElement findElementByTagName(final String tagName) {
+        return findElement("tagName", tagName);
+    }
+
+    /* package */ WebElement findElementByXPath(final String xpath) {
+        return findElement("xpath", xpath);
+    }
+
+    private Object executeAtom(final String atom, final Object... args) {
+        String scriptArgs = mDriver.convertToJsArgs(args);
+        return mDriver.executeRawJavascript("(" +
+                atom + ")(" + scriptArgs + ")");
+    }
+
+    private WebElement findElement(String strategy, String locator) {
+        String findElement = mDriver.getResourceAsString(
+                R.raw.find_element_android);
+        WebElement el;
+        if (mId.equals("")) {
+            // Use default as root which is the document object
+            el = (WebElement) executeAtom(findElement, strategy, locator);
+        } else {
+            // Use this as root
+            el = (WebElement) executeAtom(findElement, strategy, locator, this);
+        }
+        if (el == null) {
+            throw new WebElementNotFoundException("Could not find element "
+                    + "with " + strategy + ": " + locator);
+        }
+        return el;
+    }
+}
diff --git a/core/java/android/webkit/webdriver/WebElementNotFoundException.java b/core/java/android/webkit/webdriver/WebElementNotFoundException.java
new file mode 100644
index 0000000..e66d279
--- /dev/null
+++ b/core/java/android/webkit/webdriver/WebElementNotFoundException.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit.webdriver;
+
+/**
+ * Thrown when a {@link android.webkit.webdriver.WebElement} is not found in the
+ * DOM of the page.
+ * @hide
+ */
+public class WebElementNotFoundException extends RuntimeException {
+
+    public WebElementNotFoundException() {
+        super();
+    }
+
+    public WebElementNotFoundException(String reason) {
+        super(reason);
+    }
+
+    public WebElementNotFoundException(String reason, Throwable cause) {
+        super(reason, cause);
+    }
+
+    public WebElementNotFoundException(Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/core/java/android/webkit/webdriver/WebElementStaleException.java b/core/java/android/webkit/webdriver/WebElementStaleException.java
new file mode 100644
index 0000000..c59e7945
--- /dev/null
+++ b/core/java/android/webkit/webdriver/WebElementStaleException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit.webdriver;
+
+/**
+ * Thrown when trying to access a {@link android.webkit.webdriver.WebElement}
+ * that is stale. This mean that the {@link android.webkit.webdriver.WebElement}
+ * is no longer present on the DOM of the page.
+ * @hide
+ */
+public class WebElementStaleException extends RuntimeException {
+
+    public WebElementStaleException() {
+        super();
+    }
+
+    public  WebElementStaleException(String reason) {
+        super(reason);
+    }
+
+    public WebElementStaleException(String reason, Throwable cause) {
+        super(reason, cause);
+    }
+
+    public WebElementStaleException(Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/core/java/android/webkit/webdriver/WebchromeClientWrapper.java b/core/java/android/webkit/webdriver/WebchromeClientWrapper.java
new file mode 100644
index 0000000..ea33c5b
--- /dev/null
+++ b/core/java/android/webkit/webdriver/WebchromeClientWrapper.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit.webdriver;
+
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Message;
+import android.view.View;
+import android.webkit.ConsoleMessage;
+import android.webkit.GeolocationPermissions;
+import android.webkit.JsPromptResult;
+import android.webkit.JsResult;
+import android.webkit.ValueCallback;
+import android.webkit.WebChromeClient;
+import android.webkit.WebStorage;
+import android.webkit.WebView;
+
+/* package */ class WebchromeClientWrapper extends WebChromeClient {
+
+    private final WebChromeClient mDelegate;
+    private final WebDriver mDriver;
+
+    public WebchromeClientWrapper(WebChromeClient delegate, WebDriver driver) {
+        if (delegate == null) {
+            this.mDelegate = new WebChromeClient();
+        } else {
+            this.mDelegate = delegate;
+        }
+        this.mDriver = driver;
+    }
+
+    @Override
+    public void onProgressChanged(WebView view, int newProgress) {
+        if (newProgress == 100) {
+            mDriver.notifyCommandDone();
+        }
+        mDelegate.onProgressChanged(view, newProgress);
+    }
+
+    @Override
+    public void onReceivedTitle(WebView view, String title) {
+        mDelegate.onReceivedTitle(view, title);
+    }
+
+    @Override
+    public void onReceivedIcon(WebView view, Bitmap icon) {
+        mDelegate.onReceivedIcon(view, icon);
+    }
+
+    @Override
+    public void onReceivedTouchIconUrl(WebView view, String url,
+            boolean precomposed) {
+        mDelegate.onReceivedTouchIconUrl(view, url, precomposed);
+    }
+
+    @Override
+    public void onShowCustomView(View view,
+            CustomViewCallback callback) {
+        mDelegate.onShowCustomView(view, callback);
+    }
+
+    @Override
+    public void onHideCustomView() {
+        mDelegate.onHideCustomView();
+    }
+
+    @Override
+    public boolean onCreateWindow(WebView view, boolean dialog,
+            boolean userGesture, Message resultMsg) {
+        return mDelegate.onCreateWindow(view, dialog, userGesture, resultMsg);
+    }
+
+    @Override
+    public void onRequestFocus(WebView view) {
+        mDelegate.onRequestFocus(view);
+    }
+
+    @Override
+    public void onCloseWindow(WebView window) {
+        mDelegate.onCloseWindow(window);
+    }
+
+    @Override
+    public boolean onJsAlert(WebView view, String url, String message,
+            JsResult result) {
+        return mDelegate.onJsAlert(view, url, message, result);
+    }
+
+    @Override
+    public boolean onJsConfirm(WebView view, String url, String message,
+            JsResult result) {
+        return mDelegate.onJsConfirm(view, url, message, result);
+    }
+
+    @Override
+    public boolean onJsPrompt(WebView view, String url, String message,
+            String defaultValue, JsPromptResult result) {
+        return mDelegate.onJsPrompt(view, url, message, defaultValue, result);
+    }
+
+    @Override
+    public boolean onJsBeforeUnload(WebView view, String url, String message,
+            JsResult result) {
+        return mDelegate.onJsBeforeUnload(view, url, message, result);
+    }
+
+    @Override
+    public void onExceededDatabaseQuota(String url, String databaseIdentifier,
+            long currentQuota, long estimatedSize, long totalUsedQuota,
+            WebStorage.QuotaUpdater quotaUpdater) {
+        mDelegate.onExceededDatabaseQuota(url, databaseIdentifier, currentQuota,
+                estimatedSize, totalUsedQuota, quotaUpdater);
+    }
+
+    @Override
+    public void onReachedMaxAppCacheSize(long spaceNeeded, long totalUsedQuota,
+            WebStorage.QuotaUpdater quotaUpdater) {
+        mDelegate.onReachedMaxAppCacheSize(spaceNeeded, totalUsedQuota,
+                quotaUpdater);
+    }
+
+    @Override
+    public void onGeolocationPermissionsShowPrompt(String origin,
+            GeolocationPermissions.Callback callback) {
+        mDelegate.onGeolocationPermissionsShowPrompt(origin, callback);
+    }
+
+    @Override
+    public void onGeolocationPermissionsHidePrompt() {
+        mDelegate.onGeolocationPermissionsHidePrompt();
+    }
+
+    @Override
+    public boolean onJsTimeout() {
+        return mDelegate.onJsTimeout();
+    }
+
+    @Override
+    public void onConsoleMessage(String message, int lineNumber,
+            String sourceID) {
+        mDelegate.onConsoleMessage(message, lineNumber, sourceID);
+    }
+
+    @Override
+    public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
+        return mDelegate.onConsoleMessage(consoleMessage);
+    }
+
+    @Override
+    public Bitmap getDefaultVideoPoster() {
+        return mDelegate.getDefaultVideoPoster();
+    }
+
+    @Override
+    public View getVideoLoadingProgressView() {
+        return mDelegate.getVideoLoadingProgressView();
+    }
+
+    @Override
+    public void getVisitedHistory(ValueCallback<String[]> callback) {
+        mDelegate.getVisitedHistory(callback);
+    }
+
+    @Override
+    public void openFileChooser(ValueCallback<Uri> uploadFile,
+            String acceptType) {
+        mDelegate.openFileChooser(uploadFile, acceptType);
+    }
+
+    @Override
+    public void setInstallableWebApp() {
+        mDelegate.setInstallableWebApp();
+    }
+
+    @Override
+    public void setupAutoFill(Message msg) {
+        mDelegate.setupAutoFill(msg);
+    }
+}
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index f659ead..590a768 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -39,7 +39,7 @@
  * Children are drawn in a stack, with the most recently added child on top.
  * The size of the frame layout is the size of its largest child (plus padding), visible
  * or not (if the FrameLayout's parent permits). Views that are GONE are used for sizing
- * only if {@link #setMeasureAllChildren(boolean) setConsiderGoneChildrenWhenMeasuring()}
+ * only if {@link #setMeasureAllChildren(boolean) setMeasureAllChildren()}
  * is set to true.
  *
  * @attr ref android.R.styleable#FrameLayout_foreground
@@ -566,4 +566,3 @@
         }
     }
 }
-
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index d86504d..7cf33fc 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -36,6 +36,7 @@
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
 import android.util.Log;
+import android.util.LogWriter;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
 import android.util.Slog;
@@ -70,7 +71,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 54;
+    private static final int VERSION = 60;
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -154,11 +155,27 @@
     boolean mHaveBatteryLevel = false;
     boolean mRecordingHistory = true;
     int mNumHistoryItems;
+
+    static final int MAX_HISTORY_BUFFER = 128*1024; // 128KB
+    static final int MAX_MAX_HISTORY_BUFFER = 144*1024; // 144KB
+    final Parcel mHistoryBuffer = Parcel.obtain();
+    final HistoryItem mHistoryLastWritten = new HistoryItem();
+    final HistoryItem mHistoryLastLastWritten = new HistoryItem();
+    final HistoryItem mHistoryReadTmp = new HistoryItem();
+    int mHistoryBufferLastPos = -1;
+    boolean mHistoryOverflow = false;
+    long mLastHistoryTime = 0;
+
+    final HistoryItem mHistoryCur = new HistoryItem();
+
     HistoryItem mHistory;
     HistoryItem mHistoryEnd;
     HistoryItem mHistoryLastEnd;
     HistoryItem mHistoryCache;
-    final HistoryItem mHistoryCur = new HistoryItem();
+
+    private HistoryItem mHistoryIterator;
+    private boolean mReadOverflow;
+    private boolean mIteratingHistory;
 
     int mStartCount;
 
@@ -1189,9 +1206,84 @@
         mBtHeadset = headset;
     }
 
+    int mChangedBufferStates = 0;
+
+    void addHistoryBufferLocked(long curTime) {
+        if (!mHaveBatteryLevel || !mRecordingHistory) {
+            return;
+        }
+
+        final long timeDiff = (mHistoryBaseTime+curTime) - mHistoryLastWritten.time;
+        if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE
+                && timeDiff < 2000
+                && ((mHistoryLastWritten.states^mHistoryCur.states)&mChangedBufferStates) == 0) {
+            // If the current is the same as the one before, then we no
+            // longer need the entry.
+            mHistoryBuffer.setDataSize(mHistoryBufferLastPos);
+            mHistoryBuffer.setDataPosition(mHistoryBufferLastPos);
+            mHistoryBufferLastPos = -1;
+            if (mHistoryLastLastWritten.cmd == HistoryItem.CMD_UPDATE
+                    && timeDiff < 500 && mHistoryLastLastWritten.same(mHistoryCur)) {
+                // If this results in us returning to the state written
+                // prior to the last one, then we can just delete the last
+                // written one and drop the new one.  Nothing more to do.
+                mHistoryLastWritten.setTo(mHistoryLastLastWritten);
+                mHistoryLastLastWritten.cmd = HistoryItem.CMD_NULL;
+                return;
+            }
+            mChangedBufferStates |= mHistoryLastWritten.states^mHistoryCur.states;
+            curTime = mHistoryLastWritten.time - mHistoryBaseTime;
+            mHistoryLastWritten.setTo(mHistoryLastLastWritten);
+        } else {
+            mChangedBufferStates = 0;
+        }
+
+        final int dataSize = mHistoryBuffer.dataSize();
+        if (dataSize >= MAX_HISTORY_BUFFER) {
+            if (!mHistoryOverflow) {
+                mHistoryOverflow = true;
+                addHistoryBufferLocked(curTime, HistoryItem.CMD_OVERFLOW);
+            }
+
+            // Once we've reached the maximum number of items, we only
+            // record changes to the battery level and the most interesting states.
+            // Once we've reached the maximum maximum number of items, we only
+            // record changes to the battery level.
+            if (mHistoryLastWritten.batteryLevel == mHistoryCur.batteryLevel &&
+                    (dataSize >= MAX_MAX_HISTORY_BUFFER
+                            || ((mHistoryEnd.states^mHistoryCur.states)
+                                    & HistoryItem.MOST_INTERESTING_STATES) == 0)) {
+                return;
+            }
+        }
+
+        addHistoryBufferLocked(curTime, HistoryItem.CMD_UPDATE);
+    }
+
+    void addHistoryBufferLocked(long curTime, byte cmd) {
+        int origPos = 0;
+        if (mIteratingHistory) {
+            origPos = mHistoryBuffer.dataPosition();
+            mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize());
+        }
+        mHistoryBufferLastPos = mHistoryBuffer.dataPosition();
+        mHistoryLastLastWritten.setTo(mHistoryLastWritten);
+        mHistoryLastWritten.setTo(mHistoryBaseTime + curTime, cmd, mHistoryCur);
+        mHistoryLastWritten.writeDelta(mHistoryBuffer, mHistoryLastLastWritten);
+        mLastHistoryTime = curTime;
+        if (DEBUG_HISTORY) Slog.i(TAG, "Writing history buffer: was " + mHistoryBufferLastPos
+                + " now " + mHistoryBuffer.dataPosition()
+                + " size is now " + mHistoryBuffer.dataSize());
+        if (mIteratingHistory) {
+            mHistoryBuffer.setDataPosition(origPos);
+        }
+    }
+
     int mChangedStates = 0;
 
     void addHistoryRecordLocked(long curTime) {
+        addHistoryBufferLocked(curTime);
+
         if (!mHaveBatteryLevel || !mRecordingHistory) {
             return;
         }
@@ -1206,6 +1298,7 @@
             // If the current is the same as the one before, then we no
             // longer need the entry.
             if (mHistoryLastEnd != null && mHistoryLastEnd.cmd == HistoryItem.CMD_UPDATE
+                    && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+500)
                     && mHistoryLastEnd.same(mHistoryCur)) {
                 mHistoryLastEnd.next = null;
                 mHistoryEnd.next = mHistoryCache;
@@ -1268,6 +1361,7 @@
     }
 
     void clearHistoryLocked() {
+        if (DEBUG_HISTORY) Slog.i(TAG, "********** CLEARING HISTORY!");
         if (mHistory != null) {
             mHistoryEnd.next = mHistoryCache;
             mHistoryCache = mHistory;
@@ -1275,6 +1369,15 @@
         }
         mNumHistoryItems = 0;
         mHistoryBaseTime = 0;
+        mLastHistoryTime = 0;
+
+        mHistoryBuffer.setDataSize(0);
+        mHistoryBuffer.setDataPosition(0);
+        mHistoryBuffer.setDataCapacity(MAX_HISTORY_BUFFER/2);
+        mHistoryLastLastWritten.cmd = HistoryItem.CMD_NULL;
+        mHistoryLastWritten.cmd = HistoryItem.CMD_NULL;
+        mHistoryBufferLastPos = -1;
+        mHistoryOverflow = false;
     }
 
     public void doUnplugLocked(long batteryUptime, long batteryRealtime) {
@@ -3910,11 +4013,13 @@
         mDischargeUnplugLevel = 0;
         mDischargeCurrentLevel = 0;
         initDischarge();
+        clearHistoryLocked();
     }
 
     public BatteryStatsImpl(Parcel p) {
         mFile = null;
         mHandler = null;
+        clearHistoryLocked();
         readFromParcel(p);
     }
 
@@ -3932,25 +4037,84 @@
         }
     }
 
-    private HistoryItem mHistoryIterator;
-
-    public boolean startIteratingHistoryLocked() {
+    @Override
+    public boolean startIteratingOldHistoryLocked() {
+        if (DEBUG_HISTORY) Slog.i(TAG, "ITERATING: buff size=" + mHistoryBuffer.dataSize()
+                + " pos=" + mHistoryBuffer.dataPosition());
+        mHistoryBuffer.setDataPosition(0);
+        mHistoryReadTmp.clear();
+        mReadOverflow = false;
+        mIteratingHistory = true;
         return (mHistoryIterator = mHistory) != null;
     }
 
-    public boolean getNextHistoryLocked(HistoryItem out) {
+    @Override
+    public boolean getNextOldHistoryLocked(HistoryItem out) {
+        boolean end = mHistoryBuffer.dataPosition() >= mHistoryBuffer.dataSize();
+        if (!end) {
+            mHistoryReadTmp.readDelta(mHistoryBuffer);
+            mReadOverflow |= mHistoryReadTmp.cmd == HistoryItem.CMD_OVERFLOW;
+        }
         HistoryItem cur = mHistoryIterator;
         if (cur == null) {
+            if (!mReadOverflow && !end) {
+                Slog.w(TAG, "Old history ends before new history!");
+            }
             return false;
         }
         out.setTo(cur);
         mHistoryIterator = cur.next;
+        if (!mReadOverflow) {
+            if (end) {
+                Slog.w(TAG, "New history ends before old history!");
+            } else if (!out.same(mHistoryReadTmp)) {
+                long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
+                PrintWriter pw = new PrintWriter(new LogWriter(android.util.Log.WARN, TAG));
+                pw.println("Histories differ!");
+                pw.println("Old history:");
+                (new HistoryPrinter()).printNextItem(pw, out, now);
+                pw.println("New history:");
+                (new HistoryPrinter()).printNextItem(pw, mHistoryReadTmp, now);
+            }
+        }
         return true;
     }
 
     @Override
-    public HistoryItem getHistory() {
-        return mHistory;
+    public void finishIteratingOldHistoryLocked() {
+        mIteratingHistory = false;
+        mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize());
+    }
+
+    @Override
+    public boolean startIteratingHistoryLocked() {
+        if (DEBUG_HISTORY) Slog.i(TAG, "ITERATING: buff size=" + mHistoryBuffer.dataSize()
+                + " pos=" + mHistoryBuffer.dataPosition());
+        mHistoryBuffer.setDataPosition(0);
+        mReadOverflow = false;
+        mIteratingHistory = true;
+        return mHistoryBuffer.dataSize() > 0;
+    }
+
+    @Override
+    public boolean getNextHistoryLocked(HistoryItem out) {
+        final int pos = mHistoryBuffer.dataPosition();
+        if (pos == 0) {
+            out.clear();
+        }
+        boolean end = pos >= mHistoryBuffer.dataSize();
+        if (end) {
+            return false;
+        }
+
+        out.readDelta(mHistoryBuffer);
+        return true;
+    }
+
+    @Override
+    public void finishIteratingHistoryLocked() {
+        mIteratingHistory = false;
+        mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize());
     }
 
     @Override
@@ -4697,7 +4861,9 @@
             Slog.e("BatteryStats", "Error reading battery statistics", e);
         }
 
-        addHistoryRecordLocked(SystemClock.elapsedRealtime(), HistoryItem.CMD_START);
+        long now = SystemClock.elapsedRealtime();
+        addHistoryRecordLocked(now, HistoryItem.CMD_START);
+        addHistoryBufferLocked(now, HistoryItem.CMD_START);
     }
 
     public int describeContents() {
@@ -4705,30 +4871,54 @@
     }
 
     void readHistory(Parcel in) {
-        mHistory = mHistoryEnd = mHistoryCache = null;
-        mHistoryBaseTime = 0;
-        long time;
-        while ((time=in.readLong()) >= 0) {
-            HistoryItem rec = new HistoryItem(time, in);
-            addHistoryRecordLocked(rec);
-            if (rec.time > mHistoryBaseTime) {
-                mHistoryBaseTime = rec.time;
-            }
+        mHistoryBaseTime = in.readLong();
+
+        mHistoryBuffer.setDataSize(0);
+        mHistoryBuffer.setDataPosition(0);
+
+        int bufSize = in.readInt();
+        int curPos = in.dataPosition();
+        if (bufSize >= (MAX_MAX_HISTORY_BUFFER*3)) {
+            Slog.w(TAG, "File corrupt: history data buffer too large " + bufSize);
+        } else if ((bufSize&~3) != bufSize) {
+            Slog.w(TAG, "File corrupt: history data buffer not aligned " + bufSize);
+        } else {
+            if (DEBUG_HISTORY) Slog.i(TAG, "***************** READING NEW HISTORY: " + bufSize
+                    + " bytes at " + curPos);
+            mHistoryBuffer.appendFrom(in, curPos, bufSize);
+            in.setDataPosition(curPos + bufSize);
         }
 
-        long oldnow = SystemClock.elapsedRealtime() - (5*60*100);
+        long oldnow = SystemClock.elapsedRealtime() - (5*60*1000);
         if (oldnow > 0) {
             // If the system process has restarted, but not the entire
             // system, then the mHistoryBaseTime already accounts for
             // much of the elapsed time.  We thus want to adjust it back,
             // to avoid large gaps in the data.  We determine we are
             // in this case by arbitrarily saying it is so if at this
-            // point in boot the elapsed time is already more than 5 seconds.
+            // point in boot the elapsed time is already more than 5 minutes.
             mHistoryBaseTime -= oldnow;
         }
     }
 
+    void readOldHistory(Parcel in) {
+        mHistory = mHistoryEnd = mHistoryCache = null;
+        long time;
+        while ((time=in.readLong()) >= 0) {
+            HistoryItem rec = new HistoryItem(time, in);
+            addHistoryRecordLocked(rec);
+        }
+    }
+
     void writeHistory(Parcel out) {
+        out.writeLong(mLastHistoryTime);
+        out.writeInt(mHistoryBuffer.dataSize());
+        if (DEBUG_HISTORY) Slog.i(TAG, "***************** WRITING HISTORY: "
+                + mHistoryBuffer.dataSize() + " bytes at " + out.dataPosition());
+        out.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize());
+    }
+
+    void writeOldHistory(Parcel out) {
         HistoryItem rec = mHistory;
         while (rec != null) {
             if (rec.time >= 0) rec.writeToParcel(out, 0);
@@ -4746,6 +4936,7 @@
         }
 
         readHistory(in);
+        readOldHistory(in);
 
         mStartCount = in.readInt();
         mBatteryUptime = in.readLong();
@@ -4935,6 +5126,9 @@
      * @param out the Parcel to be written to.
      */
     public void writeSummaryToParcel(Parcel out) {
+        // Need to update with current kernel wake lock counts.
+        updateKernelWakelocksLocked();
+
         final long NOW_SYS = SystemClock.uptimeMillis() * 1000;
         final long NOWREAL_SYS = SystemClock.elapsedRealtime() * 1000;
         final long NOW = getBatteryUptimeLocked(NOW_SYS);
@@ -4943,6 +5137,7 @@
         out.writeInt(VERSION);
 
         writeHistory(out);
+        writeOldHistory(out);
 
         out.writeInt(mStartCount);
         out.writeLong(computeBatteryUptime(NOW_SYS, STATS_SINCE_CHARGED));
@@ -5256,6 +5451,9 @@
 
     @SuppressWarnings("unused")
     void writeToParcelLocked(Parcel out, boolean inclUids, int flags) {
+        // Need to update with current kernel wake lock counts.
+        updateKernelWakelocksLocked();
+
         final long uSecUptime = SystemClock.uptimeMillis() * 1000;
         final long uSecRealtime = SystemClock.elapsedRealtime() * 1000;
         final long batteryUptime = getBatteryUptimeLocked(uSecUptime);
@@ -5358,6 +5556,11 @@
         }
     };
 
+    public void prepareForDumpLocked() {
+        // Need to retrieve current kernel wake lock stats before printing.
+        updateKernelWakelocksLocked();
+    }
+
     public void dumpLocked(PrintWriter pw) {
         if (DEBUG) {
             Printer pr = new PrintWriterPrinter(pw);
diff --git a/core/java/com/android/internal/util/HierarchicalState.java b/core/java/com/android/internal/util/IState.java
similarity index 61%
rename from core/java/com/android/internal/util/HierarchicalState.java
rename to core/java/com/android/internal/util/IState.java
index b37f46c..056f8e9 100644
--- a/core/java/com/android/internal/util/HierarchicalState.java
+++ b/core/java/com/android/internal/util/IState.java
@@ -1,5 +1,5 @@
 /**
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2011 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -21,21 +21,29 @@
 /**
  * {@hide}
  *
- * The class for implementing states in a HierarchicalStateMachine
+ * The interface for implementing states in a {@link StateMachine}
  */
-public class HierarchicalState {
+public interface IState {
 
     /**
-     * Constructor
+     * Returned by processMessage to indicate the the message was processed.
      */
-    protected HierarchicalState() {
-    }
+    static final boolean HANDLED = true;
+
+    /**
+     * Returned by processMessage to indicate the the message was NOT processed.
+     */
+    static final boolean NOT_HANDLED = false;
 
     /**
      * Called when a state is entered.
      */
-    protected void enter() {
-    }
+    void enter();
+
+    /**
+     * Called when a state is exited.
+     */
+    void exit();
 
     /**
      * Called when a message is to be processed by the
@@ -49,28 +57,15 @@
      * be processed until this routine returns.
      *
      * @param msg to process
-     * @return true if processing has completed and false
-     *         if the parent state's processMessage should
-     *         be invoked.
+     * @return HANDLED if processing has completed and NOT_HANDLED
+     *         if the message wasn't processed.
      */
-    protected boolean processMessage(Message msg) {
-        return false;
-    }
+    boolean processMessage(Message msg);
 
     /**
-     * Called when a state is exited.
+     * Name of State for debugging purposes.
+     *
+     * @return name of state.
      */
-    protected void exit() {
-    }
-
-    /**
-     * @return name of state, but default returns the states
-     * class name. An instance name would be better but requiring
-     * it seems unnecessary.
-     */
-    public String getName() {
-        String name = getClass().getName();
-        int lastDollar = name.lastIndexOf('$');
-        return name.substring(lastDollar + 1);
-    }
+    String getName();
 }
diff --git a/core/java/com/android/internal/util/ProcessedMessages.java b/core/java/com/android/internal/util/ProcessedMessages.java
deleted file mode 100644
index 244474e..0000000
--- a/core/java/com/android/internal/util/ProcessedMessages.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/**
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.util;
-
-import android.os.Message;
-
-import java.util.Vector;
-
-/**
- * {@hide}
- *
- * A list of messages recently processed by the state machine.
- *
- * The class maintains a list of messages that have been most
- * recently processed. The list is finite and may be set in the
- * constructor or by calling setSize. The public interface also
- * includes size which returns the number of recent messages,
- * count which is the number of message processed since the
- * the last setSize, get which returns a processed message and
- * add which adds a processed messaged.
- */
-public class ProcessedMessages {
-
-    public static final int DEFAULT_SIZE = 20;
-
-    /**
-     * The information maintained for a processed message.
-     */
-    public class Info {
-        private int what;
-        private HierarchicalState state;
-        private HierarchicalState orgState;
-
-        /**
-         * Constructor
-         * @param message
-         * @param state that handled the message
-         * @param orgState is the first state the received the message but
-         * did not processes the message.
-         */
-        Info(Message message, HierarchicalState state, HierarchicalState orgState) {
-            update(message, state, orgState);
-        }
-
-        /**
-         * Update the information in the record.
-         * @param state that handled the message
-         * @param orgState is the first state the received the message but
-         * did not processes the message.
-         */
-        public void update(Message message, HierarchicalState state, HierarchicalState orgState) {
-            this.what = message.what;
-            this.state = state;
-            this.orgState = orgState;
-        }
-
-        /**
-         * @return the command that was executing
-         */
-        public int getWhat() {
-            return what;
-        }
-
-        /**
-         * @return the state that handled this message
-         */
-        public HierarchicalState getState() {
-            return state;
-        }
-
-        /**
-         * @return the original state that received the message.
-         */
-        public HierarchicalState getOriginalState() {
-            return orgState;
-        }
-
-        /**
-         * @return as string
-         */
-        public String toString() {
-            StringBuilder sb = new StringBuilder();
-            sb.append("what=");
-            sb.append(what);
-            sb.append(" state=");
-            sb.append(cn(state));
-            sb.append(" orgState=");
-            sb.append(cn(orgState));
-            return sb.toString();
-        }
-
-        /**
-         * @return an objects class name
-         */
-        private String cn(Object n) {
-            if (n == null) {
-                return "null";
-            } else {
-                String name = n.getClass().getName();
-                int lastDollar = name.lastIndexOf('$');
-                return name.substring(lastDollar + 1);
-            }
-        }
-    }
-
-    private Vector<Info> mMessages = new Vector<Info>();
-    private int mMaxSize = DEFAULT_SIZE;
-    private int mOldestIndex = 0;
-    private int mCount = 0;
-
-    /**
-     * Constructor
-     */
-    ProcessedMessages() {
-    }
-
-    ProcessedMessages(int maxSize) {
-        setSize(maxSize);
-    }
-
-    /**
-     * Set size of messages to maintain and clears all current messages.
-     *
-     * @param maxSize number of messages to maintain at anyone time.
-    */
-    void setSize(int maxSize) {
-        mMaxSize = maxSize;
-        mCount = 0;
-        mMessages.clear();
-    }
-
-    /**
-     * @return the number of recent messages.
-     */
-    int size() {
-        return mMessages.size();
-    }
-
-    /**
-     * @return the total number of messages processed since size was set.
-     */
-    int count() {
-        return mCount;
-    }
-
-    /**
-     * @return the information on a particular record. 0 is the oldest
-     * record and size()-1 is the newest record. If the index is to
-     * large null is returned.
-     */
-    Info get(int index) {
-        int nextIndex = mOldestIndex + index;
-        if (nextIndex >= mMaxSize) {
-            nextIndex -= mMaxSize;
-        }
-        if (nextIndex >= size()) {
-            return null;
-        } else {
-            return mMessages.get(nextIndex);
-        }
-    }
-
-    /**
-     * Add a processed message.
-     *
-     * @param message
-     * @param state that handled the message
-     * @param orgState is the first state the received the message but
-     * did not processes the message.
-     */
-    void add(Message message, HierarchicalState state, HierarchicalState orgState) {
-        mCount += 1;
-        if (mMessages.size() < mMaxSize) {
-            mMessages.add(new Info(message, state, orgState));
-        } else {
-            Info info = mMessages.get(mOldestIndex);
-            mOldestIndex += 1;
-            if (mOldestIndex >= mMaxSize) {
-                mOldestIndex = 0;
-            }
-            info.update(message, state, orgState);
-        }
-    }
-}
diff --git a/core/java/com/android/internal/util/State.java b/core/java/com/android/internal/util/State.java
new file mode 100644
index 0000000..3eadff5
--- /dev/null
+++ b/core/java/com/android/internal/util/State.java
@@ -0,0 +1,74 @@
+/**
+ * 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 com.android.internal.util;
+
+import android.os.Message;
+
+/**
+ * {@hide}
+ *
+ * The class for implementing states in a StateMachine
+ */
+public class State implements IState {
+
+    /**
+     * Constructor
+     */
+    protected State() {
+    }
+
+    /* (non-Javadoc)
+     * @see com.android.internal.util.IState#enter()
+     */
+    @Override
+    public void enter() {
+    }
+
+    /* (non-Javadoc)
+     * @see com.android.internal.util.IState#exit()
+     */
+    @Override
+    public void exit() {
+    }
+
+    /* (non-Javadoc)
+     * @see com.android.internal.util.IState#processMessage(android.os.Message)
+     */
+    @Override
+    public boolean processMessage(Message msg) {
+        return false;
+    }
+
+    /**
+     * Name of State for debugging purposes.
+     *
+     * This default implementation returns the class name, returning
+     * the instance name would better in cases where a State class
+     * is used for multiple states. But normally there is one class per
+     * state and the class name is sufficient and easy to get. You may
+     * want to provide a setName or some other mechanism for setting
+     * another name if the class name is not appropriate.
+     *
+     * @see com.android.internal.util.IState#processMessage(android.os.Message)
+     */
+    @Override
+    public String getName() {
+        String name = getClass().getName();
+        int lastDollar = name.lastIndexOf('$');
+        return name.substring(lastDollar + 1);
+    }
+}
diff --git a/core/java/com/android/internal/util/HierarchicalStateMachine.java b/core/java/com/android/internal/util/StateMachine.java
similarity index 76%
rename from core/java/com/android/internal/util/HierarchicalStateMachine.java
rename to core/java/com/android/internal/util/StateMachine.java
index f43f459..cbe72dd 100644
--- a/core/java/com/android/internal/util/HierarchicalStateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -24,14 +24,15 @@
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Vector;
 
 /**
  * {@hide}
  *
- * <p>A hierarchical state machine is a state machine which processes messages
+ * <p>The state machine defined here is a hierarchical state machine which processes messages
  * and can have states arranged hierarchically.</p>
  * 
- * <p>A state is a <code>HierarchicalState</code> object and must implement
+ * <p>A state is a <code>State</code> object and must implement
  * <code>processMessage</code> and optionally <code>enter/exit/getName</code>.
  * The enter/exit methods are equivalent to the construction and destruction
  * in Object Oriented programming and are used to perform initialization and
@@ -75,7 +76,7 @@
  * will exit the current state and its parent and then exit from the controlling thread
  * and no further messages will be processed.</p>
  *
- * <p>In addition to <code>processMessage</code> each <code>HierarchicalState</code> has
+ * <p>In addition to <code>processMessage</code> each <code>State</code> has
  * an <code>enter</code> method and <code>exit</exit> method which may be overridden.</p>
  *
  * <p>Since the states are arranged in a hierarchy transitioning to a new state
@@ -121,11 +122,11 @@
  * mS4.enter. The new list of active states is mP0, mP1, mS2 and mS4. So
  * when the next message is received mS4.processMessage will be invoked.</p>
  *
- * <p>Now for some concrete examples, here is the canonical HelloWorld as an HSM.
+ * <p>Now for some concrete examples, here is the canonical HelloWorld as a state machine.
  * It responds with "Hello World" being printed to the log for every message.</p>
 <code>
-class HelloWorld extends HierarchicalStateMachine {
-    Hsm1(String name) {
+class HelloWorld extends StateMachine {
+    HelloWorld(String name) {
         super(name);
         addState(mState1);
         setInitialState(mState1);
@@ -137,7 +138,7 @@
         return hw;
     }
 
-    class State1 extends HierarchicalState {
+    class State1 extends State {
         &#64;Override public boolean processMessage(Message message) {
             Log.d(TAG, "Hello World");
             return HANDLED;
@@ -220,9 +221,9 @@
      }
 }
 </code>
- * <p>The implementation is below and also in HierarchicalStateMachineTest:</p>
+ * <p>The implementation is below and also in StateMachineTest:</p>
 <code>
-class Hsm1 extends HierarchicalStateMachine {
+class Hsm1 extends StateMachine {
     private static final String TAG = "hsm1";
 
     public static final int CMD_1 = 1;
@@ -254,7 +255,7 @@
         Log.d(TAG, "ctor X");
     }
 
-    class P1 extends HierarchicalState {
+    class P1 extends State {
         &#64;Override public void enter() {
             Log.d(TAG, "mP1.enter");
         }
@@ -281,7 +282,7 @@
         }
     }
 
-    class S1 extends HierarchicalState {
+    class S1 extends State {
         &#64;Override public void enter() {
             Log.d(TAG, "mS1.enter");
         }
@@ -301,7 +302,7 @@
         }
     }
 
-    class S2 extends HierarchicalState {
+    class S2 extends State {
         &#64;Override public void enter() {
             Log.d(TAG, "mS2.enter");
         }
@@ -329,7 +330,7 @@
         }
     }
 
-    class P2 extends HierarchicalState {
+    class P2 extends State {
         &#64;Override public void enter() {
             Log.d(TAG, "mP2.enter");
             sendMessage(obtainMessage(CMD_5));
@@ -408,16 +409,16 @@
 D/hsm1    ( 1999): halting
 </code>
  */
-public class HierarchicalStateMachine {
+public class StateMachine {
 
-    private static final String TAG = "HierarchicalStateMachine";
+    private static final String TAG = "StateMachine";
     private String mName;
 
     /** Message.what value when quitting */
-    public static final int HSM_QUIT_CMD = -1;
+    public static final int SM_QUIT_CMD = -1;
 
     /** Message.what value when initializing */
-    public static final int HSM_INIT_CMD = -1;
+    public static final int SM_INIT_CMD = -1;
 
     /**
      * Convenience constant that maybe returned by processMessage
@@ -433,7 +434,181 @@
      */
     public static final boolean NOT_HANDLED = false;
 
-    private static class HsmHandler extends Handler {
+    /**
+     * {@hide}
+     *
+     * The information maintained for a processed message.
+     */
+    public static class ProcessedMessageInfo {
+        private int what;
+        private State state;
+        private State orgState;
+
+        /**
+         * Constructor
+         * @param message
+         * @param state that handled the message
+         * @param orgState is the first state the received the message but
+         * did not processes the message.
+         */
+        ProcessedMessageInfo(Message message, State state, State orgState) {
+            update(message, state, orgState);
+        }
+
+        /**
+         * Update the information in the record.
+         * @param state that handled the message
+         * @param orgState is the first state the received the message but
+         * did not processes the message.
+         */
+        public void update(Message message, State state, State orgState) {
+            this.what = message.what;
+            this.state = state;
+            this.orgState = orgState;
+        }
+
+        /**
+         * @return the command that was executing
+         */
+        public int getWhat() {
+            return what;
+        }
+
+        /**
+         * @return the state that handled this message
+         */
+        public State getState() {
+            return state;
+        }
+
+        /**
+         * @return the original state that received the message.
+         */
+        public State getOriginalState() {
+            return orgState;
+        }
+
+        /**
+         * @return as string
+         */
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("what=");
+            sb.append(what);
+            sb.append(" state=");
+            sb.append(cn(state));
+            sb.append(" orgState=");
+            sb.append(cn(orgState));
+            return sb.toString();
+        }
+
+        /**
+         * @return an objects class name
+         */
+        private String cn(Object n) {
+            if (n == null) {
+                return "null";
+            } else {
+                String name = n.getClass().getName();
+                int lastDollar = name.lastIndexOf('$');
+                return name.substring(lastDollar + 1);
+            }
+        }
+    }
+
+    /**
+     * A list of messages recently processed by the state machine.
+     *
+     * The class maintains a list of messages that have been most
+     * recently processed. The list is finite and may be set in the
+     * constructor or by calling setSize. The public interface also
+     * includes size which returns the number of recent messages,
+     * count which is the number of message processed since the
+     * the last setSize, get which returns a processed message and
+     * add which adds a processed messaged.
+     */
+    private static class ProcessedMessages {
+
+        private static final int DEFAULT_SIZE = 20;
+
+        private Vector<ProcessedMessageInfo> mMessages = new Vector<ProcessedMessageInfo>();
+        private int mMaxSize = DEFAULT_SIZE;
+        private int mOldestIndex = 0;
+        private int mCount = 0;
+
+        /**
+         * Constructor
+         */
+        ProcessedMessages() {
+        }
+
+        /**
+         * Set size of messages to maintain and clears all current messages.
+         *
+         * @param maxSize number of messages to maintain at anyone time.
+        */
+        void setSize(int maxSize) {
+            mMaxSize = maxSize;
+            mCount = 0;
+            mMessages.clear();
+        }
+
+        /**
+         * @return the number of recent messages.
+         */
+        int size() {
+            return mMessages.size();
+        }
+
+        /**
+         * @return the total number of messages processed since size was set.
+         */
+        int count() {
+            return mCount;
+        }
+
+        /**
+         * @return the information on a particular record. 0 is the oldest
+         * record and size()-1 is the newest record. If the index is to
+         * large null is returned.
+         */
+        ProcessedMessageInfo get(int index) {
+            int nextIndex = mOldestIndex + index;
+            if (nextIndex >= mMaxSize) {
+                nextIndex -= mMaxSize;
+            }
+            if (nextIndex >= size()) {
+                return null;
+            } else {
+                return mMessages.get(nextIndex);
+            }
+        }
+
+        /**
+         * Add a processed message.
+         *
+         * @param message
+         * @param state that handled the message
+         * @param orgState is the first state the received the message but
+         * did not processes the message.
+         */
+        void add(Message message, State state, State orgState) {
+            mCount += 1;
+            if (mMessages.size() < mMaxSize) {
+                mMessages.add(new ProcessedMessageInfo(message, state, orgState));
+            } else {
+                ProcessedMessageInfo pmi = mMessages.get(mOldestIndex);
+                mOldestIndex += 1;
+                if (mOldestIndex >= mMaxSize) {
+                    mOldestIndex = 0;
+                }
+                pmi.update(message, state, orgState);
+            }
+        }
+    }
+
+    private static class SmHandler extends Handler {
 
         /** The debug flag */
         private boolean mDbg = false;
@@ -441,9 +616,6 @@
         /** The quit object */
         private static final Object mQuitObj = new Object();
 
-        /** The initialization message */
-        private static final Message mInitMsg = null;
-
         /** The current message */
         private Message mMsg;
 
@@ -471,8 +643,8 @@
         /** State used when state machine is quitting */
         private QuittingState mQuittingState = new QuittingState();
 
-        /** Reference to the HierarchicalStateMachine */
-        private HierarchicalStateMachine mHsm;
+        /** Reference to the StateMachine */
+        private StateMachine mSm;
 
         /**
          * Information about a state.
@@ -480,7 +652,7 @@
          */
         private class StateInfo {
             /** The state */
-            HierarchicalState state;
+            State state;
 
             /** The parent of this state, null if there is no parent */
             StateInfo parentStateInfo;
@@ -500,14 +672,14 @@
         }
 
         /** The map of all of the states in the state machine */
-        private HashMap<HierarchicalState, StateInfo> mStateInfo =
-            new HashMap<HierarchicalState, StateInfo>();
+        private HashMap<State, StateInfo> mStateInfo =
+            new HashMap<State, StateInfo>();
 
         /** The initial state that will process the first message */
-        private HierarchicalState mInitialState;
+        private State mInitialState;
 
         /** The destination state when transitionTo has been invoked */
-        private HierarchicalState mDestState;
+        private State mDestState;
 
         /** The list of deferred messages */
         private ArrayList<Message> mDeferredMessages = new ArrayList<Message>();
@@ -515,10 +687,10 @@
         /**
          * State entered when transitionToHaltingState is called.
          */
-        private class HaltingState extends HierarchicalState {
+        private class HaltingState extends State {
             @Override
             public boolean processMessage(Message msg) {
-                mHsm.haltedProcessMessage(msg);
+                mSm.haltedProcessMessage(msg);
                 return true;
             }
         }
@@ -526,7 +698,7 @@
         /**
          * State entered when a valid quit message is handled.
          */
-        private class QuittingState extends HierarchicalState {
+        private class QuittingState extends State {
             @Override
             public boolean processMessage(Message msg) {
                 return NOT_HANDLED;
@@ -573,7 +745,7 @@
              * the appropriate states. We loop on this to allow
              * enter and exit methods to use transitionTo.
              */
-            HierarchicalState destState = null;
+            State destState = null;
             while (mDestState != null) {
                 if (mDbg) Log.d(TAG, "handleMessage: new destination call exit");
 
@@ -613,10 +785,11 @@
                     /**
                      * We are quitting so ignore all messages.
                      */
-                    mHsm.quitting();
-                    if (mHsm.mHsmThread != null) {
-                        // If we made the thread then quit looper
+                    mSm.quitting();
+                    if (mSm.mSmThread != null) {
+                        // If we made the thread then quit looper which stops the thread.
                         getLooper().quit();
+                        mSm.mSmThread = null;
                     }
                 } else if (destState == mHaltingState) {
                     /**
@@ -624,7 +797,7 @@
                      * state. All subsequent messages will be processed in
                      * in the halting state which invokes haltedProcessMessage(msg);
                      */
-                    mHsm.halting();
+                    mSm.halting();
                 }
             }
         }
@@ -660,7 +833,7 @@
              * starting at the first entry.
              */
             mIsConstructionCompleted = true;
-            mMsg = obtainMessage(HSM_INIT_CMD);
+            mMsg = obtainMessage(SM_INIT_CMD);
             invokeEnterMethods(0);
 
             /**
@@ -690,7 +863,7 @@
                     /**
                      * No parents left so it's not handled
                      */
-                    mHsm.unhandledMessage(msg);
+                    mSm.unhandledMessage(msg);
                     if (isQuit(msg)) {
                         transitionTo(mQuittingState);
                     }
@@ -705,7 +878,7 @@
              * Record that we processed the message
              */
             if (curStateInfo != null) {
-                HierarchicalState orgState = mStateStack[mStateStackTopIndex].state;
+                State orgState = mStateStack[mStateStackTopIndex].state;
                 mProcessedMessages.add(msg, curStateInfo.state, orgState);
             } else {
                 mProcessedMessages.add(msg, null, null);
@@ -719,7 +892,7 @@
         private final void invokeExitMethods(StateInfo commonStateInfo) {
             while ((mStateStackTopIndex >= 0) &&
                     (mStateStack[mStateStackTopIndex] != commonStateInfo)) {
-                HierarchicalState curState = mStateStack[mStateStackTopIndex].state;
+                State curState = mStateStack[mStateStackTopIndex].state;
                 if (mDbg) Log.d(TAG, "invokeExitMethods: " + curState.getName());
                 curState.exit();
                 mStateStack[mStateStackTopIndex].active = false;
@@ -761,7 +934,7 @@
          * reversing the order of the items on the temporary stack as
          * they are moved.
          *
-         * @return index into mStateState where entering needs to start
+         * @return index into mStateStack where entering needs to start
          */
         private final int moveTempStateStackToStateStack() {
             int startingIndex = mStateStackTopIndex + 1;
@@ -794,7 +967,7 @@
          * @return StateInfo of the common ancestor for the destState and
          * current state or null if there is no common parent.
          */
-        private final StateInfo setupTempStateStackWithStatesToEnter(HierarchicalState destState) {
+        private final StateInfo setupTempStateStackWithStatesToEnter(State destState) {
             /**
              * Search up the parent list of the destination state for an active
              * state. Use a do while() loop as the destState must always be entered
@@ -846,7 +1019,7 @@
         /**
          * @return current state
          */
-        private final HierarchicalState getCurrentState() {
+        private final IState getCurrentState() {
             return mStateStack[mStateStackTopIndex].state;
         }
 
@@ -859,7 +1032,7 @@
          * @param parent the parent of state
          * @return stateInfo for this state
          */
-        private final StateInfo addState(HierarchicalState state, HierarchicalState parent) {
+        private final StateInfo addState(State state, State parent) {
             if (mDbg) {
                 Log.d(TAG, "addStateInternal: E state=" + state.getName()
                         + ",parent=" + ((parent == null) ? "" : parent.getName()));
@@ -894,29 +1067,29 @@
          * Constructor
          *
          * @param looper for dispatching messages
-         * @param hsm the hierarchical state machine
+         * @param sm the hierarchical state machine
          */
-        private HsmHandler(Looper looper, HierarchicalStateMachine hsm) {
+        private SmHandler(Looper looper, StateMachine sm) {
             super(looper);
-            mHsm = hsm;
+            mSm = sm;
 
             addState(mHaltingState, null);
             addState(mQuittingState, null);
         }
 
-        /** @see HierarchicalStateMachine#setInitialState(HierarchicalState) */
-        private final void setInitialState(HierarchicalState initialState) {
+        /** @see StateMachine#setInitialState(State) */
+        private final void setInitialState(State initialState) {
             if (mDbg) Log.d(TAG, "setInitialState: initialState" + initialState.getName());
             mInitialState = initialState;
         }
 
-        /** @see HierarchicalStateMachine#transitionTo(HierarchicalState) */
-        private final void transitionTo(HierarchicalState destState) {
-            if (mDbg) Log.d(TAG, "StateMachine.transitionTo EX destState" + destState.getName());
-            mDestState = destState;
+        /** @see StateMachine#transitionTo(IState) */
+        private final void transitionTo(IState destState) {
+            mDestState = (State) destState;
+            if (mDbg) Log.d(TAG, "StateMachine.transitionTo EX destState" + mDestState.getName());
         }
 
-        /** @see HierarchicalStateMachine#deferMessage(Message) */
+        /** @see StateMachine#deferMessage(Message) */
         private final void deferMessage(Message msg) {
             if (mDbg) Log.d(TAG, "deferMessage: msg=" + msg.what);
 
@@ -927,51 +1100,51 @@
             mDeferredMessages.add(newMsg);
         }
 
-        /** @see HierarchicalStateMachine#deferMessage(Message) */
+        /** @see StateMachine#deferMessage(Message) */
         private final void quit() {
             if (mDbg) Log.d(TAG, "quit:");
-            sendMessage(obtainMessage(HSM_QUIT_CMD, mQuitObj));
+            sendMessage(obtainMessage(SM_QUIT_CMD, mQuitObj));
         }
 
-        /** @see HierarchicalStateMachine#isQuit(Message) */
+        /** @see StateMachine#isQuit(Message) */
         private final boolean isQuit(Message msg) {
-            return (msg.what == HSM_QUIT_CMD) && (msg.obj == mQuitObj);
+            return (msg.what == SM_QUIT_CMD) && (msg.obj == mQuitObj);
         }
 
-        /** @see HierarchicalStateMachine#isDbg() */
+        /** @see StateMachine#isDbg() */
         private final boolean isDbg() {
             return mDbg;
         }
 
-        /** @see HierarchicalStateMachine#setDbg(boolean) */
+        /** @see StateMachine#setDbg(boolean) */
         private final void setDbg(boolean dbg) {
             mDbg = dbg;
         }
 
-        /** @see HierarchicalStateMachine#setProcessedMessagesSize(int) */
+        /** @see StateMachine#setProcessedMessagesSize(int) */
         private final void setProcessedMessagesSize(int maxSize) {
             mProcessedMessages.setSize(maxSize);
         }
 
-        /** @see HierarchicalStateMachine#getProcessedMessagesSize() */
+        /** @see StateMachine#getProcessedMessagesSize() */
         private final int getProcessedMessagesSize() {
             return mProcessedMessages.size();
         }
 
-        /** @see HierarchicalStateMachine#getProcessedMessagesCount() */
+        /** @see StateMachine#getProcessedMessagesCount() */
         private final int getProcessedMessagesCount() {
             return mProcessedMessages.count();
         }
 
-        /** @see HierarchicalStateMachine#getProcessedMessage(int) */
-        private final ProcessedMessages.Info getProcessedMessage(int index) {
+        /** @see StateMachine#getProcessedMessageInfo(int) */
+        private final ProcessedMessageInfo getProcessedMessageInfo(int index) {
             return mProcessedMessages.get(index);
         }
 
     }
 
-    private HsmHandler mHsmHandler;
-    private HandlerThread mHsmThread;
+    private SmHandler mSmHandler;
+    private HandlerThread mSmThread;
 
     /**
      * Initialize.
@@ -981,28 +1154,28 @@
      */
     private void initStateMachine(String name, Looper looper) {
         mName = name;
-        mHsmHandler = new HsmHandler(looper, this);
+        mSmHandler = new SmHandler(looper, this);
     }
 
     /**
-     * Constructor creates an HSM with its own thread.
+     * Constructor creates a StateMachine with its own thread.
      *
      * @param name of the state machine
      */
-    protected HierarchicalStateMachine(String name) {
-        mHsmThread = new HandlerThread(name);
-        mHsmThread.start();
-        Looper looper = mHsmThread.getLooper();
+    protected StateMachine(String name) {
+        mSmThread = new HandlerThread(name);
+        mSmThread.start();
+        Looper looper = mSmThread.getLooper();
 
         initStateMachine(name, looper);
     }
 
     /**
-     * Constructor creates an HSMStateMachine using the looper.
+     * Constructor creates an StateMachine using the looper.
      *
      * @param name of the state machine
      */
-    protected HierarchicalStateMachine(String name, Looper looper) {
+    protected StateMachine(String name, Looper looper) {
         initStateMachine(name, looper);
     }
 
@@ -1011,30 +1184,30 @@
      * @param state the state to add
      * @param parent the parent of state
      */
-    protected final void addState(HierarchicalState state, HierarchicalState parent) {
-        mHsmHandler.addState(state, parent);
+    protected final void addState(State state, State parent) {
+        mSmHandler.addState(state, parent);
     }
 
     /**
      * @return current message
      */
     protected final Message getCurrentMessage() {
-        return mHsmHandler.getCurrentMessage();
+        return mSmHandler.getCurrentMessage();
     }
 
     /**
      * @return current state
      */
-    protected final HierarchicalState getCurrentState() {
-        return mHsmHandler.getCurrentState();
+    protected final IState getCurrentState() {
+        return mSmHandler.getCurrentState();
     }
 
     /**
      * Add a new state to the state machine, parent will be null
      * @param state to add
      */
-    protected final void addState(HierarchicalState state) {
-        mHsmHandler.addState(state, null);
+    protected final void addState(State state) {
+        mSmHandler.addState(state, null);
     }
 
     /**
@@ -1043,8 +1216,8 @@
      *
      * @param initialState is the state which will receive the first message.
      */
-    protected final void setInitialState(HierarchicalState initialState) {
-        mHsmHandler.setInitialState(initialState);
+    protected final void setInitialState(State initialState) {
+        mSmHandler.setInitialState(initialState);
     }
 
     /**
@@ -1055,8 +1228,8 @@
      *
      * @param destState will be the state that receives the next message.
      */
-    protected final void transitionTo(HierarchicalState destState) {
-        mHsmHandler.transitionTo(destState);
+    protected final void transitionTo(IState destState) {
+        mSmHandler.transitionTo(destState);
     }
 
     /**
@@ -1067,7 +1240,7 @@
      * will be called.
      */
     protected final void transitionToHaltingState() {
-        mHsmHandler.transitionTo(mHsmHandler.mHaltingState);
+        mSmHandler.transitionTo(mSmHandler.mHaltingState);
     }
 
     /**
@@ -1080,7 +1253,7 @@
      * @param msg is deferred until the next transition.
      */
     protected final void deferMessage(Message msg) {
-        mHsmHandler.deferMessage(msg);
+        mSmHandler.deferMessage(msg);
     }
 
 
@@ -1090,9 +1263,7 @@
      * @param msg that couldn't be handled.
      */
     protected void unhandledMessage(Message msg) {
-        if (false) {
-            Log.e(TAG, mName + " - unhandledMessage: msg.what=" + msg.what);
-        }
+        if (mSmHandler.mDbg) Log.e(TAG, mName + " - unhandledMessage: msg.what=" + msg.what);
     }
 
     /**
@@ -1103,16 +1274,18 @@
     }
 
     /**
-     * Called after the message that called transitionToHalting
-     * is called and should be overridden by StateMachine's that
-     * call transitionToHalting.
+     * This will be called once after handling a message that called
+     * transitionToHalting. All subsequent messages will invoke
+     * {@link StateMachine#haltedProcessMessage(Message)}
      */
     protected void halting() {
     }
 
     /**
-     * Called after the quitting message was NOT handled and
-     * just before the quit actually occurs.
+     * This will be called once after a quit message that was NOT handled by
+     * the derived StateMachine. The StateMachine will stop and any subsequent messages will be
+     * ignored. In addition, if this StateMachine created the thread, the thread will
+     * be stopped after this method returns.
      */
     protected void quitting() {
     }
@@ -1130,35 +1303,35 @@
      * @param maxSize number of messages to maintain at anyone time.
      */
     public final void setProcessedMessagesSize(int maxSize) {
-        mHsmHandler.setProcessedMessagesSize(maxSize);
+        mSmHandler.setProcessedMessagesSize(maxSize);
     }
 
     /**
      * @return number of messages processed
      */
     public final int getProcessedMessagesSize() {
-        return mHsmHandler.getProcessedMessagesSize();
+        return mSmHandler.getProcessedMessagesSize();
     }
 
     /**
      * @return the total number of messages processed
      */
     public final int getProcessedMessagesCount() {
-        return mHsmHandler.getProcessedMessagesCount();
+        return mSmHandler.getProcessedMessagesCount();
     }
 
     /**
-     * @return a processed message
+     * @return a processed message information
      */
-    public final ProcessedMessages.Info getProcessedMessage(int index) {
-        return mHsmHandler.getProcessedMessage(index);
+    public final ProcessedMessageInfo getProcessedMessageInfo(int index) {
+        return mSmHandler.getProcessedMessageInfo(index);
     }
 
     /**
      * @return Handler
      */
     public final Handler getHandler() {
-        return mHsmHandler;
+        return mSmHandler;
     }
 
     /**
@@ -1168,7 +1341,7 @@
      */
     public final Message obtainMessage()
     {
-        return Message.obtain(mHsmHandler);
+        return Message.obtain(mSmHandler);
     }
 
     /**
@@ -1178,7 +1351,7 @@
      * @return message
      */
     public final Message obtainMessage(int what) {
-        return Message.obtain(mHsmHandler, what);
+        return Message.obtain(mSmHandler, what);
     }
 
     /**
@@ -1191,7 +1364,7 @@
      */
     public final Message obtainMessage(int what, Object obj)
     {
-        return Message.obtain(mHsmHandler, what, obj);
+        return Message.obtain(mSmHandler, what, obj);
     }
 
     /**
@@ -1205,7 +1378,7 @@
      */
     public final Message obtainMessage(int what, int arg1, int arg2)
     {
-        return Message.obtain(mHsmHandler, what, arg1, arg2);
+        return Message.obtain(mSmHandler, what, arg1, arg2);
     }
 
     /**
@@ -1220,107 +1393,107 @@
      */
     public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
     {
-        return Message.obtain(mHsmHandler, what, arg1, arg2, obj);
+        return Message.obtain(mSmHandler, what, arg1, arg2, obj);
     }
 
     /**
      * Enqueue a message to this state machine.
      */
     public final void sendMessage(int what) {
-        mHsmHandler.sendMessage(obtainMessage(what));
+        mSmHandler.sendMessage(obtainMessage(what));
     }
 
     /**
      * Enqueue a message to this state machine.
      */
     public final void sendMessage(int what, Object obj) {
-        mHsmHandler.sendMessage(obtainMessage(what,obj));
+        mSmHandler.sendMessage(obtainMessage(what,obj));
     }
 
     /**
      * Enqueue a message to this state machine.
      */
     public final void sendMessage(Message msg) {
-        mHsmHandler.sendMessage(msg);
+        mSmHandler.sendMessage(msg);
     }
 
     /**
      * Enqueue a message to this state machine after a delay.
      */
     public final void sendMessageDelayed(int what, long delayMillis) {
-        mHsmHandler.sendMessageDelayed(obtainMessage(what), delayMillis);
+        mSmHandler.sendMessageDelayed(obtainMessage(what), delayMillis);
     }
 
     /**
      * Enqueue a message to this state machine after a delay.
      */
     public final void sendMessageDelayed(int what, Object obj, long delayMillis) {
-        mHsmHandler.sendMessageDelayed(obtainMessage(what, obj), delayMillis);
+        mSmHandler.sendMessageDelayed(obtainMessage(what, obj), delayMillis);
     }
 
     /**
      * Enqueue a message to this state machine after a delay.
      */
     public final void sendMessageDelayed(Message msg, long delayMillis) {
-        mHsmHandler.sendMessageDelayed(msg, delayMillis);
+        mSmHandler.sendMessageDelayed(msg, delayMillis);
     }
 
     /**
      * Enqueue a message to the front of the queue for this state machine.
-     * Protected, may only be called by instances of HierarchicalStateMachine.
+     * Protected, may only be called by instances of StateMachine.
      */
     protected final void sendMessageAtFrontOfQueue(int what, Object obj) {
-        mHsmHandler.sendMessageAtFrontOfQueue(obtainMessage(what, obj));
+        mSmHandler.sendMessageAtFrontOfQueue(obtainMessage(what, obj));
     }
 
     /**
      * Enqueue a message to the front of the queue for this state machine.
-     * Protected, may only be called by instances of HierarchicalStateMachine.
+     * Protected, may only be called by instances of StateMachine.
      */
     protected final void sendMessageAtFrontOfQueue(int what) {
-        mHsmHandler.sendMessageAtFrontOfQueue(obtainMessage(what));
+        mSmHandler.sendMessageAtFrontOfQueue(obtainMessage(what));
     }
 
     /**
      * Enqueue a message to the front of the queue for this state machine.
-     * Protected, may only be called by instances of HierarchicalStateMachine.
+     * Protected, may only be called by instances of StateMachine.
      */
     protected final void sendMessageAtFrontOfQueue(Message msg) {
-        mHsmHandler.sendMessageAtFrontOfQueue(msg);
+        mSmHandler.sendMessageAtFrontOfQueue(msg);
     }
 
     /**
      * Removes a message from the message queue.
-     * Protected, may only be called by instances of HierarchicalStateMachine.
+     * Protected, may only be called by instances of StateMachine.
      */
     protected final void removeMessages(int what) {
-        mHsmHandler.removeMessages(what);
+        mSmHandler.removeMessages(what);
     }
 
     /**
      * Conditionally quit the looper and stop execution.
      *
-     * This sends the HSM_QUIT_MSG to the state machine and
+     * This sends the SM_QUIT_MSG to the state machine and
      * if not handled by any state's processMessage then the
      * state machine will be stopped and no further messages
      * will be processed.
      */
     public final void quit() {
-        mHsmHandler.quit();
+        mSmHandler.quit();
     }
 
     /**
      * @return ture if msg is quit
      */
     protected final boolean isQuit(Message msg) {
-        return mHsmHandler.isQuit(msg);
+        return mSmHandler.isQuit(msg);
     }
 
     /**
      * @return if debugging is enabled
      */
     public boolean isDbg() {
-        return mHsmHandler.isDbg();
+        return mSmHandler.isDbg();
     }
 
     /**
@@ -1329,7 +1502,7 @@
      * @param dbg is true to enable debugging.
      */
     public void setDbg(boolean dbg) {
-        mHsmHandler.setDbg(dbg);
+        mSmHandler.setDbg(dbg);
     }
 
     /**
@@ -1337,6 +1510,6 @@
      */
     public void start() {
         /** Send the complete construction message */
-        mHsmHandler.completeConstruction();
+        mSmHandler.completeConstruction();
     }
 }
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 611d987..4ffa4e1 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -33,6 +33,7 @@
     List<InputMethodInfo> getEnabledInputMethodList();
     List<InputMethodSubtype> getEnabledInputMethodSubtypeList(in InputMethodInfo imi,
             boolean allowsImplicitlySelectedSubtypes);
+    InputMethodSubtype getLastInputMethodSubtype();
     // TODO: We should change the return type from List to List<Parcelable>
     // Currently there is a bug that aidl doesn't accept List<Parcelable>
     List getShortcutInputMethodsAndSubtypes();
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index f29d83e..b628b9dc 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -1,19 +1,18 @@
-/* //device/libs/android_runtime/AndroidRuntime.cpp
-**
-** Copyright 2006, 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.
-*/
+/*
+ * Copyright (C) 2006 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 "AndroidRuntime"
 //#define LOG_NDEBUG 0
@@ -212,7 +211,7 @@
     if (value != NULL && strcmp(value, "true") == 0) {
         return 1;
     }
-    
+
     return 0;
 }
 
@@ -227,7 +226,7 @@
     { "isComputerOn", "()I",
         (void*) com_android_internal_os_RuntimeInit_isComputerOn },
     { "turnComputerOn", "()V",
-        (void*) com_android_internal_os_RuntimeInit_turnComputerOn },    
+        (void*) com_android_internal_os_RuntimeInit_turnComputerOn },
     { "getQwertyKeyboard", "()I",
         (void*) com_android_internal_os_RuntimeInit_getQwertyKeyboard },
 };
@@ -278,51 +277,16 @@
     return jniRegisterNativeMethods(env, className, gMethods, numMethods);
 }
 
-/*
- * Call a static Java Programming Language function that takes no arguments and returns void.
- */
-status_t AndroidRuntime::callStatic(const char* className, const char* methodName)
+status_t AndroidRuntime::callMain(const char* className,
+    jclass clazz, int argc, const char* const argv[])
 {
     JNIEnv* env;
-    jclass clazz;
-    jmethodID methodId;
-
-    env = getJNIEnv();
-    if (env == NULL)
-        return UNKNOWN_ERROR;
-
-    clazz = findClass(env, className);
-    if (clazz == NULL) {
-        LOGE("ERROR: could not find class '%s'\n", className);
-        return UNKNOWN_ERROR;
-    }
-    methodId = env->GetStaticMethodID(clazz, methodName, "()V");
-    if (methodId == NULL) {
-        LOGE("ERROR: could not find method %s.%s\n", className, methodName);
-        return UNKNOWN_ERROR;
-    }
-
-    env->CallStaticVoidMethod(clazz, methodId);
-
-    return NO_ERROR;
-}
-
-status_t AndroidRuntime::callMain(
-    const char* className, int argc, const char* const argv[])
-{
-    JNIEnv* env;
-    jclass clazz;
     jmethodID methodId;
 
     LOGD("Calling main entry %s", className);
 
     env = getJNIEnv();
-    if (env == NULL)
-        return UNKNOWN_ERROR;
-
-    clazz = findClass(env, className);
-    if (clazz == NULL) {
-        LOGE("ERROR: could not find class '%s'\n", className);
+    if (clazz == NULL || env == NULL) {
         return UNKNOWN_ERROR;
     }
 
@@ -352,70 +316,6 @@
 }
 
 /*
- * Find the named class.
- */
-jclass AndroidRuntime::findClass(JNIEnv* env, const char* className)
-{
-    if (env->ExceptionCheck()) {
-        LOGE("ERROR: exception pending on entry to findClass()");
-        return NULL;
-    }
-
-    /*
-     * This is a little awkward because the JNI FindClass call uses the
-     * class loader associated with the native method we're executing in.
-     * Because this native method is part of a "boot" class, JNI doesn't
-     * look for the class in CLASSPATH, which unfortunately is a likely
-     * location for it.  (Had we issued the FindClass call before calling
-     * into the VM -- at which point there isn't a native method frame on
-     * the stack -- the VM would have checked CLASSPATH.  We have to do
-     * this because we call into Java Programming Language code and
-     * bounce back out.)
-     *
-     * JNI lacks a "find class in a specific class loader" operation, so we
-     * have to do things the hard way.
-     */
-    jclass cls = NULL;
-
-    jclass javaLangClassLoader;
-    jmethodID getSystemClassLoader, loadClass;
-    jobject systemClassLoader;
-    jstring strClassName;
-
-    /* find the "system" class loader; none of this is expected to fail */
-    javaLangClassLoader = env->FindClass("java/lang/ClassLoader");
-    assert(javaLangClassLoader != NULL);
-    getSystemClassLoader = env->GetStaticMethodID(javaLangClassLoader,
-        "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
-    loadClass = env->GetMethodID(javaLangClassLoader,
-        "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
-    assert(getSystemClassLoader != NULL && loadClass != NULL);
-    systemClassLoader = env->CallStaticObjectMethod(javaLangClassLoader,
-        getSystemClassLoader);
-    assert(systemClassLoader != NULL);
-
-    /* create an object for the class name string; alloc could fail */
-    strClassName = env->NewStringUTF(className);
-    if (env->ExceptionCheck()) {
-        LOGE("ERROR: unable to convert '%s' to string", className);
-        return NULL;
-    }
-    LOGV("system class loader is %p, loading %p (%s)",
-        systemClassLoader, strClassName, className);
-
-    /* try to find the named class */
-    cls = (jclass) env->CallObjectMethod(systemClassLoader, loadClass,
-                        strClassName);
-    if (env->ExceptionCheck()) {
-        LOGE("ERROR: unable to load class '%s' from %p",
-            className, systemClassLoader);
-        return NULL;
-    }
-
-    return cls;
-}
-
-/*
  * The VM calls this through the "exit" hook.
  */
 static void runtime_exit(int code)
@@ -457,7 +357,7 @@
 int AndroidRuntime::addVmArguments(int argc, const char* const argv[])
 {
     int i;
-    
+
     for (i = 0; i<argc; i++) {
         if (argv[i][0] != '-') {
             return i;
@@ -890,6 +790,17 @@
     return result;
 }
 
+char* AndroidRuntime::toSlashClassName(const char* className)
+{
+    char* result = strdup(className);
+    for (char* cp = result; *cp != '\0'; cp++) {
+        if (*cp == '.') {
+            *cp = '/';
+        }
+    }
+    return result;
+}
+
 /*
  * Start the Android runtime.  This involves starting the virtual machine
  * and calling the "static void main(String[] args)" method in the class
@@ -900,20 +811,16 @@
     LOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n",
             className != NULL ? className : "(unknown)");
 
-    char* slashClassName = NULL;
-    char* cp;
-    JNIEnv* env;
-
     blockSigpipe();
 
-    /* 
-     * 'startSystemServer == true' means runtime is obsolete and not run from 
+    /*
+     * 'startSystemServer == true' means runtime is obsolete and not run from
      * init.rc anymore, so we print out the boot start event here.
      */
     if (startSystemServer) {
         /* track our progress through the boot sequence */
         const int LOG_BOOT_PROGRESS_START = 3000;
-        LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, 
+        LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,
                        ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
     }
 
@@ -922,7 +829,7 @@
         rootDir = "/system";
         if (!hasDir("/system")) {
             LOG_FATAL("No root directory specified, and /android does not exist.");
-            goto bail;
+            return;
         }
         setenv("ANDROID_ROOT", rootDir, 1);
     }
@@ -931,15 +838,18 @@
     //LOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
 
     /* start the virtual machine */
-    if (startVm(&mJavaVM, &env) != 0)
-        goto bail;
+    JNIEnv* env;
+    if (startVm(&mJavaVM, &env) != 0) {
+        return;
+    }
+    onVmCreated(env);
 
     /*
      * Register android functions.
      */
     if (startReg(env) < 0) {
         LOGE("Unable to register all android natives\n");
-        goto bail;
+        return;
     }
 
     /*
@@ -959,7 +869,7 @@
     classNameStr = env->NewStringUTF(className);
     assert(classNameStr != NULL);
     env->SetObjectArrayElement(strArray, 0, classNameStr);
-    startSystemServerStr = env->NewStringUTF(startSystemServer ? 
+    startSystemServerStr = env->NewStringUTF(startSystemServer ?
                                                  "true" : "false");
     env->SetObjectArrayElement(strArray, 1, startSystemServerStr);
 
@@ -967,20 +877,13 @@
      * Start VM.  This thread becomes the main thread of the VM, and will
      * not return until the VM exits.
      */
-    jclass startClass;
-    jmethodID startMeth;
-
-    slashClassName = strdup(className);
-    for (cp = slashClassName; *cp != '\0'; cp++)
-        if (*cp == '.')
-            *cp = '/';
-
-    startClass = env->FindClass(slashClassName);
+    char* slashClassName = toSlashClassName(className);
+    jclass startClass = env->FindClass(slashClassName);
     if (startClass == NULL) {
         LOGE("JavaVM unable to locate class '%s'\n", slashClassName);
         /* keep going */
     } else {
-        startMeth = env->GetStaticMethodID(startClass, "main",
+        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
             "([Ljava/lang/String;)V");
         if (startMeth == NULL) {
             LOGE("JavaVM unable to find main() in '%s'\n", className);
@@ -994,15 +897,13 @@
 #endif
         }
     }
+    free(slashClassName);
 
     LOGD("Shutting down VM\n");
     if (mJavaVM->DetachCurrentThread() != JNI_OK)
         LOGW("Warning: unable to detach main thread\n");
     if (mJavaVM->DestroyJavaVM() != 0)
         LOGW("Warning: VM did not shut down cleanly\n");
-
-bail:
-    free(slashClassName);
 }
 
 void AndroidRuntime::start()
@@ -1017,6 +918,11 @@
     exit(code);
 }
 
+void AndroidRuntime::onVmCreated(JNIEnv* env)
+{
+    // If AndroidRuntime had anything to do here, we'd have done it in 'start'.
+}
+
 /*
  * Get the JNIEnv pointer for this thread.
  *
@@ -1111,7 +1017,7 @@
  * into the VM before it really starts executing.
  */
 /*static*/ int AndroidRuntime::javaCreateThreadEtc(
-                                android_thread_func_t entryFunction, 
+                                android_thread_func_t entryFunction,
                                 void* userData,
                                 const char* threadName,
                                 int32_t threadPriority,
@@ -1299,7 +1205,7 @@
     REG_JNI(register_android_backup_BackupDataOutput),
     REG_JNI(register_android_backup_FileBackupHelperBase),
     REG_JNI(register_android_backup_BackupHelperDispatcher),
-    
+
     REG_JNI(register_android_app_ActivityThread),
     REG_JNI(register_android_app_NativeActivity),
     REG_JNI(register_android_view_InputChannel),
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 05a46a8..1b22e2d 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -655,7 +655,6 @@
 

 #define kClassPathName  "android/graphics/Bitmap"

 

-int register_android_graphics_Bitmap(JNIEnv* env);

 int register_android_graphics_Bitmap(JNIEnv* env)

 {

     return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,

diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 1034fbd..258ffa5 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -12,6 +12,7 @@
 #include "CreateJavaOutputStreamAdaptor.h"
 #include "AutoDecodeCancel.h"
 #include "Utils.h"
+#include "JNIHelp.h"
 
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/Asset.h>
@@ -20,7 +21,6 @@
 #include <sys/mman.h>
 #include <sys/stat.h>
 
-jclass gOptions_class;
 jfieldID gOptions_justBoundsFieldID;
 jfieldID gOptions_sampleSizeFieldID;
 jfieldID gOptions_configFieldID;
@@ -34,12 +34,8 @@
 jfieldID gOptions_mimeFieldID;
 jfieldID gOptions_mCancelID;
 jfieldID gOptions_bitmapFieldID;
-jclass gBitmap_class;
 jfieldID gBitmap_nativeBitmapFieldID;
 
-static jclass gFileDescriptor_class;
-static jfieldID gFileDescriptor_descriptor;
-
 #if 0
     #define TRACE_BITMAP(code)  code
 #else
@@ -66,7 +62,7 @@
         { SkImageDecoder::kPNG_Format,  "image/png" },
         { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" }
     };
-    
+
     const char* cstr = NULL;
     for (size_t i = 0; i < SK_ARRAY_COUNT(gMimeTypes); i++) {
         if (gMimeTypes[i].fFormat == format) {
@@ -127,7 +123,7 @@
                         (allowPurgeable && optionsPurgeable(env, options));
     bool preferQualityOverSpeed = false;
     jobject javaBitmap = NULL;
-    
+
     if (NULL != options) {
         sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
         if (optionsJustBounds(env, options)) {
@@ -137,7 +133,7 @@
         env->SetIntField(options, gOptions_widthFieldID, -1);
         env->SetIntField(options, gOptions_heightFieldID, -1);
         env->SetObjectField(options, gOptions_mimeFieldID, 0);
-        
+
         jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
         prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
         isMutable = env->GetBooleanField(options, gOptions_mutableFieldID);
@@ -151,7 +147,7 @@
     if (NULL == decoder) {
         return nullObjectReturn("SkImageDecoder::Factory returned null");
     }
-    
+
     decoder->setSampleSize(sampleSize);
     decoder->setDitherImage(doDither);
     decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
@@ -298,8 +294,7 @@
                                           jobject bitmapFactoryOptions) {
     NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
 
-    jint descriptor = env->GetIntField(fileDescriptor,
-                                       gFileDescriptor_descriptor);
+    jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
 
     bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions);
     bool isShareable = optionsShareable(env, bitmapFactoryOptions);
@@ -424,7 +419,7 @@
         }
 
         for (int i = 0; i < chunk->numYDivs; i++) {
-            chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);            
+            chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
             if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
                 chunk->yDivs[i]++;
             }
@@ -460,7 +455,7 @@
 }
 
 static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) {
-    jint descriptor = env->GetIntField(fileDescriptor, gFileDescriptor_descriptor);
+    jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
     return ::lseek64(descriptor, 0, SEEK_CUR) != -1 ? JNI_TRUE : JNI_FALSE;
 }
 
@@ -504,12 +499,6 @@
     {   "requestCancel", "()V", (void*)nativeRequestCancel }
 };
 
-static jclass make_globalref(JNIEnv* env, const char classname[]) {
-    jclass c = env->FindClass(classname);
-    SkASSERT(c);
-    return (jclass)env->NewGlobalRef(c);
-}
-
 static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
                                 const char fieldname[], const char type[]) {
     jfieldID id = env->GetFieldID(clazz, fieldname, type);
@@ -517,35 +506,29 @@
     return id;
 }
 
-#define kClassPathName  "android/graphics/BitmapFactory"
-
-#define RETURN_ERR_IF_NULL(value) \
-    do { if (!(value)) { assert(0); return -1; } } while (false)
-
-int register_android_graphics_BitmapFactory(JNIEnv* env);
 int register_android_graphics_BitmapFactory(JNIEnv* env) {
-    gOptions_class = make_globalref(env, "android/graphics/BitmapFactory$Options");
-    gOptions_bitmapFieldID = getFieldIDCheck(env, gOptions_class, "inBitmap",
+    jclass options_class = env->FindClass("android/graphics/BitmapFactory$Options");
+    SkASSERT(options_class);
+    gOptions_bitmapFieldID = getFieldIDCheck(env, options_class, "inBitmap",
         "Landroid/graphics/Bitmap;");
-    gOptions_justBoundsFieldID = getFieldIDCheck(env, gOptions_class, "inJustDecodeBounds", "Z");
-    gOptions_sampleSizeFieldID = getFieldIDCheck(env, gOptions_class, "inSampleSize", "I");
-    gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig",
+    gOptions_justBoundsFieldID = getFieldIDCheck(env, options_class, "inJustDecodeBounds", "Z");
+    gOptions_sampleSizeFieldID = getFieldIDCheck(env, options_class, "inSampleSize", "I");
+    gOptions_configFieldID = getFieldIDCheck(env, options_class, "inPreferredConfig",
             "Landroid/graphics/Bitmap$Config;");
-    gOptions_mutableFieldID = getFieldIDCheck(env, gOptions_class, "inMutable", "Z");
-    gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z");
-    gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z");
-    gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z");
-    gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, gOptions_class,
+    gOptions_mutableFieldID = getFieldIDCheck(env, options_class, "inMutable", "Z");
+    gOptions_ditherFieldID = getFieldIDCheck(env, options_class, "inDither", "Z");
+    gOptions_purgeableFieldID = getFieldIDCheck(env, options_class, "inPurgeable", "Z");
+    gOptions_shareableFieldID = getFieldIDCheck(env, options_class, "inInputShareable", "Z");
+    gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, options_class,
             "inPreferQualityOverSpeed", "Z");
-    gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I");
-    gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I");
-    gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;");
-    gOptions_mCancelID = getFieldIDCheck(env, gOptions_class, "mCancel", "Z");
+    gOptions_widthFieldID = getFieldIDCheck(env, options_class, "outWidth", "I");
+    gOptions_heightFieldID = getFieldIDCheck(env, options_class, "outHeight", "I");
+    gOptions_mimeFieldID = getFieldIDCheck(env, options_class, "outMimeType", "Ljava/lang/String;");
+    gOptions_mCancelID = getFieldIDCheck(env, options_class, "mCancel", "Z");
 
-    gBitmap_class = make_globalref(env, "android/graphics/Bitmap");
-    gBitmap_nativeBitmapFieldID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I");
-    gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor");
-    gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I");
+    jclass bitmap_class = env->FindClass("android/graphics/Bitmap");
+    SkASSERT(bitmap_class);
+    gBitmap_nativeBitmapFieldID = getFieldIDCheck(env, bitmap_class, "mNativeBitmap", "I");
 
     int ret = AndroidRuntime::registerNativeMethods(env,
                                     "android/graphics/BitmapFactory$Options",
@@ -554,6 +537,6 @@
     if (ret) {
         return ret;
     }
-    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
+    return android::AndroidRuntime::registerNativeMethods(env, "android/graphics/BitmapFactory",
                                          gMethods, SK_ARRAY_COUNT(gMethods));
 }
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index ee3e209..d437929 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -28,6 +28,7 @@
 #include "SkBitmapRegionDecoder.h"
 #include "CreateJavaOutputStreamAdaptor.h"
 #include "Utils.h"
+#include "JNIHelp.h"
 
 #include <android_runtime/AndroidRuntime.h>
 #include "android_util_Binder.h"
@@ -39,9 +40,6 @@
 #include <utils/Asset.h>
 #include <sys/stat.h>
 
-static jclass gFileDescriptor_class;
-static jfieldID gFileDescriptor_descriptor;
-
 #if 0
     #define TRACE_BITMAP(code)  code
 #else
@@ -111,8 +109,7 @@
                                           jobject fileDescriptor, jboolean isShareable) {
     NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
 
-    jint descriptor = env->GetIntField(fileDescriptor,
-                                       gFileDescriptor_descriptor);
+    jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
     SkStream *stream = NULL;
     struct stat fdStat;
     int newFD;
@@ -300,25 +297,8 @@
 
 #define kClassPathName  "android/graphics/BitmapRegionDecoder"
 
-static jclass make_globalref(JNIEnv* env, const char classname[]) {
-    jclass c = env->FindClass(classname);
-    SkASSERT(c);
-    return (jclass)env->NewGlobalRef(c);
-}
-
-static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
-                                const char fieldname[], const char type[]) {
-    jfieldID id = env->GetFieldID(clazz, fieldname, type);
-    SkASSERT(id);
-    return id;
-}
-
-int register_android_graphics_BitmapRegionDecoder(JNIEnv* env);
 int register_android_graphics_BitmapRegionDecoder(JNIEnv* env)
 {
-
-    gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor");
-    gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I");
     return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
             gBitmapRegionDecoderMethods, SK_ARRAY_COUNT(gBitmapRegionDecoderMethods));
 }
diff --git a/core/jni/android/graphics/Camera.cpp b/core/jni/android/graphics/Camera.cpp
index 0d715fd..76d415a 100644
--- a/core/jni/android/graphics/Camera.cpp
+++ b/core/jni/android/graphics/Camera.cpp
@@ -98,7 +98,6 @@
     { "dotWithNormal",       "(FFF)F", (void*)Camera_dotWithNormal }
 };
 
-int register_android_graphics_Camera(JNIEnv* env);
 int register_android_graphics_Camera(JNIEnv* env) {
     jclass clazz = env->FindClass("android/graphics/Camera");
     if (clazz == 0) {
@@ -113,4 +112,3 @@
                                                gCameraMethods,
                                                SK_ARRAY_COUNT(gCameraMethods));
 }
-
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index b2caa98..207c72f 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -756,26 +756,36 @@
         env->ReleaseStringChars(text, textArray);
     }
 
+    static void logGlyphs(sp<TextLayoutCacheValue> value) {
+        LOGD("drawTextWithGlyphs -- got glyphs - count=%d", value->getGlyphsCount());
+        for (size_t i = 0; i < value->getGlyphsCount(); i++) {
+            LOGD("                          glyphs[%d]=%d", i, value->getGlyphs()[i]);
+        }
+    }
+
+    static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray,
+            int start, int end,
+            jfloat x, jfloat y, int flags, SkPaint* paint) {
+
+        jint count = end - start;
+        sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
+                paint, textArray, start, count, count, flags);
+        if (value == NULL) {
+            LOGE("drawTextWithGlyphs -- cannot get Cache value");
+            return ;
+        }
+#if DEBUG_GLYPHS
+        logGlyphs(value);
+#endif
+        doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(),
+                x, y, flags, paint);
+    }
+
     static void drawTextWithGlyphs___CIIFFIPaint(JNIEnv* env, jobject, SkCanvas* canvas,
                                       jcharArray text, int index, int count,
                                       jfloat x, jfloat y, int flags, SkPaint* paint) {
         jchar* textArray = env->GetCharArrayElements(text, NULL);
-#if RTL_USE_HARFBUZZ && USE_TEXT_LAYOUT_CACHE
-        sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
-                paint, textArray + index, 0, count, count, flags);
-        if (value != NULL) {
-#if DEBUG_GLYPHS
-            LOGD("drawTextWithGlyphs -- got glyphs - count=%d", value->getGlyphsCount());
-            for (size_t i = 0; i < value->getGlyphsCount(); i++) {
-                LOGD("                          glyphs[%d]=%d", i, value->getGlyphs()[i]);
-            }
-#endif
-            doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(),
-                    x, y, flags, paint);
-        }
-#else
-        TextLayout::drawText(paint, textArray + index, count, flags, x, y, canvas);
-#endif
+        drawTextWithGlyphs(canvas, textArray + index, 0, count, x, y, flags, paint);
         env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
     }
 
@@ -785,23 +795,7 @@
                                           jfloat x, jfloat y, int flags, SkPaint* paint) {
 
         const jchar* textArray = env->GetStringChars(text, NULL);
-#if RTL_USE_HARFBUZZ && USE_TEXT_LAYOUT_CACHE
-        size_t count = end - start;
-        sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
-                paint, textArray, start, count, count, flags);
-        if (value != NULL) {
-#if DEBUG_GLYPHS
-            LOGD("drawTextWithGlyphs -- got glyphs - count=%d", value->getGlyphsCount());
-            for (size_t i = 0; i < value->getGlyphsCount(); i++) {
-                LOGD("                          glyphs[%d]=%d", i, value->getGlyphs()[i]);
-            }
-#endif
-            doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(),
-                    x, y, flags, paint);
-        }
-#else
-        TextLayout::drawText(paint, textArray + start, end - start, flags, x, y, canvas);
-#endif
+        drawTextWithGlyphs(canvas, textArray, start, end, x, y, flags, paint);
         env->ReleaseStringChars(text, textArray);
     }
 
diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
index 137acc6..6ce3f51 100644
--- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
+++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
@@ -3,7 +3,6 @@
 #define RETURN_NULL_IF_NULL(value) \
     do { if (!(value)) { SkASSERT(0); return NULL; } } while (false)
 
-static jclass       gInputStream_Clazz;
 static jmethodID    gInputStream_resetMethodID;
 static jmethodID    gInputStream_markMethodID;
 static jmethodID    gInputStream_availableMethodID;
@@ -19,10 +18,10 @@
         SkASSERT(fCapacity > 0);
         fBytesRead  = 0;
     }
-    
+
 	virtual bool rewind() {
         JNIEnv* env = fEnv;
-        
+
         fBytesRead = 0;
 
         env->CallVoidMethod(fJavaInputStream, gInputStream_resetMethodID);
@@ -34,7 +33,7 @@
         }
         return true;
     }
-    
+
     size_t doRead(void* buffer, size_t size) {
         JNIEnv* env = fEnv;
         size_t bytesRead = 0;
@@ -43,7 +42,7 @@
             size_t requested = size;
             if (requested > fCapacity)
                 requested = fCapacity;
-            
+
             jint n = env->CallIntMethod(fJavaInputStream,
                                         gInputStream_readMethodID, fJavaByteArray, 0, requested);
             if (env->ExceptionCheck()) {
@@ -52,11 +51,11 @@
                 SkDebugf("---- read threw an exception\n");
                 return 0;
             }
-            
+
             if (n < 0) { // n == 0 should not be possible, see InputStream read() specifications.
                 break;  // eof
             }
-            
+
             env->GetByteArrayRegion(fJavaByteArray, 0, n,
                                     reinterpret_cast<jbyte*>(buffer));
             if (env->ExceptionCheck()) {
@@ -65,16 +64,16 @@
                 SkDebugf("---- read:GetByteArrayRegion threw an exception\n");
                 return 0;
             }
-            
+
             buffer = (void*)((char*)buffer + n);
             bytesRead += n;
             size -= n;
             fBytesRead += n;
         } while (size != 0);
-        
+
         return bytesRead;
     }
-    
+
     size_t doSkip(size_t size) {
         JNIEnv* env = fEnv;
 
@@ -92,7 +91,7 @@
 
         return (size_t)skipped;
     }
-    
+
     size_t doSize() {
         JNIEnv* env = fEnv;
         jint avail = env->CallIntMethod(fJavaInputStream,
@@ -105,7 +104,7 @@
         }
         return avail;
     }
-    
+
 	virtual size_t read(void* buffer, size_t size) {
         JNIEnv* env = fEnv;
         if (NULL == buffer) {
@@ -134,7 +133,7 @@
         }
         return this->doRead(buffer, size);
     }
-    
+
 private:
     JNIEnv*     fEnv;
     jobject     fJavaInputStream;   // the caller owns this object
@@ -148,19 +147,18 @@
     static bool gInited;
 
     if (!gInited) {
-        gInputStream_Clazz = env->FindClass("java/io/InputStream");
-        RETURN_NULL_IF_NULL(gInputStream_Clazz);
-        gInputStream_Clazz = (jclass)env->NewGlobalRef(gInputStream_Clazz);
+        jclass inputStream_Clazz = env->FindClass("java/io/InputStream");
+        RETURN_NULL_IF_NULL(inputStream_Clazz);
 
-        gInputStream_resetMethodID      = env->GetMethodID(gInputStream_Clazz,
+        gInputStream_resetMethodID      = env->GetMethodID(inputStream_Clazz,
                                                            "reset", "()V");
-        gInputStream_markMethodID       = env->GetMethodID(gInputStream_Clazz,
+        gInputStream_markMethodID       = env->GetMethodID(inputStream_Clazz,
                                                            "mark", "(I)V");
-        gInputStream_availableMethodID  = env->GetMethodID(gInputStream_Clazz,
+        gInputStream_availableMethodID  = env->GetMethodID(inputStream_Clazz,
                                                            "available", "()I");
-        gInputStream_readMethodID       = env->GetMethodID(gInputStream_Clazz,
+        gInputStream_readMethodID       = env->GetMethodID(inputStream_Clazz,
                                                            "read", "([BII)I");
-        gInputStream_skipMethodID       = env->GetMethodID(gInputStream_Clazz,
+        gInputStream_skipMethodID       = env->GetMethodID(inputStream_Clazz,
                                                            "skip", "(J)J");
 
         RETURN_NULL_IF_NULL(gInputStream_resetMethodID);
@@ -181,7 +179,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static jclass       gOutputStream_Clazz;
 static jmethodID    gOutputStream_writeMethodID;
 static jmethodID    gOutputStream_flushMethodID;
 
@@ -191,11 +188,11 @@
         : fEnv(env), fJavaOutputStream(stream), fJavaByteArray(storage) {
         fCapacity = env->GetArrayLength(storage);
     }
-    
+
 	virtual bool write(const void* buffer, size_t size) {
         JNIEnv* env = fEnv;
         jbyteArray storage = fJavaByteArray;
-        
+
         while (size > 0) {
             size_t requested = size;
             if (requested > fCapacity) {
@@ -210,7 +207,7 @@
                 SkDebugf("--- write:SetByteArrayElements threw an exception\n");
                 return false;
             }
-            
+
             fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_writeMethodID,
                                  storage, 0, requested);
             if (env->ExceptionCheck()) {
@@ -219,17 +216,17 @@
                 SkDebugf("------- write threw an exception\n");
                 return false;
             }
-            
+
             buffer = (void*)((char*)buffer + requested);
             size -= requested;
         }
         return true;
     }
-    
+
     virtual void flush() {
         fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_flushMethodID);
     }
-    
+
 private:
     JNIEnv*     fEnv;
     jobject     fJavaOutputStream;  // the caller owns this object
@@ -242,14 +239,13 @@
     static bool gInited;
 
     if (!gInited) {
-        gOutputStream_Clazz = env->FindClass("java/io/OutputStream");
-        RETURN_NULL_IF_NULL(gOutputStream_Clazz);
-        gOutputStream_Clazz = (jclass)env->NewGlobalRef(gOutputStream_Clazz);
+        jclass outputStream_Clazz = env->FindClass("java/io/OutputStream");
+        RETURN_NULL_IF_NULL(outputStream_Clazz);
 
-        gOutputStream_writeMethodID = env->GetMethodID(gOutputStream_Clazz,
+        gOutputStream_writeMethodID = env->GetMethodID(outputStream_Clazz,
                                                        "write", "([BII)V");
         RETURN_NULL_IF_NULL(gOutputStream_writeMethodID);
-        gOutputStream_flushMethodID = env->GetMethodID(gOutputStream_Clazz,
+        gOutputStream_flushMethodID = env->GetMethodID(outputStream_Clazz,
                                                        "flush", "()V");
         RETURN_NULL_IF_NULL(gOutputStream_flushMethodID);
 
@@ -258,4 +254,3 @@
 
     return new SkJavaOutputStream(env, stream, storage);
 }
-
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 6c28e65..64bd207 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -350,14 +350,10 @@
     SkASSERT(bitmap);
     SkASSERT(bitmap->pixelRef());
 
-    jobject obj = env->AllocObject(gBitmap_class);
-    if (obj) {
-        env->CallVoidMethod(obj, gBitmap_constructorMethodID,
-                            (jint)bitmap, buffer, isMutable, ninepatch, density);
-        if (hasException(env)) {
-            obj = NULL;
-        }
-    }
+    jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,
+            static_cast<jint>(reinterpret_cast<uintptr_t>(bitmap)),
+            buffer, isMutable, ninepatch, density);
+    hasException(env); // For the side effect of logging.
     return obj;
 }
 
@@ -372,30 +368,19 @@
 {
     SkASSERT(bitmap != NULL);
 
-    jobject obj = env->AllocObject(gBitmapRegionDecoder_class);
-    if (hasException(env)) {
-        obj = NULL;
-        return obj;
-    }
-    if (obj) {
-        env->CallVoidMethod(obj, gBitmapRegionDecoder_constructorMethodID, (jint)bitmap);
-        if (hasException(env)) {
-            obj = NULL;
-        }
-    }
+    jobject obj = env->NewObject(gBitmapRegionDecoder_class,
+            gBitmapRegionDecoder_constructorMethodID,
+            static_cast<jint>(reinterpret_cast<uintptr_t>(bitmap)));
+    hasException(env); // For the side effect of logging.
     return obj;
 }
 
 jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region)
 {
     SkASSERT(region != NULL);
-    jobject obj = env->AllocObject(gRegion_class);
-    if (obj) {
-        env->CallVoidMethod(obj, gRegion_constructorMethodID, (jint)region, 0);
-        if (hasException(env)) {
-            obj = NULL;
-        }
-    }
+    jobject obj = env->NewObject(gRegion_class, gRegion_constructorMethodID,
+            static_cast<jint>(reinterpret_cast<uintptr_t>(region)), 0);
+    hasException(env); // For the side effect of logging.
     return obj;
 }
 
diff --git a/core/jni/android/graphics/Interpolator.cpp b/core/jni/android/graphics/Interpolator.cpp
index beec351..aa33c3d 100644
--- a/core/jni/android/graphics/Interpolator.cpp
+++ b/core/jni/android/graphics/Interpolator.cpp
@@ -61,7 +61,7 @@
 
     float* values = valueArray ? env->GetFloatArrayElements(valueArray, NULL) : NULL;
     result = interp->timeToValues(msec, (SkScalar*)values);
-    
+
     if (valueArray) {
         int n = env->GetArrayLength(valueArray);
         for (int i = 0; i < n; i++) {
@@ -69,7 +69,7 @@
         }
         env->ReleaseFloatArrayElements(valueArray, values, 0);
     }
-    
+
     return result;
 }
 
@@ -87,7 +87,6 @@
     { "nativeTimeToValues",     "(II[F)I",      (void*)Interpolator_timeToValues    }
 };
 
-int register_android_graphics_Interpolator(JNIEnv* env);
 int register_android_graphics_Interpolator(JNIEnv* env)
 {
     return android::AndroidRuntime::registerNativeMethods(env,
diff --git a/core/jni/android/graphics/LayerRasterizer.cpp b/core/jni/android/graphics/LayerRasterizer.cpp
index 7c458899..e5bc6f8 100644
--- a/core/jni/android/graphics/LayerRasterizer.cpp
+++ b/core/jni/android/graphics/LayerRasterizer.cpp
@@ -11,7 +11,7 @@
         SkASSERT(layer);
         SkASSERT(paint);
         layer->addLayer(*paint, SkFloatToScalar(dx), SkFloatToScalar(dy));
-    } 
+    }
 };
 
 /////////////////////////////////////////////////////////////////////////////////////////
@@ -23,7 +23,6 @@
     { "nativeAddLayer",     "(IIFF)V",  (void*)SkLayerRasterizerGlue::addLayer  }
 };
 
-int register_android_graphics_LayerRasterizer(JNIEnv* env);
 int register_android_graphics_LayerRasterizer(JNIEnv* env)
 {
     return android::AndroidRuntime::registerNativeMethods(env,
@@ -31,4 +30,3 @@
                                                        gLayerRasterizerMethods,
                                                        SK_ARRAY_COUNT(gLayerRasterizerMethods));
 }
-
diff --git a/core/jni/android/graphics/MaskFilter.cpp b/core/jni/android/graphics/MaskFilter.cpp
index d3f9b78..d954ddf 100644
--- a/core/jni/android/graphics/MaskFilter.cpp
+++ b/core/jni/android/graphics/MaskFilter.cpp
@@ -23,7 +23,7 @@
         ThrowIAE_IfNull(env, filter);
         return filter;
     }
- 
+
     static SkMaskFilter* createEmboss(JNIEnv* env, jobject, jfloatArray dirArray, float ambient, float specular, float radius) {
         SkScalar direction[3];
 
@@ -79,16 +79,14 @@
     result = android::AndroidRuntime::registerNativeMethods(env, name, array, SK_ARRAY_COUNT(array));  \
     if (result < 0) return result
 
-int register_android_graphics_MaskFilter(JNIEnv* env);
 int register_android_graphics_MaskFilter(JNIEnv* env)
 {
     int result;
-    
+
     REG(env, "android/graphics/MaskFilter", gMaskFilterMethods);
     REG(env, "android/graphics/BlurMaskFilter", gBlurMaskFilterMethods);
     REG(env, "android/graphics/EmbossMaskFilter", gEmbossMaskFilterMethods);
     REG(env, "android/graphics/TableMaskFilter", gTableMaskFilterMethods);
-    
+
     return 0;
 }
-
diff --git a/core/jni/android/graphics/Movie.cpp b/core/jni/android/graphics/Movie.cpp
index 7145433..c112423 100644
--- a/core/jni/android/graphics/Movie.cpp
+++ b/core/jni/android/graphics/Movie.cpp
@@ -23,11 +23,8 @@
     if (NULL == moov) {
         return NULL;
     }
-    jobject obj = env->AllocObject(gMovie_class);
-    if (obj) {
-        env->CallVoidMethod(obj, gMovie_constructorMethodID, (jint)moov);
-    }
-    return obj;
+    return env->NewObject(gMovie_class, gMovie_constructorMethodID,
+            static_cast<jint>(reinterpret_cast<uintptr_t>(moov)));
 }
 
 static SkMovie* J2Movie(JNIEnv* env, jobject movie) {
diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp
index f9a3518..0c8a8a3 100644
--- a/core/jni/android/graphics/NinePatch.cpp
+++ b/core/jni/android/graphics/NinePatch.cpp
@@ -182,7 +182,6 @@
                                                         (void*)SkNinePatchGlue::getTransparentRegion   }
 };
 
-int register_android_graphics_NinePatch(JNIEnv* env);
 int register_android_graphics_NinePatch(JNIEnv* env)
 {
     return android::AndroidRuntime::registerNativeMethods(env,
diff --git a/core/jni/android/graphics/PathEffect.cpp b/core/jni/android/graphics/PathEffect.cpp
index cfa9ce4..0503614 100644
--- a/core/jni/android/graphics/PathEffect.cpp
+++ b/core/jni/android/graphics/PathEffect.cpp
@@ -19,12 +19,12 @@
                                    SkPathEffect* outer, SkPathEffect* inner) {
         return new SkComposePathEffect(outer, inner);
     }
-    
+
     static SkPathEffect* Sum_constructor(JNIEnv* env, jobject,
                                   SkPathEffect* first, SkPathEffect* second) {
         return new SkSumPathEffect(first, second);
     }
-    
+
     static SkPathEffect* Dash_constructor(JNIEnv* env, jobject,
                                       jfloatArray intervalArray, float phase) {
         AutoJavaFloatArray autoInterval(env, intervalArray);
@@ -32,30 +32,30 @@
         float*  values = autoInterval.ptr();
 
         SkAutoSTMalloc<32, SkScalar>    storage(count);
-        SkScalar*                       intervals = storage.get();        
+        SkScalar*                       intervals = storage.get();
         for (int i = 0; i < count; i++) {
             intervals[i] = SkFloatToScalar(values[i]);
         }
         return new SkDashPathEffect(intervals, count, SkFloatToScalar(phase));
     }
- 
+
     static SkPathEffect* OneD_constructor(JNIEnv* env, jobject,
                   const SkPath* shape, float advance, float phase, int style) {
         SkASSERT(shape != NULL);
         return new SkPath1DPathEffect(*shape, SkFloatToScalar(advance),
                      SkFloatToScalar(phase), (SkPath1DPathEffect::Style)style);
     }
-    
+
     static SkPathEffect* Corner_constructor(JNIEnv* env, jobject, float radius){
         return new SkCornerPathEffect(SkFloatToScalar(radius));
     }
-    
+
     static SkPathEffect* Discrete_constructor(JNIEnv* env, jobject,
                                               float length, float deviation) {
         return new SkDiscretePathEffect(SkFloatToScalar(length),
                                         SkFloatToScalar(deviation));
     }
-    
+
 };
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -95,11 +95,10 @@
                                                   SK_ARRAY_COUNT(array));  \
     if (result < 0) return result
 
-int register_android_graphics_PathEffect(JNIEnv* env);
 int register_android_graphics_PathEffect(JNIEnv* env)
 {
     int result;
-    
+
     REG(env, "android/graphics/PathEffect", gPathEffectMethods);
     REG(env, "android/graphics/ComposePathEffect", gComposePathEffectMethods);
     REG(env, "android/graphics/SumPathEffect", gSumPathEffectMethods);
@@ -107,7 +106,6 @@
     REG(env, "android/graphics/PathDashPathEffect", gPathDashPathEffectMethods);
     REG(env, "android/graphics/CornerPathEffect", gCornerPathEffectMethods);
     REG(env, "android/graphics/DiscretePathEffect", gDiscretePathEffectMethods);
-    
+
     return 0;
 }
-
diff --git a/core/jni/android/graphics/Region.cpp b/core/jni/android/graphics/Region.cpp
index 2445229..5c6ebdf 100644
--- a/core/jni/android/graphics/Region.cpp
+++ b/core/jni/android/graphics/Region.cpp
@@ -69,7 +69,7 @@
 
 static jboolean Region_op0(JNIEnv* env, jobject, SkRegion* dst, int left, int top, int right, int bottom, int op) {
     SkIRect ir;
-    
+
     ir.set(left, top, right, bottom);
     return dst->op(ir, (SkRegion::Op)op);
 }
@@ -84,16 +84,16 @@
     return dst->op(*region1, *region2, (SkRegion::Op)op);
 }
 
-////////////////////////////////////  These are methods, not static 
+////////////////////////////////////  These are methods, not static
 
 static jboolean Region_isEmpty(JNIEnv* env, jobject region) {
     return GetSkRegion(env, region)->isEmpty();
 }
- 
+
 static jboolean Region_isRect(JNIEnv* env, jobject region) {
     return GetSkRegion(env, region)->isRect();
 }
- 
+
 static jboolean Region_isComplex(JNIEnv* env, jobject region) {
     return GetSkRegion(env, region)->isComplex();
 }
@@ -101,21 +101,21 @@
 static jboolean Region_contains(JNIEnv* env, jobject region, int x, int y) {
     return GetSkRegion(env, region)->contains(x, y);
 }
- 
+
 static jboolean Region_quickContains(JNIEnv* env, jobject region, int left, int top, int right, int bottom) {
     return GetSkRegion(env, region)->quickContains(left, top, right, bottom);
 }
- 
+
 static jboolean Region_quickRejectIIII(JNIEnv* env, jobject region, int left, int top, int right, int bottom) {
     SkIRect ir;
     ir.set(left, top, right, bottom);
     return GetSkRegion(env, region)->quickReject(ir);
 }
- 
+
 static jboolean Region_quickRejectRgn(JNIEnv* env, jobject region, jobject other) {
     return GetSkRegion(env, region)->quickReject(*GetSkRegion(env, other));
 }
- 
+
 static void Region_translate(JNIEnv* env, jobject region, int x, int y, jobject dst) {
     SkRegion* rgn = GetSkRegion(env, region);
     if (dst)
@@ -171,13 +171,13 @@
     if (parcel == NULL) {
         return NULL;
     }
-    
+
     android::Parcel* p = android::parcelForJavaObject(env, parcel);
-    
+
     SkRegion* region = new SkRegion;
     size_t size = p->readInt32();
     region->unflatten(p->readInplace(size));
-    
+
     return region;
 }
 
@@ -186,7 +186,7 @@
     if (parcel == NULL) {
         return false;
     }
-    
+
     android::Parcel* p = android::parcelForJavaObject(env, parcel);
 
     size_t size = region->flatten(NULL);
@@ -208,7 +208,7 @@
 struct RgnIterPair {
     SkRegion            fRgn;   // a copy of the caller's region
     SkRegion::Iterator  fIter;  // an iterator acting upon the copy (fRgn)
-    
+
     RgnIterPair(const SkRegion& rgn) : fRgn(rgn) {
         // have our iterator reference our copy (fRgn), so we know it will be
         // unchanged for the lifetime of the iterator
@@ -218,7 +218,7 @@
 
 static RgnIterPair* RegionIter_constructor(JNIEnv* env, jobject, const SkRegion* region)
 {
-    SkASSERT(region);    
+    SkASSERT(region);
     return new RgnIterPair(*region);
 }
 
@@ -279,12 +279,11 @@
     { "nativeEquals",           "(II)Z",                            (void*)Region_equals            },
 };
 
-int register_android_graphics_Region(JNIEnv* env);
 int register_android_graphics_Region(JNIEnv* env)
 {
     jclass clazz = env->FindClass("android/graphics/Region");
     SkASSERT(clazz);
-    
+
     gRegion_nativeInstanceFieldID = env->GetFieldID(clazz, "mNativeRegion", "I");
     SkASSERT(gRegion_nativeInstanceFieldID);
 
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index 7fdad10..f4cc9e4 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -35,7 +35,7 @@
         values[i] = SkScalarToFloat(hsv[i]);
     }
 }
- 
+
 static int Color_HSVToColor(JNIEnv* env, jobject, int alpha, jfloatArray hsvArray)
 {
     AutoJavaFloatArray  autoHSV(env, hsvArray, 3);
@@ -45,7 +45,7 @@
     for (int i = 0; i < 3; i++) {
         hsv[i] = SkFloatToScalar(values[i]);
     }
-    
+
     return SkHSVToColor(alpha, hsv);
 }
 
@@ -104,7 +104,7 @@
     return NULL;
 #endif
 }
-    
+
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
 static SkShader* LinearGradient_create1(JNIEnv* env, jobject o,
@@ -129,7 +129,7 @@
             pos[i] = SkFloatToScalar(posValues[i]);
         }
     }
-    
+
     SkShader* shader = SkGradientShader::CreateLinear(pts,
                                 reinterpret_cast<const SkColor*>(colorValues),
                                 pos, count,
@@ -218,7 +218,7 @@
     SkColor colors[2];
     colors[0] = color0;
     colors[1] = color1;
-    
+
     SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2, (SkShader::TileMode)tileMode);
 
     ThrowIAE_IfNull(env, s);
@@ -237,7 +237,7 @@
 
     SkAutoSTMalloc<8, SkScalar> storage(posArray ? count : 0);
     SkScalar*                   pos = NULL;
-    
+
     if (posArray) {
         AutoJavaFloatArray autoPos(env, posArray, count);
         const float* posValues = autoPos.ptr();
@@ -338,10 +338,10 @@
         jintArray jcolors, jfloatArray jpositions) {
     size_t      count = env->GetArrayLength(jcolors);
     const jint* colors = env->GetIntArrayElements(jcolors, NULL);
-    
+
     SkAutoSTMalloc<8, SkScalar> storage(jpositions ? count : 0);
     SkScalar*                   pos = NULL;
-    
+
     if (NULL != jpositions) {
         AutoJavaFloatArray autoPos(env, jpositions, count);
         const float* posValues = autoPos.ptr();
@@ -520,11 +520,10 @@
     result = android::AndroidRuntime::registerNativeMethods(env, name, array, SK_ARRAY_COUNT(array));  \
     if (result < 0) return result
 
-int register_android_graphics_Shader(JNIEnv* env);
 int register_android_graphics_Shader(JNIEnv* env)
 {
     int result;
-    
+
     REG(env, "android/graphics/Color", gColorMethods);
     REG(env, "android/graphics/Shader", gShaderMethods);
     REG(env, "android/graphics/BitmapShader", gBitmapShaderMethods);
@@ -532,7 +531,6 @@
     REG(env, "android/graphics/RadialGradient", gRadialGradientMethods);
     REG(env, "android/graphics/SweepGradient", gSweepGradientMethods);
     REG(env, "android/graphics/ComposeShader", gComposeShaderMethods);
-    
+
     return result;
 }
-
diff --git a/core/jni/android/graphics/TextLayout.h b/core/jni/android/graphics/TextLayout.h
index f203b75..9bb1b92 100644
--- a/core/jni/android/graphics/TextLayout.h
+++ b/core/jni/android/graphics/TextLayout.h
@@ -46,27 +46,27 @@
     static TextLayoutCache gTextLayoutCache;
 #endif
 
+enum {
+    kBidi_LTR = 0,
+    kBidi_RTL = 1,
+    kBidi_Default_LTR = 2,
+    kBidi_Default_RTL = 3,
+    kBidi_Force_LTR = 4,
+    kBidi_Force_RTL = 5,
+
+    kBidi_Mask = 0x7
+};
+
+enum {
+    kDirection_LTR = 0,
+    kDirection_RTL = 1,
+
+    kDirection_Mask = 0x1
+};
+
 class TextLayout {
 public:
 
-    enum {
-        kDirection_LTR = 0,
-        kDirection_RTL = 1,
-
-        kDirection_Mask = 0x1
-    };
-
-    enum {
-        kBidi_LTR = 0,
-        kBidi_RTL = 1,
-        kBidi_Default_LTR = 2,
-        kBidi_Default_RTL = 3,
-        kBidi_Force_LTR = 4,
-        kBidi_Force_RTL = 5,
-
-        kBidi_Mask = 0x7
-    };
-
     /*
      * Draws a unidirectional run of text.
      */
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index 8db768c..77a731a 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "TextLayoutCache.h"
+#include "TextLayout.h"
 
 namespace android {
 
@@ -381,13 +382,128 @@
     }
 }
 
+struct GlyphRun {
+    inline GlyphRun() {}
+    inline GlyphRun(jchar* glyphs, size_t glyphsCount, bool isRTL) :
+            glyphs(glyphs), glyphsCount(glyphsCount), isRTL(isRTL) { }
+    jchar* glyphs;
+    size_t glyphsCount;
+    int isRTL;
+};
+
 void TextLayoutCacheValue::computeValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
         size_t start, size_t count, size_t contextCount, int dirFlags,
         jfloat* outAdvances, jfloat* outTotalAdvance,
         jchar** outGlyphs, size_t* outGlyphsCount) {
+
+        UBiDiLevel bidiReq = 0;
+        bool forceLTR = false;
+        bool forceRTL = false;
+
+        switch (dirFlags) {
+            case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level
+            case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level
+            case kBidi_Default_LTR: bidiReq = UBIDI_DEFAULT_LTR; break;
+            case kBidi_Default_RTL: bidiReq = UBIDI_DEFAULT_RTL; break;
+            case kBidi_Force_LTR: forceLTR = true; break; // every char is LTR
+            case kBidi_Force_RTL: forceRTL = true; break; // every char is RTL
+        }
+
+        if (forceLTR || forceRTL) {
+#if DEBUG_GLYPHS
+                    LOGD("computeValuesWithHarfbuzz -- forcing run with LTR=%d RTL=%d",
+                            forceLTR, forceRTL);
+#endif
+            computeRunValuesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags,
+                    outAdvances, outTotalAdvance, outGlyphs, outGlyphsCount);
+        } else {
+            UBiDi* bidi = ubidi_open();
+            if (bidi) {
+                UErrorCode status = U_ZERO_ERROR;
+#if DEBUG_GLYPHS
+                LOGD("computeValuesWithHarfbuzz -- bidiReq=%d", bidiReq);
+#endif
+                ubidi_setPara(bidi, chars, contextCount, bidiReq, NULL, &status);
+                if (U_SUCCESS(status)) {
+                    int paraDir = ubidi_getParaLevel(bidi) & kDirection_Mask; // 0 if ltr, 1 if rtl
+                    size_t rc = ubidi_countRuns(bidi, &status);
+#if DEBUG_GLYPHS
+                    LOGD("computeValuesWithHarfbuzz -- dirFlags=%d run-count=%d paraDir=%d", dirFlags, rc, paraDir);
+#endif
+
+                    if (rc == 1 || !U_SUCCESS(status)) {
+                        computeRunValuesWithHarfbuzz(paint, chars, start, count, contextCount,
+                                dirFlags, outAdvances, outTotalAdvance, outGlyphs, outGlyphsCount);
+                        ubidi_close(bidi);
+                        return;
+                    }
+
+                    size_t runIndex = 0;
+                    Vector<GlyphRun> glyphRuns;
+                    for (size_t i = 0; i < rc; ++i) {
+                        int32_t startRun;
+                        int32_t lengthRun;
+                        UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &startRun, &lengthRun);
+
+                        int newFlags = (runDir == UBIDI_RTL) ? kDirection_RTL : kDirection_LTR;
+                        jfloat runTotalAdvance = 0;
+                        jchar* runGlyphs;
+                        size_t runGlyphsCount = 0;
+
+#if DEBUG_GLYPHS
+                        LOGD("computeValuesWithHarfbuzz -- run-start=%d run-len=%d newFlags=%d",
+                                startRun, lengthRun, newFlags);
+#endif
+                        computeRunValuesWithHarfbuzz(paint, chars, startRun,
+                                lengthRun, contextCount, newFlags,
+                                outAdvances + runIndex, &runTotalAdvance,
+                                &runGlyphs, &runGlyphsCount);
+
+                        runIndex += lengthRun;
+
+                        *outTotalAdvance += runTotalAdvance;
+                        *outGlyphsCount += runGlyphsCount;
+
+#if DEBUG_GLYPHS
+                        LOGD("computeValuesWithHarfbuzz -- run=%d run-glyphs-count=%d",
+                                i, runGlyphsCount);
+                        for (size_t j = 0; j < runGlyphsCount; j++) {
+                            LOGD("                          -- glyphs[%d]=%d", j, runGlyphs[j]);
+                        }
+#endif
+                        glyphRuns.push(GlyphRun(runGlyphs, runGlyphsCount, newFlags));
+                    }
+
+#if DEBUG_GLYPHS
+                    LOGD("computeValuesWithHarfbuzz -- total-glyphs-count=%d", *outGlyphsCount);
+#endif
+                    *outGlyphs = new jchar[*outGlyphsCount];
+                    jchar* glyphs = *outGlyphs;
+                    for (size_t i = 0; i < glyphRuns.size(); i++) {
+                        const GlyphRun& glyphRun = glyphRuns.itemAt(i);
+                        if (glyphRun.isRTL) {
+                            for (size_t n = 0; n < glyphRun.glyphsCount; n++) {
+                                glyphs[glyphRun.glyphsCount - n - 1] = glyphRun.glyphs[n];
+                            }
+                        } else {
+                            memcpy(glyphs, glyphRun.glyphs, glyphRun.glyphsCount * sizeof(jchar));
+                        }
+                        glyphs += glyphRun.glyphsCount;
+                        delete[] glyphRun.glyphs;
+                    }
+                }
+                ubidi_close(bidi);
+            }
+        }
+}
+
+void TextLayoutCacheValue::computeRunValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
+        size_t start, size_t count, size_t contextCount, int dirFlags,
+        jfloat* outAdvances, jfloat* outTotalAdvance,
+        jchar** outGlyphs, size_t* outGlyphsCount) {
+
     bool isRTL = dirFlags & 0x1;
 
-    // TODO: need to run BiDi algo here to breakdown the text in several runs
     HB_ShaperItem shaperItem;
     HB_FontRec font;
     FontData fontData;
@@ -397,21 +513,30 @@
 #if DEBUG_GLYPHS
     LOGD("HARFBUZZ -- num_glypth=%d - kerning_applied=%d", shaperItem.num_glyphs,
             shaperItem.kerning_applied);
-    LOGD("         -- string= '%s'", String8(chars, contextCount).string());
+    LOGD("         -- string= '%s'", String8(chars + start, count).string());
     LOGD("         -- isDevKernText=%d", paint->isDevKernText());
 #endif
 
     // Get Advances and their total
-    jfloat totalAdvance = 0;
-    for (size_t i = 0; i < count; i++) {
-        totalAdvance += outAdvances[i] = HBFixedToFloat(shaperItem.advances[i]);
-#if DEBUG_ADVANCES
-        LOGD("hb-adv = %d - rebased = %f - total = %f", shaperItem.advances[i], outAdvances[i],
-                totalAdvance);
-#endif
+    jfloat totalAdvance = outAdvances[0] = HBFixedToFloat(shaperItem.advances[shaperItem.log_clusters[0]]);
+    for (size_t i = 1; i < count; i++) {
+        size_t clusterPrevious = shaperItem.log_clusters[i - 1];
+        size_t cluster = shaperItem.log_clusters[i];
+        if (cluster == clusterPrevious) {
+            outAdvances[i] = 0;
+        } else {
+            totalAdvance += outAdvances[i] = HBFixedToFloat(shaperItem.advances[shaperItem.log_clusters[i]]);
+        }
     }
     *outTotalAdvance = totalAdvance;
 
+#if DEBUG_ADVANCES
+    for (size_t i = 0; i < count; i++) {
+        LOGD("hb-adv[%d] = %f - log_clusters = %d - total = %f", i,
+                outAdvances[i], shaperItem.log_clusters[i], totalAdvance);
+    }
+#endif
+
     // Get Glyphs
     if (outGlyphs) {
         *outGlyphsCount = shaperItem.num_glyphs;
diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h
index e6ce68d..62813df 100644
--- a/core/jni/android/graphics/TextLayoutCache.h
+++ b/core/jni/android/graphics/TextLayoutCache.h
@@ -178,6 +178,10 @@
     static void createGlyphArrays(HB_ShaperItem* shaperItem, int size);
     static void resetGlyphArrays(HB_ShaperItem* shaperItem);
 
+    static void computeRunValuesWithHarfbuzz(SkPaint* paint, const UChar* chars, size_t start,
+            size_t count, size_t contextCount, int dirFlags,
+            jfloat* outAdvances, jfloat* outTotalAdvance,
+            jchar** outGlyphs, size_t* outGlyphsCount);
 }; // TextLayoutCacheValue
 
 /**
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index 53a5c0a..b25598a 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -44,7 +44,7 @@
 static SkTypeface* Typeface_createFromTypeface(JNIEnv* env, jobject, SkTypeface* family, int style) {
     return SkTypeface::CreateFromTypeface(family, (SkTypeface::Style)style);
 }
- 
+
 static void Typeface_unref(JNIEnv* env, jobject obj, SkTypeface* face) {
     SkSafeUnref(face);
 }
@@ -64,7 +64,7 @@
     {
         delete fAsset;
     }
-    
+
     virtual const void* getMemoryBase()
     {
         return fMemoryBase;
@@ -75,38 +75,38 @@
         off64_t pos = fAsset->seek(0, SEEK_SET);
         return pos != (off64_t)-1;
     }
-    
+
 	virtual size_t read(void* buffer, size_t size)
     {
         ssize_t amount;
-        
+
         if (NULL == buffer)
         {
             if (0 == size)  // caller is asking us for our total length
                 return fAsset->getLength();
-            
+
             // asset->seek returns new total offset
             // we want to return amount that was skipped
-            
+
             off64_t oldOffset = fAsset->seek(0, SEEK_CUR);
             if (-1 == oldOffset)
                 return 0;
             off64_t newOffset = fAsset->seek(size, SEEK_CUR);
             if (-1 == newOffset)
                 return 0;
-            
+
             amount = newOffset - oldOffset;
         }
         else
         {
             amount = fAsset->read(buffer, size);
         }
-        
+
         if (amount < 0)
             amount = 0;
         return amount;
     }
-    
+
 private:
     Asset*      fAsset;
     const void* fMemoryBase;
@@ -115,21 +115,21 @@
 static SkTypeface* Typeface_createFromAsset(JNIEnv* env, jobject,
                                             jobject jassetMgr,
                                             jstring jpath) {
-    
+
     NPE_CHECK_RETURN_ZERO(env, jassetMgr);
     NPE_CHECK_RETURN_ZERO(env, jpath);
-    
+
     AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr);
     if (NULL == mgr) {
         return NULL;
     }
-    
+
     AutoJavaStringToUTF8    str(env, jpath);
     Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER);
     if (NULL == asset) {
         return NULL;
     }
-    
+
     SkStream* stream = new AssetStream(asset, true);
     SkTypeface* face = SkTypeface::CreateFromStream(stream);
     // SkTypeFace::CreateFromStream calls ref() on the stream, so we
@@ -180,7 +180,6 @@
     { "setGammaForText", "(FF)V", (void*)Typeface_setGammaForText },
 };
 
-int register_android_graphics_Typeface(JNIEnv* env);
 int register_android_graphics_Typeface(JNIEnv* env)
 {
     return android::AndroidRuntime::registerNativeMethods(env,
diff --git a/core/jni/android/graphics/YuvToJpegEncoder.cpp b/core/jni/android/graphics/YuvToJpegEncoder.cpp
index 0a0c5b3..8333e00 100644
--- a/core/jni/android/graphics/YuvToJpegEncoder.cpp
+++ b/core/jni/android/graphics/YuvToJpegEncoder.cpp
@@ -242,7 +242,6 @@
 
 #define kClassPathName  "android/graphics/YuvImage"
 
-int register_android_graphics_YuvImage(JNIEnv* env);
 int register_android_graphics_YuvImage(JNIEnv* env)
 {
     return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
diff --git a/core/jni/android_backup_BackupDataInput.cpp b/core/jni/android_backup_BackupDataInput.cpp
index b03dd16..c174a41 100644
--- a/core/jni/android_backup_BackupDataInput.cpp
+++ b/core/jni/android_backup_BackupDataInput.cpp
@@ -25,9 +25,6 @@
 namespace android
 {
 
-// java.io.FileDescriptor
-static jfieldID s_descriptorField = 0;
-
 // android.app.backup.BackupDataInput$EntityHeader
 static jfieldID s_keyField = 0;
 static jfieldID s_dataSizeField = 0;
@@ -35,9 +32,7 @@
 static int
 ctor_native(JNIEnv* env, jobject clazz, jobject fileDescriptor)
 {
-    int err;
-
-    int fd = env->GetIntField(fileDescriptor, s_descriptorField);
+    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
     if (fd == -1) {
         return NULL;
     }
@@ -140,15 +135,7 @@
 {
     //LOGD("register_android_backup_BackupDataInput");
 
-    jclass clazz;
-
-    clazz = env->FindClass("java/io/FileDescriptor");
-    LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
-    s_descriptorField = env->GetFieldID(clazz, "descriptor", "I");
-    LOG_FATAL_IF(s_descriptorField == NULL,
-            "Unable to find descriptor field in java.io.FileDescriptor");
-
-    clazz = env->FindClass("android/app/backup/BackupDataInput$EntityHeader");
+    jclass clazz = env->FindClass("android/app/backup/BackupDataInput$EntityHeader");
     LOG_FATAL_IF(clazz == NULL, "Unable to find class android.app.backup.BackupDataInput.EntityHeader");
     s_keyField = env->GetFieldID(clazz, "key", "Ljava/lang/String;");
     LOG_FATAL_IF(s_keyField == NULL,
diff --git a/core/jni/android_backup_BackupDataOutput.cpp b/core/jni/android_backup_BackupDataOutput.cpp
index b895d11..144a10c 100644
--- a/core/jni/android_backup_BackupDataOutput.cpp
+++ b/core/jni/android_backup_BackupDataOutput.cpp
@@ -25,14 +25,10 @@
 namespace android
 {
 
-static jfieldID s_descriptorField = 0;
-
 static int
 ctor_native(JNIEnv* env, jobject clazz, jobject fileDescriptor)
 {
-    int err;
-
-    int fd = env->GetIntField(fileDescriptor, s_descriptorField);
+    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
     if (fd == -1) {
         return NULL;
     }
@@ -112,15 +108,6 @@
 int register_android_backup_BackupDataOutput(JNIEnv* env)
 {
     //LOGD("register_android_backup_BackupDataOutput");
-
-    jclass clazz;
-
-    clazz = env->FindClass("java/io/FileDescriptor");
-    LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
-    s_descriptorField = env->GetFieldID(clazz, "descriptor", "I");
-    LOG_FATAL_IF(s_descriptorField == NULL,
-            "Unable to find descriptor field in java.io.FileDescriptor");
-
     return AndroidRuntime::registerNativeMethods(env, "android/app/backup/BackupDataOutput",
             g_methods, NELEM(g_methods));
 }
diff --git a/core/jni/android_backup_BackupHelperDispatcher.cpp b/core/jni/android_backup_BackupHelperDispatcher.cpp
index 26e7d66..49f1cd4 100644
--- a/core/jni/android_backup_BackupHelperDispatcher.cpp
+++ b/core/jni/android_backup_BackupHelperDispatcher.cpp
@@ -37,8 +37,6 @@
     int nameLength; // not including the NULL terminator, which is not written to the file
 };
 
-// java.io.FileDescriptor
-static jfieldID s_descriptorField = 0;
 static jfieldID s_chunkSizeField = 0;
 static jfieldID s_keyPrefixField = 0;
 
@@ -46,12 +44,11 @@
 readHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj)
 {
     chunk_header_v1 flattenedHeader;
-    int fd;
     ssize_t amt;
     String8 keyPrefix;
     char* buf;
 
-    fd = env->GetIntField(fdObj, s_descriptorField);
+    int fd = jniGetFDFromFileDescriptor(env, fdObj);
 
     amt = read(fd, &flattenedHeader.headerSize, sizeof(flattenedHeader.headerSize));
     if (amt != sizeof(flattenedHeader.headerSize)) {
@@ -128,9 +125,7 @@
 static int
 skipChunk_native(JNIEnv* env, jobject clazz, jobject fdObj, jint bytesToSkip)
 {
-    int fd;
-
-    fd = env->GetIntField(fdObj, s_descriptorField);
+    int fd = jniGetFDFromFileDescriptor(env, fdObj);
 
     lseek(fd, bytesToSkip, SEEK_CUR);
 
@@ -152,9 +147,8 @@
     int nameLength;
     int namePadding;
     int headerSize;
-    int fd;
 
-    fd = env->GetIntField(fdObj, s_descriptorField);
+    int fd = jniGetFDFromFileDescriptor(env, fdObj);
 
     nameObj = (jstring)env->GetObjectField(headerObj, s_keyPrefixField);
 
@@ -166,7 +160,7 @@
     pos = lseek(fd, 0, SEEK_CUR);
 
     lseek(fd, headerSize, SEEK_CUR);
-    
+
     return pos;
 }
 
@@ -175,13 +169,12 @@
 {
     int err;
     chunk_header_v1 header;
-    int fd;
     int namePadding;
     int prevPos;
     jstring nameObj;
     const char* buf;
 
-    fd = env->GetIntField(fdObj, s_descriptorField);
+    int fd = jniGetFDFromFileDescriptor(env, fdObj);
     prevPos = lseek(fd, 0, SEEK_CUR);
 
     nameObj = (jstring)env->GetObjectField(headerObj, s_keyPrefixField);
@@ -234,15 +227,7 @@
 
 int register_android_backup_BackupHelperDispatcher(JNIEnv* env)
 {
-    jclass clazz;
-
-    clazz = env->FindClass("java/io/FileDescriptor");
-    LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
-    s_descriptorField = env->GetFieldID(clazz, "descriptor", "I");
-    LOG_FATAL_IF(s_descriptorField == NULL,
-            "Unable to find descriptor field in java.io.FileDescriptor");
-    
-    clazz = env->FindClass("android/app/backup/BackupHelperDispatcher$Header");
+    jclass clazz = env->FindClass("android/app/backup/BackupHelperDispatcher$Header");
     LOG_FATAL_IF(clazz == NULL,
             "Unable to find class android.app.backup.BackupHelperDispatcher.Header");
     s_chunkSizeField = env->GetFieldID(clazz, "chunkSize", "I");
@@ -251,7 +236,7 @@
     s_keyPrefixField = env->GetFieldID(clazz, "keyPrefix", "Ljava/lang/String;");
     LOG_FATAL_IF(s_keyPrefixField == NULL,
             "Unable to find keyPrefix field in android.app.backup.BackupHelperDispatcher.Header");
-    
+
     return AndroidRuntime::registerNativeMethods(env, "android/app/backup/BackupHelperDispatcher",
             g_methods, NELEM(g_methods));
 }
diff --git a/core/jni/android_backup_FileBackupHelperBase.cpp b/core/jni/android_backup_FileBackupHelperBase.cpp
index 0137a06..0dfd8db 100644
--- a/core/jni/android_backup_FileBackupHelperBase.cpp
+++ b/core/jni/android_backup_FileBackupHelperBase.cpp
@@ -25,9 +25,6 @@
 namespace android
 {
 
-// java.io.FileDescriptor
-static jfieldID s_descriptorField = 0;
-
 static int
 ctor(JNIEnv* env, jobject clazz)
 {
@@ -47,8 +44,8 @@
     int err;
 
     // all parameters have already been checked against null
-    int oldStateFD = oldState != NULL ? env->GetIntField(oldState, s_descriptorField) : -1;
-    int newStateFD = env->GetIntField(newState, s_descriptorField);
+    int oldStateFD = oldState != NULL ? jniGetFDFromFileDescriptor(env, oldState) : -1;
+    int newStateFD = jniGetFDFromFileDescriptor(env, newState);
     BackupDataWriter* dataStream = (BackupDataWriter*)data;
 
     const int fileCount = env->GetArrayLength(files);
@@ -102,7 +99,7 @@
     int err;
 
     RestoreHelperBase* restore = (RestoreHelperBase*)ptr;
-    int fd = env->GetIntField(fileDescriptor, s_descriptorField);
+    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
 
     err = restore->WriteSnapshot(fd);
 
@@ -121,14 +118,6 @@
 
 int register_android_backup_FileBackupHelperBase(JNIEnv* env)
 {
-    jclass clazz;
-
-    clazz = env->FindClass("java/io/FileDescriptor");
-    LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
-    s_descriptorField = env->GetFieldID(clazz, "descriptor", "I");
-    LOG_FATAL_IF(s_descriptorField == NULL,
-            "Unable to find descriptor field in java.io.FileDescriptor");
-    
     return AndroidRuntime::registerNativeMethods(env, "android/app/backup/FileBackupHelperBase",
             g_methods, NELEM(g_methods));
 }
diff --git a/core/jni/android_content_res_ObbScanner.cpp b/core/jni/android_content_res_ObbScanner.cpp
index 404a87d..8837538 100644
--- a/core/jni/android_content_res_ObbScanner.cpp
+++ b/core/jni/android_content_res_ObbScanner.cpp
@@ -39,7 +39,7 @@
 static void android_content_res_ObbScanner_getObbInfo(JNIEnv* env, jobject clazz, jstring file,
         jobject obbInfo)
 {
-    const char* filePath = env->GetStringUTFChars(file, JNI_FALSE);
+    const char* filePath = env->GetStringUTFChars(file, NULL);
 
     sp<ObbFile> obb = new ObbFile();
     if (!obb->readFrom(filePath)) {
diff --git a/core/jni/android_database_SQLiteStatement.cpp b/core/jni/android_database_SQLiteStatement.cpp
index 97e0483..05ffbb1 100644
--- a/core/jni/android_database_SQLiteStatement.cpp
+++ b/core/jni/android_database_SQLiteStatement.cpp
@@ -143,7 +143,7 @@
 static jobject createParcelFileDescriptor(JNIEnv * env, int fd)
 {
     // Create FileDescriptor object
-    jobject fileDesc = newFileDescriptor(env, fd);
+    jobject fileDesc = jniCreateFileDescriptor(env, fd);
     if (fileDesc == NULL) {
         // FileDescriptor constructor has thrown an exception
         close(fd);
diff --git a/core/jni/android_emoji_EmojiFactory.cpp b/core/jni/android_emoji_EmojiFactory.cpp
index 1ebb36c..81dae88 100644
--- a/core/jni/android_emoji_EmojiFactory.cpp
+++ b/core/jni/android_emoji_EmojiFactory.cpp
@@ -93,8 +93,6 @@
 static pthread_once_t g_once = PTHREAD_ONCE_INIT;
 static bool lib_emoji_factory_is_ready;
 
-static jclass    gString_class;
-
 static jclass    gBitmap_class;
 static jmethodID gBitmap_constructorMethodID;
 
@@ -108,15 +106,11 @@
 
 static jobject create_java_EmojiFactory(
     JNIEnv* env, EmojiFactory* factory, jstring name) {
-  jobject obj = env->AllocObject(gEmojiFactory_class);
-  if (obj) {
-    env->CallVoidMethod(obj, gEmojiFactory_constructorMethodID,
-                        (jint)factory, name);
-    if (env->ExceptionCheck() != 0) {
-      LOGE("*** Uncaught exception returned from Java call!\n");
-      env->ExceptionDescribe();
-      obj = NULL;
-    }
+  jobject obj = env->NewObject(gEmojiFactory_class, gEmojiFactory_constructorMethodID,
+      static_cast<jint>(reinterpret_cast<uintptr_t>(factory)), name);
+  if (env->ExceptionCheck() != 0) {
+    LOGE("*** Uncaught exception returned from Java call!\n");
+    env->ExceptionDescribe();
   }
   return obj;
 }
@@ -182,17 +176,12 @@
     return NULL;
   }
 
-  jobject obj = env->AllocObject(gBitmap_class);
-  if (obj) {
-    env->CallVoidMethod(obj, gBitmap_constructorMethodID,
-                        reinterpret_cast<jint>(bitmap), NULL, false, NULL, -1);
-    if (env->ExceptionCheck() != 0) {
-      LOGE("*** Uncaught exception returned from Java call!\n");
-      env->ExceptionDescribe();
-      return NULL;
-    }
+  jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,
+      static_cast<jint>(reinterpret_cast<uintptr_t>(bitmap)), NULL, false, NULL, -1);
+  if (env->ExceptionCheck() != 0) {
+    LOGE("*** Uncaught exception returned from Java call!\n");
+    env->ExceptionDescribe();
   }
-
   return obj;
 }
 
diff --git a/core/jni/android_hardware_UsbDevice.cpp b/core/jni/android_hardware_UsbDevice.cpp
index c2950ea..25f901b 100644
--- a/core/jni/android_hardware_UsbDevice.cpp
+++ b/core/jni/android_hardware_UsbDevice.cpp
@@ -54,13 +54,6 @@
 
 int register_android_hardware_UsbDevice(JNIEnv *env)
 {
-    jclass clazz = env->FindClass("android/hardware/usb/UsbDevice");
-    if (clazz == NULL) {
-        LOGE("Can't find android/hardware/usb/UsbDevice");
-        return -1;
-    }
-
     return AndroidRuntime::registerNativeMethods(env, "android/hardware/usb/UsbDevice",
             method_table, NELEM(method_table));
 }
-
diff --git a/core/jni/android_hardware_UsbDeviceConnection.cpp b/core/jni/android_hardware_UsbDeviceConnection.cpp
index e259514..4d73bf3 100644
--- a/core/jni/android_hardware_UsbDeviceConnection.cpp
+++ b/core/jni/android_hardware_UsbDeviceConnection.cpp
@@ -42,7 +42,7 @@
 android_hardware_UsbDeviceConnection_open(JNIEnv *env, jobject thiz, jstring deviceName,
         jobject fileDescriptor)
 {
-    int fd = getParcelFileDescriptorFD(env, fileDescriptor);
+    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
     // duplicate the file descriptor, since ParcelFileDescriptor will eventually close its copy
     fd = dup(fd);
     if (fd < 0)
diff --git a/core/jni/android_media_JetPlayer.cpp b/core/jni/android_media_JetPlayer.cpp
index a7ffff4..e124069 100644
--- a/core/jni/android_media_JetPlayer.cpp
+++ b/core/jni/android_media_JetPlayer.cpp
@@ -175,7 +175,7 @@
     lpJet->setEventCallback(jetPlayerEventCallback);
 
     LOGV("android_media_JetPlayer_openFileDescr(): trying to load JET file through its fd" );
-    EAS_RESULT result = lpJet->loadFromFD(getParcelFileDescriptorFD(env, fileDescriptor),
+    EAS_RESULT result = lpJet->loadFromFD(jniGetFDFromFileDescriptor(env, fileDescriptor),
         (long long)offset, (long long)length); // cast params to types used by EAS_FILE
 
     if(result==EAS_SUCCESS) {
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index db132ec..4a0e68e 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -1,16 +1,16 @@
 /*
  * 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 
+ * 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 
+ *     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 
+ * 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.
  */
 
@@ -225,9 +225,6 @@
 
 int register_android_net_NetworkUtils(JNIEnv* env)
 {
-    jclass netutils = env->FindClass(NETUTILS_PKG_NAME);
-    LOG_FATAL_IF(netutils == NULL, "Unable to find class " NETUTILS_PKG_NAME);
-
     jclass dhcpInfoInternalClass = env->FindClass("android/net/DhcpInfoInternal");
     LOG_FATAL_IF(dhcpInfoInternalClass == NULL, "Unable to find class android/net/DhcpInfoInternal");
     dhcpInfoInternalFieldIds.constructorId = env->GetMethodID(dhcpInfoInternalClass, "<init>", "()V");
diff --git a/core/jni/android_net_TrafficStats.cpp b/core/jni/android_net_TrafficStats.cpp
index 0c84f11..203b5ef 100644
--- a/core/jni/android_net_TrafficStats.cpp
+++ b/core/jni/android_net_TrafficStats.cpp
@@ -25,6 +25,7 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <cutils/logger.h>
 #include <jni.h>
+#include <ScopedUtfChars.h>
 #include <utils/misc.h>
 #include <utils/Log.h>
 
@@ -130,13 +131,14 @@
             "/sys/class/net/ppp0/statistics/rx_bytes");
 }
 
-static jlong getData(JNIEnv* env, char *what, jstring interface) {
+static jlong getData(JNIEnv* env, const char* what, jstring javaInterface) {
+    ScopedUtfChars interface(env, javaInterface);
+    if (interface.c_str() == NULL) {
+        return -1;
+    }
+
     char filename[80];
-    jboolean isCopy;
-
-    const char *interfaceStr = env->GetStringUTFChars(interface, &isCopy);
-    snprintf(filename, sizeof(filename), "/sys/class/net/%s/statistics/%s", interfaceStr, what);
-
+    snprintf(filename, sizeof(filename), "/sys/class/net/%s/statistics/%s", interface.c_str(), what);
     return readNumber(filename);
 }
 
diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index 494dc27..9d17fe9 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "wifi"
 
 #include "jni.h"
+#include <ScopedUtfChars.h>
 #include <utils/misc.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/Log.h>
@@ -47,82 +48,100 @@
     }
 }
 
-static jint doIntCommand(const char *cmd)
+static jint doIntCommand(const char* fmt, ...)
 {
-    char reply[BUF_SIZE];
-
-    if (doCommand(cmd, reply, sizeof(reply)) != 0) {
-        return (jint)-1;
-    } else {
-        return (jint)atoi(reply);
+    char buf[BUF_SIZE];
+    va_list args;
+    va_start(args, fmt);
+    int byteCount = vsnprintf(buf, sizeof(buf), fmt, args);
+    va_end(args);
+    if (byteCount < 0 || byteCount >= BUF_SIZE) {
+        return -1;
     }
+    char reply[BUF_SIZE];
+    if (doCommand(buf, reply, sizeof(reply)) != 0) {
+        return -1;
+    }
+    return static_cast<jint>(atoi(reply));
 }
 
-static jboolean doBooleanCommand(const char *cmd, const char *expect)
+static jboolean doBooleanCommand(const char* expect, const char* fmt, ...)
 {
-    char reply[BUF_SIZE];
-
-    if (doCommand(cmd, reply, sizeof(reply)) != 0) {
-        return (jboolean)JNI_FALSE;
-    } else {
-        return (jboolean)(strcmp(reply, expect) == 0);
+    char buf[BUF_SIZE];
+    va_list args;
+    va_start(args, fmt);
+    int byteCount = vsnprintf(buf, sizeof(buf), fmt, args);
+    va_end(args);
+    if (byteCount < 0 || byteCount >= BUF_SIZE) {
+        return JNI_FALSE;
     }
+    char reply[BUF_SIZE];
+    if (doCommand(buf, reply, sizeof(reply)) != 0) {
+        return JNI_FALSE;
+    }
+    return (strcmp(reply, expect) == 0);
 }
 
 // Send a command to the supplicant, and return the reply as a String
-static jstring doStringCommand(JNIEnv *env, const char *cmd)
-{
-    char reply[4096];
-
-    if (doCommand(cmd, reply, sizeof(reply)) != 0) {
-        return env->NewStringUTF(NULL);
-    } else {
-        String16 str((char *)reply);
-        return env->NewString((const jchar *)str.string(), str.size());
+static jstring doStringCommand(JNIEnv* env, const char* fmt, ...) {
+    char buf[BUF_SIZE];
+    va_list args;
+    va_start(args, fmt);
+    int byteCount = vsnprintf(buf, sizeof(buf), fmt, args);
+    va_end(args);
+    if (byteCount < 0 || byteCount >= BUF_SIZE) {
+        return NULL;
     }
+    char reply[4096];
+    if (doCommand(buf, reply, sizeof(reply)) != 0) {
+        return NULL;
+    }
+    // TODO: why not just NewStringUTF?
+    String16 str((char *)reply);
+    return env->NewString((const jchar *)str.string(), str.size());
 }
 
-static jboolean android_net_wifi_isDriverLoaded(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_isDriverLoaded(JNIEnv* env, jobject)
 {
     return (jboolean)(::is_wifi_driver_loaded() == 1);
 }
 
-static jboolean android_net_wifi_loadDriver(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_loadDriver(JNIEnv* env, jobject)
 {
     return (jboolean)(::wifi_load_driver() == 0);
 }
 
-static jboolean android_net_wifi_unloadDriver(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_unloadDriver(JNIEnv* env, jobject)
 {
     return (jboolean)(::wifi_unload_driver() == 0);
 }
 
-static jboolean android_net_wifi_startSupplicant(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_startSupplicant(JNIEnv* env, jobject)
 {
     return (jboolean)(::wifi_start_supplicant() == 0);
 }
 
-static jboolean android_net_wifi_stopSupplicant(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_stopSupplicant(JNIEnv* env, jobject)
 {
-    return doBooleanCommand("TERMINATE", "OK");
+    return doBooleanCommand("OK", "TERMINATE");
 }
 
-static jboolean android_net_wifi_killSupplicant(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_killSupplicant(JNIEnv* env, jobject)
 {
     return (jboolean)(::wifi_stop_supplicant() == 0);
 }
 
-static jboolean android_net_wifi_connectToSupplicant(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_connectToSupplicant(JNIEnv* env, jobject)
 {
     return (jboolean)(::wifi_connect_to_supplicant() == 0);
 }
 
-static void android_net_wifi_closeSupplicantConnection(JNIEnv* env, jobject clazz)
+static void android_net_wifi_closeSupplicantConnection(JNIEnv* env, jobject)
 {
     ::wifi_close_supplicant_connection();
 }
 
-static jstring android_net_wifi_waitForEvent(JNIEnv* env, jobject clazz)
+static jstring android_net_wifi_waitForEvent(JNIEnv* env, jobject)
 {
     char buf[BUF_SIZE];
 
@@ -130,197 +149,143 @@
     if (nread > 0) {
         return env->NewStringUTF(buf);
     } else {
-        return  env->NewStringUTF(NULL);
+        return NULL;
     }
 }
 
-static jstring android_net_wifi_listNetworksCommand(JNIEnv* env, jobject clazz)
+static jstring android_net_wifi_listNetworksCommand(JNIEnv* env, jobject)
 {
     return doStringCommand(env, "LIST_NETWORKS");
 }
 
-static jint android_net_wifi_addNetworkCommand(JNIEnv* env, jobject clazz)
+static jint android_net_wifi_addNetworkCommand(JNIEnv* env, jobject)
 {
     return doIntCommand("ADD_NETWORK");
 }
 
-static jboolean android_net_wifi_wpsPbcCommand(JNIEnv* env, jobject clazz, jstring bssid)
+static jboolean android_net_wifi_wpsPbcCommand(JNIEnv* env, jobject, jstring javaBssid)
 {
-    char cmdstr[BUF_SIZE];
-    jboolean isCopy;
-
-    const char *bssidStr = env->GetStringUTFChars(bssid, &isCopy);
-    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "WPS_PBC %s", bssidStr);
-    env->ReleaseStringUTFChars(bssid, bssidStr);
-
-    if ((numWritten == -1) || (numWritten >= sizeof(cmdstr))) {
-        return false;
+    ScopedUtfChars bssid(env, javaBssid);
+    if (bssid.c_str() == NULL) {
+        return JNI_FALSE;
     }
-    return doBooleanCommand(cmdstr, "OK");
+    return doBooleanCommand("OK", "WPS_PBC %s", bssid.c_str());
 }
 
-static jboolean android_net_wifi_wpsPinFromAccessPointCommand(JNIEnv* env, jobject clazz,
-        jstring bssid, jstring apPin)
+static jboolean android_net_wifi_wpsPinFromAccessPointCommand(JNIEnv* env, jobject,
+        jstring javaBssid, jstring javaApPin)
 {
-    char cmdstr[BUF_SIZE];
-    jboolean isCopy;
-
-    const char *bssidStr = env->GetStringUTFChars(bssid, &isCopy);
-    const char *apPinStr = env->GetStringUTFChars(apPin, &isCopy);
-    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "WPS_REG %s %s", bssidStr, apPinStr);
-    env->ReleaseStringUTFChars(bssid, bssidStr);
-    env->ReleaseStringUTFChars(apPin, apPinStr);
-
-    if ((numWritten == -1) || (numWritten >= (int)sizeof(cmdstr))) {
-        return false;
+    ScopedUtfChars bssid(env, javaBssid);
+    if (bssid.c_str() == NULL) {
+        return JNI_FALSE;
     }
-    return doBooleanCommand(cmdstr, "OK");
+    ScopedUtfChars apPin(env, javaApPin);
+    if (apPin.c_str() == NULL) {
+        return JNI_FALSE;
+    }
+    return doBooleanCommand("OK", "WPS_REG %s %s", bssid.c_str(), apPin.c_str());
 }
 
-static jstring android_net_wifi_wpsPinFromDeviceCommand(JNIEnv* env, jobject clazz, jstring bssid)
+static jstring android_net_wifi_wpsPinFromDeviceCommand(JNIEnv* env, jobject, jstring javaBssid)
 {
-    char cmdstr[BUF_SIZE];
-    jboolean isCopy;
-
-    const char *bssidStr = env->GetStringUTFChars(bssid, &isCopy);
-    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "WPS_PIN %s", bssidStr);
-    env->ReleaseStringUTFChars(bssid, bssidStr);
-
-    if ((numWritten == -1) || (numWritten >= (int)sizeof(cmdstr))) {
+    ScopedUtfChars bssid(env, javaBssid);
+    if (bssid.c_str() == NULL) {
         return NULL;
     }
-    return doStringCommand(env, cmdstr);
+    return doStringCommand(env, "WPS_PIN %s", bssid.c_str());
 }
 
-static jboolean android_net_wifi_setCountryCodeCommand(JNIEnv* env, jobject clazz, jstring country)
+static jboolean android_net_wifi_setCountryCodeCommand(JNIEnv* env, jobject, jstring javaCountry)
 {
-    char cmdstr[BUF_SIZE];
-    jboolean isCopy;
-
-    const char *countryStr = env->GetStringUTFChars(country, &isCopy);
-    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER COUNTRY %s", countryStr);
-    env->ReleaseStringUTFChars(country, countryStr);
-
-    if ((numWritten == -1) || (numWritten >= (int)sizeof(cmdstr))) {
-        return false;
+    ScopedUtfChars country(env, javaCountry);
+    if (country.c_str() == NULL) {
+        return JNI_FALSE;
     }
-    return doBooleanCommand(cmdstr, "OK");
+    return doBooleanCommand("OK", "DRIVER COUNTRY %s", country.c_str());
 }
 
 static jboolean android_net_wifi_setNetworkVariableCommand(JNIEnv* env,
-                                                           jobject clazz,
+                                                           jobject,
                                                            jint netId,
-                                                           jstring name,
-                                                           jstring value)
+                                                           jstring javaName,
+                                                           jstring javaValue)
 {
-    char cmdstr[BUF_SIZE];
-    jboolean isCopy;
-
-    const char *nameStr = env->GetStringUTFChars(name, &isCopy);
-    const char *valueStr = env->GetStringUTFChars(value, &isCopy);
-
-    if (nameStr == NULL || valueStr == NULL)
+    ScopedUtfChars name(env, javaName);
+    if (name.c_str() == NULL) {
         return JNI_FALSE;
-
-    int cmdTooLong = snprintf(cmdstr, sizeof(cmdstr), "SET_NETWORK %d %s %s",
-                 netId, nameStr, valueStr) >= (int)sizeof(cmdstr);
-
-    env->ReleaseStringUTFChars(name, nameStr);
-    env->ReleaseStringUTFChars(value, valueStr);
-
-    return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
+    }
+    ScopedUtfChars value(env, javaValue);
+    if (value.c_str() == NULL) {
+        return JNI_FALSE;
+    }
+    return doBooleanCommand("OK", "SET_NETWORK %d %s %s", netId, name.c_str(), value.c_str());
 }
 
 static jstring android_net_wifi_getNetworkVariableCommand(JNIEnv* env,
-                                                          jobject clazz,
+                                                          jobject,
                                                           jint netId,
-                                                          jstring name)
+                                                          jstring javaName)
 {
-    char cmdstr[BUF_SIZE];
-    jboolean isCopy;
-
-    const char *nameStr = env->GetStringUTFChars(name, &isCopy);
-
-    if (nameStr == NULL)
-        return env->NewStringUTF(NULL);
-
-    int cmdTooLong = snprintf(cmdstr, sizeof(cmdstr), "GET_NETWORK %d %s",
-                             netId, nameStr) >= (int)sizeof(cmdstr);
-
-    env->ReleaseStringUTFChars(name, nameStr);
-
-    return cmdTooLong ? env->NewStringUTF(NULL) : doStringCommand(env, cmdstr);
+    ScopedUtfChars name(env, javaName);
+    if (name.c_str() == NULL) {
+        return NULL;
+    }
+    return doStringCommand(env, "GET_NETWORK %d %s", netId, name.c_str());
 }
 
-static jboolean android_net_wifi_removeNetworkCommand(JNIEnv* env, jobject clazz, jint netId)
+static jboolean android_net_wifi_removeNetworkCommand(JNIEnv* env, jobject, jint netId)
 {
-    char cmdstr[BUF_SIZE];
-
-    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "REMOVE_NETWORK %d", netId);
-    int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
-
-    return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
+    return doBooleanCommand("OK", "REMOVE_NETWORK %d", netId);
 }
 
 static jboolean android_net_wifi_enableNetworkCommand(JNIEnv* env,
-                                                  jobject clazz,
+                                                  jobject,
                                                   jint netId,
                                                   jboolean disableOthers)
 {
-    char cmdstr[BUF_SIZE];
-    const char *cmd = disableOthers ? "SELECT_NETWORK" : "ENABLE_NETWORK";
-
-    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "%s %d", cmd, netId);
-    int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
-
-    return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
+    return doBooleanCommand("OK", "%s_NETWORK %d", disableOthers ? "SELECT" : "ENABLE", netId);
 }
 
-static jboolean android_net_wifi_disableNetworkCommand(JNIEnv* env, jobject clazz, jint netId)
+static jboolean android_net_wifi_disableNetworkCommand(JNIEnv* env, jobject, jint netId)
 {
-    char cmdstr[BUF_SIZE];
-
-    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DISABLE_NETWORK %d", netId);
-    int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
-
-    return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
+    return doBooleanCommand("OK", "DISABLE_NETWORK %d", netId);
 }
 
-static jstring android_net_wifi_statusCommand(JNIEnv* env, jobject clazz)
+static jstring android_net_wifi_statusCommand(JNIEnv* env, jobject)
 {
     return doStringCommand(env, "STATUS");
 }
 
-static jboolean android_net_wifi_pingCommand(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_pingCommand(JNIEnv* env, jobject)
 {
-    return doBooleanCommand("PING", "PONG");
+    return doBooleanCommand("PONG", "PING");
 }
 
-static jstring android_net_wifi_scanResultsCommand(JNIEnv* env, jobject clazz)
+static jstring android_net_wifi_scanResultsCommand(JNIEnv* env, jobject)
 {
     return doStringCommand(env, "SCAN_RESULTS");
 }
 
-static jboolean android_net_wifi_disconnectCommand(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_disconnectCommand(JNIEnv* env, jobject)
 {
-    return doBooleanCommand("DISCONNECT", "OK");
+    return doBooleanCommand("OK", "DISCONNECT");
 }
 
-static jboolean android_net_wifi_reconnectCommand(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_reconnectCommand(JNIEnv* env, jobject)
 {
-    return doBooleanCommand("RECONNECT", "OK");
+    return doBooleanCommand("OK", "RECONNECT");
 }
-static jboolean android_net_wifi_reassociateCommand(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_reassociateCommand(JNIEnv* env, jobject)
 {
-    return doBooleanCommand("REASSOCIATE", "OK");
+    return doBooleanCommand("OK", "REASSOCIATE");
 }
 
 static jboolean doSetScanMode(jboolean setActive)
 {
-    return doBooleanCommand((setActive ? "DRIVER SCAN-ACTIVE" : "DRIVER SCAN-PASSIVE"), "OK");
+    return doBooleanCommand("OK", (setActive ? "DRIVER SCAN-ACTIVE" : "DRIVER SCAN-PASSIVE"));
 }
 
-static jboolean android_net_wifi_scanCommand(JNIEnv* env, jobject clazz, jboolean forceActive)
+static jboolean android_net_wifi_scanCommand(JNIEnv* env, jobject, jboolean forceActive)
 {
     jboolean result;
 
@@ -328,43 +293,43 @@
     // The scan will still work.
     if (forceActive && !sScanModeActive)
         doSetScanMode(true);
-    result = doBooleanCommand("SCAN", "OK");
+    result = doBooleanCommand("OK", "SCAN");
     if (forceActive && !sScanModeActive)
         doSetScanMode(sScanModeActive);
     return result;
 }
 
-static jboolean android_net_wifi_setScanModeCommand(JNIEnv* env, jobject clazz, jboolean setActive)
+static jboolean android_net_wifi_setScanModeCommand(JNIEnv* env, jobject, jboolean setActive)
 {
     sScanModeActive = setActive;
     return doSetScanMode(setActive);
 }
 
-static jboolean android_net_wifi_startDriverCommand(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_startDriverCommand(JNIEnv* env, jobject)
 {
-    return doBooleanCommand("DRIVER START", "OK");
+    return doBooleanCommand("OK", "DRIVER START");
 }
 
-static jboolean android_net_wifi_stopDriverCommand(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_stopDriverCommand(JNIEnv* env, jobject)
 {
-    return doBooleanCommand("DRIVER STOP", "OK");
+    return doBooleanCommand("OK", "DRIVER STOP");
 }
 
-static jboolean android_net_wifi_startPacketFiltering(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_startPacketFiltering(JNIEnv* env, jobject)
 {
-    return doBooleanCommand("DRIVER RXFILTER-ADD 0", "OK")
-	&& doBooleanCommand("DRIVER RXFILTER-ADD 1", "OK")
-	&& doBooleanCommand("DRIVER RXFILTER-ADD 3", "OK")
-	&& doBooleanCommand("DRIVER RXFILTER-START", "OK");
+    return doBooleanCommand("OK", "DRIVER RXFILTER-ADD 0")
+            && doBooleanCommand("OK", "DRIVER RXFILTER-ADD 1")
+            && doBooleanCommand("OK", "DRIVER RXFILTER-ADD 3")
+            && doBooleanCommand("OK", "DRIVER RXFILTER-START");
 }
 
-static jboolean android_net_wifi_stopPacketFiltering(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_stopPacketFiltering(JNIEnv* env, jobject)
 {
-    jboolean result = doBooleanCommand("DRIVER RXFILTER-STOP", "OK");
+    jboolean result = doBooleanCommand("OK", "DRIVER RXFILTER-STOP");
     if (result) {
-	(void)doBooleanCommand("DRIVER RXFILTER-REMOVE 3", "OK");
-	(void)doBooleanCommand("DRIVER RXFILTER-REMOVE 1", "OK");
-	(void)doBooleanCommand("DRIVER RXFILTER-REMOVE 0", "OK");
+        (void)doBooleanCommand("OK", "DRIVER RXFILTER-REMOVE 3");
+        (void)doBooleanCommand("OK", "DRIVER RXFILTER-REMOVE 1");
+        (void)doBooleanCommand("OK", "DRIVER RXFILTER-REMOVE 0");
     }
 
     return result;
@@ -399,17 +364,17 @@
     return (jint)rssi;
 }
 
-static jint android_net_wifi_getRssiCommand(JNIEnv* env, jobject clazz)
+static jint android_net_wifi_getRssiCommand(JNIEnv* env, jobject)
 {
     return android_net_wifi_getRssiHelper("DRIVER RSSI");
 }
 
-static jint android_net_wifi_getRssiApproxCommand(JNIEnv* env, jobject clazz)
+static jint android_net_wifi_getRssiApproxCommand(JNIEnv* env, jobject)
 {
     return android_net_wifi_getRssiHelper("DRIVER RSSI-APPROX");
 }
 
-static jint android_net_wifi_getLinkSpeedCommand(JNIEnv* env, jobject clazz)
+static jint android_net_wifi_getLinkSpeedCommand(JNIEnv* env, jobject)
 {
     char reply[BUF_SIZE];
     int linkspeed;
@@ -423,33 +388,28 @@
     return (jint)linkspeed;
 }
 
-static jstring android_net_wifi_getMacAddressCommand(JNIEnv* env, jobject clazz)
+static jstring android_net_wifi_getMacAddressCommand(JNIEnv* env, jobject)
 {
     char reply[BUF_SIZE];
     char buf[BUF_SIZE];
 
     if (doCommand("DRIVER MACADDR", reply, sizeof(reply)) != 0) {
-        return env->NewStringUTF(NULL);
+        return NULL;
     }
     // reply comes back in the form "Macaddr = XX.XX.XX.XX.XX.XX" where XX
     // is the part of the string we're interested in.
-    if (sscanf(reply, "%*s = %255s", buf) == 1)
+    if (sscanf(reply, "%*s = %255s", buf) == 1) {
         return env->NewStringUTF(buf);
-    else
-        return env->NewStringUTF(NULL);
+    }
+    return NULL;
 }
 
-static jboolean android_net_wifi_setPowerModeCommand(JNIEnv* env, jobject clazz, jint mode)
+static jboolean android_net_wifi_setPowerModeCommand(JNIEnv* env, jobject, jint mode)
 {
-    char cmdstr[BUF_SIZE];
-
-    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER POWERMODE %d", mode);
-    int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
-
-    return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
+    return doBooleanCommand("OK", "DRIVER POWERMODE %d", mode);
 }
 
-static jint android_net_wifi_getPowerModeCommand(JNIEnv* env, jobject clazz)
+static jint android_net_wifi_getPowerModeCommand(JNIEnv* env, jobject)
 {
     char reply[BUF_SIZE];
     int power;
@@ -463,17 +423,12 @@
     return (jint)power;
 }
 
-static jboolean android_net_wifi_setBandCommand(JNIEnv* env, jobject clazz, jint band)
+static jboolean android_net_wifi_setBandCommand(JNIEnv* env, jobject, jint band)
 {
-    char cmdstr[25];
-
-    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER SETBAND %d", band);
-    int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
-
-    return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
+    return doBooleanCommand("OK", "DRIVER SETBAND %d", band);
 }
 
-static jint android_net_wifi_getBandCommand(JNIEnv* env, jobject clazz)
+static jint android_net_wifi_getBandCommand(JNIEnv* env, jobject)
 {
     char reply[25];
     int band;
@@ -487,94 +442,66 @@
     return (jint)band;
 }
 
-static jboolean android_net_wifi_setBluetoothCoexistenceModeCommand(JNIEnv* env, jobject clazz, jint mode)
+static jboolean android_net_wifi_setBluetoothCoexistenceModeCommand(JNIEnv* env, jobject, jint mode)
 {
-    char cmdstr[BUF_SIZE];
-
-    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER BTCOEXMODE %d", mode);
-    int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
-
-    return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
+    return doBooleanCommand("OK", "DRIVER BTCOEXMODE %d", mode);
 }
 
-static jboolean android_net_wifi_setBluetoothCoexistenceScanModeCommand(JNIEnv* env, jobject clazz, jboolean setCoexScanMode)
+static jboolean android_net_wifi_setBluetoothCoexistenceScanModeCommand(JNIEnv* env, jobject, jboolean setCoexScanMode)
 {
-    char cmdstr[BUF_SIZE];
-
-    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER BTCOEXSCAN-%s", setCoexScanMode ? "START" : "STOP");
-    int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
-
-    return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
+    return doBooleanCommand("OK", "DRIVER BTCOEXSCAN-%s", setCoexScanMode ? "START" : "STOP");
 }
 
-static jboolean android_net_wifi_saveConfigCommand(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_saveConfigCommand(JNIEnv* env, jobject)
 {
     // Make sure we never write out a value for AP_SCAN other than 1
-    (void)doBooleanCommand("AP_SCAN 1", "OK");
-    return doBooleanCommand("SAVE_CONFIG", "OK");
+    (void)doBooleanCommand("OK", "AP_SCAN 1");
+    return doBooleanCommand("OK", "SAVE_CONFIG");
 }
 
-static jboolean android_net_wifi_reloadConfigCommand(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_reloadConfigCommand(JNIEnv* env, jobject)
 {
-    return doBooleanCommand("RECONFIGURE", "OK");
+    return doBooleanCommand("OK", "RECONFIGURE");
 }
 
-static jboolean android_net_wifi_setScanResultHandlingCommand(JNIEnv* env, jobject clazz, jint mode)
+static jboolean android_net_wifi_setScanResultHandlingCommand(JNIEnv* env, jobject, jint mode)
 {
-    char cmdstr[BUF_SIZE];
-
-    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "AP_SCAN %d", mode);
-    int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
-
-    return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
+    return doBooleanCommand("OK", "AP_SCAN %d", mode);
 }
 
-static jboolean android_net_wifi_addToBlacklistCommand(JNIEnv* env, jobject clazz, jstring bssid)
+static jboolean android_net_wifi_addToBlacklistCommand(JNIEnv* env, jobject, jstring javaBssid)
 {
-    char cmdstr[BUF_SIZE];
-    jboolean isCopy;
-
-    const char *bssidStr = env->GetStringUTFChars(bssid, &isCopy);
-
-    int cmdTooLong = snprintf(cmdstr, sizeof(cmdstr), "BLACKLIST %s", bssidStr) >= (int)sizeof(cmdstr);
-
-    env->ReleaseStringUTFChars(bssid, bssidStr);
-
-    return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
+    ScopedUtfChars bssid(env, javaBssid);
+    if (bssid.c_str() == NULL) {
+        return JNI_FALSE;
+    }
+    return doBooleanCommand("OK", "BLACKLIST %s", bssid.c_str());
 }
 
-static jboolean android_net_wifi_clearBlacklistCommand(JNIEnv* env, jobject clazz)
+static jboolean android_net_wifi_clearBlacklistCommand(JNIEnv* env, jobject)
 {
-    return doBooleanCommand("BLACKLIST clear", "OK");
+    return doBooleanCommand("OK", "BLACKLIST clear");
 }
 
-static jboolean android_net_wifi_setSuspendOptimizationsCommand(JNIEnv* env, jobject clazz, jboolean enabled)
+static jboolean android_net_wifi_setSuspendOptimizationsCommand(JNIEnv* env, jobject, jboolean enabled)
 {
-    char cmdstr[BUF_SIZE];
-
-    snprintf(cmdstr, sizeof(cmdstr), "DRIVER SETSUSPENDOPT %d", enabled ? 0 : 1);
-    return doBooleanCommand(cmdstr, "OK");
+    return doBooleanCommand("OK", "DRIVER SETSUSPENDOPT %d", enabled ? 0 : 1);
 }
 
-static void android_net_wifi_enableBackgroundScanCommand(JNIEnv* env, jobject clazz, jboolean enable)
+static void android_net_wifi_enableBackgroundScanCommand(JNIEnv* env, jobject, jboolean enable)
 {
     //Note: BGSCAN-START and BGSCAN-STOP are documented in core/res/res/values/config.xml
     //and will need an update if the names are changed
     if (enable) {
-        doBooleanCommand("DRIVER BGSCAN-START", "OK");
-    }
-    else {
-        doBooleanCommand("DRIVER BGSCAN-STOP", "OK");
+        doBooleanCommand("OK", "DRIVER BGSCAN-START");
+    } else {
+        doBooleanCommand("OK", "DRIVER BGSCAN-STOP");
     }
 }
 
-static void android_net_wifi_setScanIntervalCommand(JNIEnv* env, jobject clazz, jint scanInterval)
+static void android_net_wifi_setScanIntervalCommand(JNIEnv* env, jobject, jint scanInterval)
 {
-    char cmdstr[BUF_SIZE];
-
-    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "SCAN_INTERVAL %d", scanInterval);
-
-    if(numWritten < (int)sizeof(cmdstr)) doBooleanCommand(cmdstr, "OK");
+    doBooleanCommand("OK", "SCAN_INTERVAL %d", scanInterval);
 }
 
 
@@ -651,9 +578,6 @@
 
 int register_android_net_wifi_WifiManager(JNIEnv* env)
 {
-    jclass wifi = env->FindClass(WIFI_PKG_NAME);
-    LOG_FATAL_IF(wifi == NULL, "Unable to find class " WIFI_PKG_NAME);
-
     return AndroidRuntime::registerNativeMethods(env,
             WIFI_PKG_NAME, gWifiMethods, NELEM(gWifiMethods));
 }
diff --git a/core/jni/android_nio_utils.cpp b/core/jni/android_nio_utils.cpp
index 584e7a4..7cbbe12 100644
--- a/core/jni/android_nio_utils.cpp
+++ b/core/jni/android_nio_utils.cpp
@@ -32,20 +32,20 @@
     jlong pointer;
     jint offset;
     void *data;
-    
+
     pointer = _env->CallStaticLongMethod(gNioJNI.nioAccessClass,
                                          gNioJNI.getBasePointerID, buffer);
     if (pointer != 0L) {
         *array = NULL;
         return (void *) (jint) pointer;
     }
-    
+
     *array = (jarray) _env->CallStaticObjectMethod(gNioJNI.nioAccessClass,
                                                gNioJNI.getBaseArrayID, buffer);
     offset = _env->CallStaticIntMethod(gNioJNI.nioAccessClass,
                                        gNioJNI.getBaseArrayOffsetID, buffer);
     data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0);
-    
+
     return (void *) ((char *) data + offset);
 }
 
@@ -94,8 +94,7 @@
 }
 
 namespace android {
-    
-int register_android_nio_utils(JNIEnv* env);
+
 int register_android_nio_utils(JNIEnv* env) {
     jclass localClass = findClass(env, "java/nio/NIOAccess");
     gNioJNI.getBasePointerID = findStaticMethod(env, localClass,
diff --git a/core/jni/android_opengl_GLES10.cpp b/core/jni/android_opengl_GLES10.cpp
index 2685d75..6c29d6c 100644
--- a/core/jni/android_opengl_GLES10.cpp
+++ b/core/jni/android_opengl_GLES10.cpp
@@ -2,21 +2,23 @@
 **
 ** 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 
+** 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 
+**     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 
+** 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.
 */
 
 // This source file is automatically generated
 
+#include "jni.h"
+#include "JNIHelp.h"
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/misc.h>
 
@@ -41,10 +43,6 @@
 
 static jclass nioAccessClass;
 static jclass bufferClass;
-static jclass OOMEClass;
-static jclass UOEClass;
-static jclass IAEClass;
-static jclass AIOOBEClass;
 static jmethodID getBasePointerID;
 static jmethodID getBaseArrayID;
 static jmethodID getBaseArrayOffsetID;
@@ -55,7 +53,7 @@
 /* Cache method IDs each time the class is loaded. */
 
 static void
-nativeClassInitBuffer(JNIEnv *_env)
+nativeClassInit(JNIEnv *_env, jclass glImplClass)
 {
     jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
     nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
@@ -76,26 +74,6 @@
         _env->GetFieldID(bufferClass, "_elementSizeShift", "I");
 }
 
-static void
-nativeClassInit(JNIEnv *_env, jclass glImplClass)
-{
-    nativeClassInitBuffer(_env);
-
-    jclass IAEClassLocal =
-        _env->FindClass("java/lang/IllegalArgumentException");
-    jclass OOMEClassLocal =
-         _env->FindClass("java/lang/OutOfMemoryError");
-    jclass UOEClassLocal =
-         _env->FindClass("java/lang/UnsupportedOperationException");
-    jclass AIOOBEClassLocal =
-         _env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
-
-    IAEClass = (jclass) _env->NewGlobalRef(IAEClassLocal);
-    OOMEClass = (jclass) _env->NewGlobalRef(OOMEClassLocal);
-    UOEClass = (jclass) _env->NewGlobalRef(UOEClassLocal);
-    AIOOBEClass = (jclass) _env->NewGlobalRef(AIOOBEClassLocal);
-}
-
 static void *
 getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
 {
@@ -116,13 +94,13 @@
         *array = NULL;
         return (void *) (jint) pointer;
     }
-    
+
     *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
             getBaseArrayID, buffer);
     offset = _env->CallStaticIntMethod(nioAccessClass,
             getBaseArrayOffsetID, buffer);
     data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0);
-    
+
     return (void *) ((char *) data + offset);
 }
 
@@ -141,7 +119,8 @@
         jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
         buf += position << elementSizeShift;
     } else {
-        _env->ThrowNew(IAEClass, "Must use a native order direct Buffer");
+        jniThrowException(_env, "java/lang/IllegalArgumentException",
+                          "Must use a native order direct Buffer");
     }
     return (void*) buf;
 }
@@ -154,7 +133,6 @@
 }
 
 // --------------------------------------------------------------------------
-
 /* void glActiveTexture ( GLenum texture ) */
 static void
 android_glActiveTexture__I
@@ -431,16 +409,16 @@
     GLuint *textures = (GLuint *) 0;
 
     if (!textures_ref) {
-        _env->ThrowNew(IAEClass, "textures == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "textures == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(textures_ref) - offset;
     if (_remaining < n) {
-        _env->ThrowNew(IAEClass, "length - offset < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
         goto exit;
     }
     textures_base = (GLuint *)
@@ -469,7 +447,7 @@
 
     textures = (GLuint *)getPointer(_env, textures_buf, &_array, &_remaining);
     if (_remaining < n) {
-        _env->ThrowNew(IAEClass, "remaining() < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
         goto exit;
     }
     glDeleteTextures(
@@ -560,7 +538,7 @@
 
     indices = (GLvoid *)getPointer(_env, indices_buf, &_array, &_remaining);
     if (_remaining < count) {
-        _env->ThrowNew(AIOOBEClass, "remaining() < count");
+        jniThrowException(_env, "java/lang/ArrayIndexOutOfBoundsException", "remaining() < count");
         goto exit;
     }
     glDrawElements(
@@ -627,11 +605,11 @@
     GLfloat *params = (GLfloat *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -661,7 +639,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfloat *)
@@ -715,7 +693,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glFogfv(
@@ -748,11 +726,11 @@
     GLfixed *params = (GLfixed *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -782,7 +760,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfixed *)
@@ -836,7 +814,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glFogxv(
@@ -898,18 +876,18 @@
 
     if (!textures_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "textures == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "textures == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(textures_ref) - offset;
     if (_remaining < n) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
         goto exit;
     }
     textures_base = (GLuint *)
@@ -940,7 +918,7 @@
     textures = (GLuint *)getPointer(_env, textures_buf, &_array, &_remaining);
     if (_remaining < n) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
         goto exit;
     }
     glGenTextures(
@@ -974,12 +952,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1316,7 +1294,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLint *)
@@ -1678,7 +1656,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glGetIntegerv(
@@ -1692,16 +1670,10 @@
     }
 }
 
-#include <string.h>
-
 /* const GLubyte * glGetString ( GLenum name ) */
-static
-jstring
-android_glGetString
-  (JNIEnv *_env, jobject _this, jint name) {
-    const char * chars = (const char *)glGetString((GLenum)name);
-    jstring output = _env->NewStringUTF(chars);
-    return output;
+static jstring android_glGetString(JNIEnv* _env, jobject, jint name) {
+    const char* chars = (const char*) glGetString((GLenum) name);
+    return _env->NewStringUTF(chars);
 }
 /* void glHint ( GLenum target, GLenum mode ) */
 static void
@@ -1732,11 +1704,11 @@
     GLfloat *params = (GLfloat *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1757,7 +1729,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfloat *)
@@ -1802,7 +1774,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glLightModelfv(
@@ -1835,11 +1807,11 @@
     GLfixed *params = (GLfixed *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1860,7 +1832,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfixed *)
@@ -1905,7 +1877,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glLightModelxv(
@@ -1939,11 +1911,11 @@
     GLfloat *params = (GLfloat *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1990,7 +1962,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfloat *)
@@ -2062,7 +2034,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glLightfv(
@@ -2097,11 +2069,11 @@
     GLfixed *params = (GLfixed *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2148,7 +2120,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfixed *)
@@ -2220,7 +2192,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glLightxv(
@@ -2269,11 +2241,11 @@
     GLfloat *m = (GLfloat *) 0;
 
     if (!m_ref) {
-        _env->ThrowNew(IAEClass, "m == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "m == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(m_ref) - offset;
@@ -2318,11 +2290,11 @@
     GLfixed *m = (GLfixed *) 0;
 
     if (!m_ref) {
-        _env->ThrowNew(IAEClass, "m == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "m == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(m_ref) - offset;
@@ -2387,11 +2359,11 @@
     GLfloat *params = (GLfloat *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2424,7 +2396,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfloat *)
@@ -2482,7 +2454,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glMaterialfv(
@@ -2517,11 +2489,11 @@
     GLfixed *params = (GLfixed *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2554,7 +2526,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfixed *)
@@ -2612,7 +2584,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glMaterialxv(
@@ -2645,11 +2617,11 @@
     GLfloat *m = (GLfloat *) 0;
 
     if (!m_ref) {
-        _env->ThrowNew(IAEClass, "m == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "m == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(m_ref) - offset;
@@ -2694,11 +2666,11 @@
     GLfixed *m = (GLfixed *) 0;
 
     if (!m_ref) {
-        _env->ThrowNew(IAEClass, "m == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "m == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(m_ref) - offset;
@@ -3079,11 +3051,11 @@
     GLfloat *params = (GLfloat *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -3110,7 +3082,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfloat *)
@@ -3162,7 +3134,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glTexEnvfv(
@@ -3197,11 +3169,11 @@
     GLfixed *params = (GLfixed *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -3228,7 +3200,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfixed *)
@@ -3280,7 +3252,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glTexEnvxv(
diff --git a/core/jni/android_opengl_GLES10Ext.cpp b/core/jni/android_opengl_GLES10Ext.cpp
index f17ef21..1154cef 100644
--- a/core/jni/android_opengl_GLES10Ext.cpp
+++ b/core/jni/android_opengl_GLES10Ext.cpp
@@ -2,21 +2,23 @@
 **
 ** 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 
+** 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 
+**     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 
+** 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.
 */
 
 // This source file is automatically generated
 
+#include "jni.h"
+#include "JNIHelp.h"
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/misc.h>
 
@@ -28,10 +30,6 @@
 
 static jclass nioAccessClass;
 static jclass bufferClass;
-static jclass OOMEClass;
-static jclass UOEClass;
-static jclass IAEClass;
-static jclass AIOOBEClass;
 static jmethodID getBasePointerID;
 static jmethodID getBaseArrayID;
 static jmethodID getBaseArrayOffsetID;
@@ -42,7 +40,7 @@
 /* Cache method IDs each time the class is loaded. */
 
 static void
-nativeClassInitBuffer(JNIEnv *_env)
+nativeClassInit(JNIEnv *_env, jclass glImplClass)
 {
     jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
     nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
@@ -64,26 +62,6 @@
 }
 
 
-static void
-nativeClassInit(JNIEnv *_env, jclass glImplClass)
-{
-    nativeClassInitBuffer(_env);
-
-    jclass IAEClassLocal =
-        _env->FindClass("java/lang/IllegalArgumentException");
-    jclass OOMEClassLocal =
-         _env->FindClass("java/lang/OutOfMemoryError");
-    jclass UOEClassLocal =
-         _env->FindClass("java/lang/UnsupportedOperationException");
-    jclass AIOOBEClassLocal =
-         _env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
-
-    IAEClass = (jclass) _env->NewGlobalRef(IAEClassLocal);
-    OOMEClass = (jclass) _env->NewGlobalRef(OOMEClassLocal);
-    UOEClass = (jclass) _env->NewGlobalRef(UOEClassLocal);
-    AIOOBEClass = (jclass) _env->NewGlobalRef(AIOOBEClassLocal);
-}
-
 static void *
 getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
 {
@@ -104,13 +82,13 @@
         *array = NULL;
         return (void *) (jint) pointer;
     }
-    
+
     *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
             getBaseArrayID, buffer);
     offset = _env->CallStaticIntMethod(nioAccessClass,
             getBaseArrayOffsetID, buffer);
     data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0);
-    
+
     return (void *) ((char *) data + offset);
 }
 
@@ -123,7 +101,6 @@
 }
 
 // --------------------------------------------------------------------------
-
 /* GLbitfield glQueryMatrixxOES ( GLfixed *mantissa, GLint *exponent ) */
 static jint
 android_glQueryMatrixxOES___3II_3II
@@ -139,18 +116,18 @@
 
     if (!mantissa_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "mantissa == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "mantissa == null");
         goto exit;
     }
     if (mantissaOffset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "mantissaOffset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "mantissaOffset < 0");
         goto exit;
     }
     _mantissaRemaining = _env->GetArrayLength(mantissa_ref) - mantissaOffset;
     if (_mantissaRemaining < 16) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - mantissaOffset < 16");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - mantissaOffset < 16");
         goto exit;
     }
     mantissa_base = (GLfixed *)
@@ -159,18 +136,18 @@
 
     if (!exponent_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "exponent == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "exponent == null");
         goto exit;
     }
     if (exponentOffset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "exponentOffset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "exponentOffset < 0");
         goto exit;
     }
     _exponentRemaining = _env->GetArrayLength(exponent_ref) - exponentOffset;
     if (_exponentRemaining < 16) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - exponentOffset < 16");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - exponentOffset < 16");
         goto exit;
     }
     exponent_base = (GLint *)
@@ -210,13 +187,13 @@
     mantissa = (GLfixed *)getPointer(_env, mantissa_buf, &_mantissaArray, &_mantissaRemaining);
     if (_mantissaRemaining < 16) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < 16");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 16");
         goto exit;
     }
     exponent = (GLint *)getPointer(_env, exponent_buf, &_exponentArray, &_exponentRemaining);
     if (_exponentRemaining < 16) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < 16");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 16");
         goto exit;
     }
     _returnValue = glQueryMatrixxOES(
diff --git a/core/jni/android_opengl_GLES11.cpp b/core/jni/android_opengl_GLES11.cpp
index 1c326ba..d038f20 100644
--- a/core/jni/android_opengl_GLES11.cpp
+++ b/core/jni/android_opengl_GLES11.cpp
@@ -2,21 +2,23 @@
 **
 ** 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 
+** 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 
+**     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 
+** 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.
 */
 
 // This source file is automatically generated
 
+#include "jni.h"
+#include "JNIHelp.h"
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/misc.h>
 
@@ -35,10 +37,6 @@
 
 static jclass nioAccessClass;
 static jclass bufferClass;
-static jclass OOMEClass;
-static jclass UOEClass;
-static jclass IAEClass;
-static jclass AIOOBEClass;
 static jmethodID getBasePointerID;
 static jmethodID getBaseArrayID;
 static jmethodID getBaseArrayOffsetID;
@@ -49,7 +47,7 @@
 /* Cache method IDs each time the class is loaded. */
 
 static void
-nativeClassInitBuffer(JNIEnv *_env)
+nativeClassInit(JNIEnv *_env, jclass glImplClass)
 {
     jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
     nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
@@ -71,26 +69,6 @@
 }
 
 
-static void
-nativeClassInit(JNIEnv *_env, jclass glImplClass)
-{
-    nativeClassInitBuffer(_env);
-
-    jclass IAEClassLocal =
-        _env->FindClass("java/lang/IllegalArgumentException");
-    jclass OOMEClassLocal =
-         _env->FindClass("java/lang/OutOfMemoryError");
-    jclass UOEClassLocal =
-         _env->FindClass("java/lang/UnsupportedOperationException");
-    jclass AIOOBEClassLocal =
-         _env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
-
-    IAEClass = (jclass) _env->NewGlobalRef(IAEClassLocal);
-    OOMEClass = (jclass) _env->NewGlobalRef(OOMEClassLocal);
-    UOEClass = (jclass) _env->NewGlobalRef(UOEClassLocal);
-    AIOOBEClass = (jclass) _env->NewGlobalRef(AIOOBEClassLocal);
-}
-
 static void *
 getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
 {
@@ -111,13 +89,13 @@
         *array = NULL;
         return (void *) (jint) pointer;
     }
-    
+
     *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
             getBaseArrayID, buffer);
     offset = _env->CallStaticIntMethod(nioAccessClass,
             getBaseArrayOffsetID, buffer);
     data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0);
-    
+
     return (void *) ((char *) data + offset);
 }
 
@@ -137,13 +115,13 @@
         jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
         buf += position << elementSizeShift;
     } else {
-        _env->ThrowNew(IAEClass, "Must use a native order direct Buffer");
+        jniThrowException(_env, "java/lang/IllegalArgumentException",
+                          "Must use a native order direct Buffer");
     }
     return (void*) buf;
 }
 
 // --------------------------------------------------------------------------
-
 /* void glBindBuffer ( GLenum target, GLuint buffer ) */
 static void
 android_glBindBuffer__II
@@ -165,7 +143,7 @@
     if (data_buf) {
         data = (GLvoid *)getPointer(_env, data_buf, &_array, &_remaining);
         if (_remaining < size) {
-            _env->ThrowNew(IAEClass, "remaining() < size");
+            jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < size");
             goto exit;
         }
     }
@@ -192,7 +170,7 @@
 
     data = (GLvoid *)getPointer(_env, data_buf, &_array, &_remaining);
     if (_remaining < size) {
-        _env->ThrowNew(IAEClass, "remaining() < size");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < size");
         goto exit;
     }
     glBufferSubData(
@@ -217,11 +195,11 @@
     GLfloat *equation = (GLfloat *) 0;
 
     if (!equation_ref) {
-        _env->ThrowNew(IAEClass, "equation == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "equation == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(equation_ref) - offset;
@@ -268,11 +246,11 @@
     GLfixed *equation = (GLfixed *) 0;
 
     if (!equation_ref) {
-        _env->ThrowNew(IAEClass, "equation == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "equation == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(equation_ref) - offset;
@@ -343,16 +321,16 @@
     GLuint *buffers = (GLuint *) 0;
 
     if (!buffers_ref) {
-        _env->ThrowNew(IAEClass, "buffers == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "buffers == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(buffers_ref) - offset;
     if (_remaining < n) {
-        _env->ThrowNew(IAEClass, "length - offset < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
         goto exit;
     }
     buffers_base = (GLuint *)
@@ -381,7 +359,7 @@
 
     buffers = (GLuint *)getPointer(_env, buffers_buf, &_array, &_remaining);
     if (_remaining < n) {
-        _env->ThrowNew(IAEClass, "remaining() < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
         goto exit;
     }
     glDeleteBuffers(
@@ -418,18 +396,18 @@
 
     if (!buffers_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "buffers == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "buffers == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(buffers_ref) - offset;
     if (_remaining < n) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
         goto exit;
     }
     buffers_base = (GLuint *)
@@ -460,7 +438,7 @@
     buffers = (GLuint *)getPointer(_env, buffers_buf, &_array, &_remaining);
     if (_remaining < n) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
         goto exit;
     }
     glGenBuffers(
@@ -485,12 +463,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -540,18 +518,18 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
     if (_remaining < 1) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
         goto exit;
     }
     params_base = (GLint *)
@@ -583,7 +561,7 @@
     params = (GLint *)getPointer(_env, params_buf, &_array, &_remaining);
     if (_remaining < 1) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
         goto exit;
     }
     glGetBufferParameteriv(
@@ -609,12 +587,12 @@
 
     if (!eqn_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "eqn == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "eqn == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(eqn_ref) - offset;
@@ -664,12 +642,12 @@
 
     if (!eqn_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "eqn == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "eqn == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(eqn_ref) - offset;
@@ -719,12 +697,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -774,12 +752,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -829,12 +807,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -882,7 +860,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfloat *)
@@ -956,7 +934,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glGetLightfv(
@@ -982,12 +960,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1035,7 +1013,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfixed *)
@@ -1109,7 +1087,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glGetLightxv(
@@ -1135,12 +1113,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1174,7 +1152,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfloat *)
@@ -1234,7 +1212,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glGetMaterialfv(
@@ -1260,12 +1238,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1299,7 +1277,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfixed *)
@@ -1359,7 +1337,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glGetMaterialxv(
@@ -1385,12 +1363,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1418,7 +1396,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfloat *)
@@ -1472,7 +1450,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glGetTexEnvfv(
@@ -1498,12 +1476,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1531,7 +1509,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLint *)
@@ -1585,7 +1563,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glGetTexEnviv(
@@ -1611,12 +1589,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1644,7 +1622,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfixed *)
@@ -1698,7 +1676,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glGetTexEnvxv(
@@ -1724,18 +1702,18 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
     if (_remaining < 1) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
         goto exit;
     }
     params_base = (GLfloat *)
@@ -1767,7 +1745,7 @@
     params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining);
     if (_remaining < 1) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
         goto exit;
     }
     glGetTexParameterfv(
@@ -1793,18 +1771,18 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
     if (_remaining < 1) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
         goto exit;
     }
     params_base = (GLint *)
@@ -1836,7 +1814,7 @@
     params = (GLint *)getPointer(_env, params_buf, &_array, &_remaining);
     if (_remaining < 1) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
         goto exit;
     }
     glGetTexParameteriv(
@@ -1862,18 +1840,18 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
     if (_remaining < 1) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
         goto exit;
     }
     params_base = (GLfixed *)
@@ -1905,7 +1883,7 @@
     params = (GLfixed *)getPointer(_env, params_buf, &_array, &_remaining);
     if (_remaining < 1) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
         goto exit;
     }
     glGetTexParameterxv(
@@ -1983,16 +1961,16 @@
     GLfloat *params = (GLfloat *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
     if (_remaining < 1) {
-        _env->ThrowNew(IAEClass, "length - offset < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
         goto exit;
     }
     params_base = (GLfloat *)
@@ -2021,7 +1999,7 @@
 
     params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining);
     if (_remaining < 1) {
-        _env->ThrowNew(IAEClass, "remaining() < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
         goto exit;
     }
     glPointParameterfv(
@@ -2054,16 +2032,16 @@
     GLfixed *params = (GLfixed *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
     if (_remaining < 1) {
-        _env->ThrowNew(IAEClass, "length - offset < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
         goto exit;
     }
     params_base = (GLfixed *)
@@ -2092,7 +2070,7 @@
 
     params = (GLfixed *)getPointer(_env, params_buf, &_array, &_remaining);
     if (_remaining < 1) {
-        _env->ThrowNew(IAEClass, "remaining() < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
         goto exit;
     }
     glPointParameterxv(
@@ -2160,11 +2138,11 @@
     GLint *params = (GLint *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2191,7 +2169,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLint *)
@@ -2243,7 +2221,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glTexEnviv(
@@ -2267,16 +2245,16 @@
     GLfloat *params = (GLfloat *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
     if (_remaining < 1) {
-        _env->ThrowNew(IAEClass, "length - offset < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
         goto exit;
     }
     params_base = (GLfloat *)
@@ -2306,7 +2284,7 @@
 
     params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining);
     if (_remaining < 1) {
-        _env->ThrowNew(IAEClass, "remaining() < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
         goto exit;
     }
     glTexParameterfv(
@@ -2341,16 +2319,16 @@
     GLint *params = (GLint *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
     if (_remaining < 1) {
-        _env->ThrowNew(IAEClass, "length - offset < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
         goto exit;
     }
     params_base = (GLint *)
@@ -2380,7 +2358,7 @@
 
     params = (GLint *)getPointer(_env, params_buf, &_array, &_remaining);
     if (_remaining < 1) {
-        _env->ThrowNew(IAEClass, "remaining() < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
         goto exit;
     }
     glTexParameteriv(
@@ -2404,16 +2382,16 @@
     GLfixed *params = (GLfixed *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
     if (_remaining < 1) {
-        _env->ThrowNew(IAEClass, "length - offset < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
         goto exit;
     }
     params_base = (GLfixed *)
@@ -2443,7 +2421,7 @@
 
     params = (GLfixed *)getPointer(_env, params_buf, &_array, &_remaining);
     if (_remaining < 1) {
-        _env->ThrowNew(IAEClass, "remaining() < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
         goto exit;
     }
     glTexParameterxv(
diff --git a/core/jni/android_opengl_GLES11Ext.cpp b/core/jni/android_opengl_GLES11Ext.cpp
index 1390506..d6dc0fe 100644
--- a/core/jni/android_opengl_GLES11Ext.cpp
+++ b/core/jni/android_opengl_GLES11Ext.cpp
@@ -2,21 +2,23 @@
 **
 ** 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 
+** 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 
+**     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 
+** 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.
 */
 
 // This source file is automatically generated
 
+#include "jni.h"
+#include "JNIHelp.h"
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/misc.h>
 
@@ -37,10 +39,6 @@
 
 static jclass nioAccessClass;
 static jclass bufferClass;
-static jclass OOMEClass;
-static jclass UOEClass;
-static jclass IAEClass;
-static jclass AIOOBEClass;
 static jmethodID getBasePointerID;
 static jmethodID getBaseArrayID;
 static jmethodID getBaseArrayOffsetID;
@@ -51,7 +49,7 @@
 /* Cache method IDs each time the class is loaded. */
 
 static void
-nativeClassInitBuffer(JNIEnv *_env)
+nativeClassInit(JNIEnv *_env, jclass glImplClass)
 {
     jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
     nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
@@ -73,26 +71,6 @@
 }
 
 
-static void
-nativeClassInit(JNIEnv *_env, jclass glImplClass)
-{
-    nativeClassInitBuffer(_env);
-
-    jclass IAEClassLocal =
-        _env->FindClass("java/lang/IllegalArgumentException");
-    jclass OOMEClassLocal =
-         _env->FindClass("java/lang/OutOfMemoryError");
-    jclass UOEClassLocal =
-         _env->FindClass("java/lang/UnsupportedOperationException");
-    jclass AIOOBEClassLocal =
-         _env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
-
-    IAEClass = (jclass) _env->NewGlobalRef(IAEClassLocal);
-    OOMEClass = (jclass) _env->NewGlobalRef(OOMEClassLocal);
-    UOEClass = (jclass) _env->NewGlobalRef(UOEClassLocal);
-    AIOOBEClass = (jclass) _env->NewGlobalRef(AIOOBEClassLocal);
-}
-
 static void *
 getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
 {
@@ -113,13 +91,13 @@
         *array = NULL;
         return (void *) (jint) pointer;
     }
-    
+
     *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
             getBaseArrayID, buffer);
     offset = _env->CallStaticIntMethod(nioAccessClass,
             getBaseArrayOffsetID, buffer);
     data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0);
-    
+
     return (void *) ((char *) data + offset);
 }
 
@@ -139,12 +117,12 @@
         jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
         buf += position << elementSizeShift;
     } else {
-        _env->ThrowNew(IAEClass, "Must use a native order direct Buffer");
+        jniThrowException(_env, "java/lang/IllegalArgumentException",
+                          "Must use a native order direct Buffer");
     }
     return (void*) buf;
 }
 // --------------------------------------------------------------------------
-
 /* void glBlendEquationSeparateOES ( GLenum modeRGB, GLenum modeAlpha ) */
 static void
 android_glBlendEquationSeparateOES__II
@@ -224,16 +202,16 @@
     GLshort *coords = (GLshort *) 0;
 
     if (!coords_ref) {
-        _env->ThrowNew(IAEClass, "coords == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "coords == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(coords_ref) - offset;
     if (_remaining < 5) {
-        _env->ThrowNew(IAEClass, "length - offset < 5");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 5");
         goto exit;
     }
     coords_base = (GLshort *)
@@ -261,7 +239,7 @@
 
     coords = (GLshort *)getPointer(_env, coords_buf, &_array, &_remaining);
     if (_remaining < 5) {
-        _env->ThrowNew(IAEClass, "remaining() < 5");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 5");
         goto exit;
     }
     glDrawTexsvOES(
@@ -283,16 +261,16 @@
     GLint *coords = (GLint *) 0;
 
     if (!coords_ref) {
-        _env->ThrowNew(IAEClass, "coords == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "coords == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(coords_ref) - offset;
     if (_remaining < 5) {
-        _env->ThrowNew(IAEClass, "length - offset < 5");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 5");
         goto exit;
     }
     coords_base = (GLint *)
@@ -320,7 +298,7 @@
 
     coords = (GLint *)getPointer(_env, coords_buf, &_array, &_remaining);
     if (_remaining < 5) {
-        _env->ThrowNew(IAEClass, "remaining() < 5");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 5");
         goto exit;
     }
     glDrawTexivOES(
@@ -342,16 +320,16 @@
     GLfixed *coords = (GLfixed *) 0;
 
     if (!coords_ref) {
-        _env->ThrowNew(IAEClass, "coords == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "coords == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(coords_ref) - offset;
     if (_remaining < 5) {
-        _env->ThrowNew(IAEClass, "length - offset < 5");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 5");
         goto exit;
     }
     coords_base = (GLfixed *)
@@ -379,7 +357,7 @@
 
     coords = (GLfixed *)getPointer(_env, coords_buf, &_array, &_remaining);
     if (_remaining < 5) {
-        _env->ThrowNew(IAEClass, "remaining() < 5");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 5");
         goto exit;
     }
     glDrawTexxvOES(
@@ -414,16 +392,16 @@
     GLfloat *coords = (GLfloat *) 0;
 
     if (!coords_ref) {
-        _env->ThrowNew(IAEClass, "coords == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "coords == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(coords_ref) - offset;
     if (_remaining < 5) {
-        _env->ThrowNew(IAEClass, "length - offset < 5");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 5");
         goto exit;
     }
     coords_base = (GLfloat *)
@@ -451,7 +429,7 @@
 
     coords = (GLfloat *)getPointer(_env, coords_buf, &_array, &_remaining);
     if (_remaining < 5) {
-        _env->ThrowNew(IAEClass, "remaining() < 5");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 5");
         goto exit;
     }
     glDrawTexfvOES(
@@ -542,11 +520,11 @@
     GLfixed *equation = (GLfixed *) 0;
 
     if (!equation_ref) {
-        _env->ThrowNew(IAEClass, "equation == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "equation == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(equation_ref) - offset;
@@ -625,11 +603,11 @@
     GLfixed *params = (GLfixed *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -692,18 +670,18 @@
 
     if (!eqn_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "eqn == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "eqn == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(eqn_ref) - offset;
     if (_remaining < 4) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < 4");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 4");
         goto exit;
     }
     eqn_base = (GLfixed *)
@@ -734,7 +712,7 @@
     eqn = (GLfixed *)getPointer(_env, eqn_buf, &_array, &_remaining);
     if (_remaining < 4) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < 4");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 4");
         goto exit;
     }
     glGetClipPlanexOES(
@@ -759,12 +737,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -814,12 +792,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -871,12 +849,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -928,12 +906,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -985,12 +963,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1050,11 +1028,11 @@
     GLfixed *params = (GLfixed *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1112,11 +1090,11 @@
     GLfixed *params = (GLfixed *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1174,11 +1152,11 @@
     GLfixed *m = (GLfixed *) 0;
 
     if (!m_ref) {
-        _env->ThrowNew(IAEClass, "m == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "m == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(m_ref) - offset;
@@ -1234,11 +1212,11 @@
     GLfixed *params = (GLfixed *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1287,11 +1265,11 @@
     GLfixed *m = (GLfixed *) 0;
 
     if (!m_ref) {
-        _env->ThrowNew(IAEClass, "m == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "m == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(m_ref) - offset;
@@ -1384,11 +1362,11 @@
     GLfixed *params = (GLfixed *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1498,11 +1476,11 @@
     GLfixed *params = (GLfixed *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1562,11 +1540,11 @@
     GLfixed *params = (GLfixed *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1647,16 +1625,16 @@
     GLuint *renderbuffers = (GLuint *) 0;
 
     if (!renderbuffers_ref) {
-        _env->ThrowNew(IAEClass, "renderbuffers == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "renderbuffers == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(renderbuffers_ref) - offset;
     if (_remaining < n) {
-        _env->ThrowNew(IAEClass, "length - offset < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
         goto exit;
     }
     renderbuffers_base = (GLuint *)
@@ -1685,7 +1663,7 @@
 
     renderbuffers = (GLuint *)getPointer(_env, renderbuffers_buf, &_array, &_remaining);
     if (_remaining < n) {
-        _env->ThrowNew(IAEClass, "remaining() < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
         goto exit;
     }
     glDeleteRenderbuffersOES(
@@ -1710,18 +1688,18 @@
 
     if (!renderbuffers_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "renderbuffers == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "renderbuffers == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(renderbuffers_ref) - offset;
     if (_remaining < n) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
         goto exit;
     }
     renderbuffers_base = (GLuint *)
@@ -1752,7 +1730,7 @@
     renderbuffers = (GLuint *)getPointer(_env, renderbuffers_buf, &_array, &_remaining);
     if (_remaining < n) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
         goto exit;
     }
     glGenRenderbuffersOES(
@@ -1789,18 +1767,18 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
     if (_remaining < 1) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
         goto exit;
     }
     params_base = (GLint *)
@@ -1832,7 +1810,7 @@
     params = (GLint *)getPointer(_env, params_buf, &_array, &_remaining);
     if (_remaining < 1) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
         goto exit;
     }
     glGetRenderbufferParameterivOES(
@@ -1877,16 +1855,16 @@
     GLuint *framebuffers = (GLuint *) 0;
 
     if (!framebuffers_ref) {
-        _env->ThrowNew(IAEClass, "framebuffers == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "framebuffers == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(framebuffers_ref) - offset;
     if (_remaining < n) {
-        _env->ThrowNew(IAEClass, "length - offset < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
         goto exit;
     }
     framebuffers_base = (GLuint *)
@@ -1915,7 +1893,7 @@
 
     framebuffers = (GLuint *)getPointer(_env, framebuffers_buf, &_array, &_remaining);
     if (_remaining < n) {
-        _env->ThrowNew(IAEClass, "remaining() < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
         goto exit;
     }
     glDeleteFramebuffersOES(
@@ -1940,18 +1918,18 @@
 
     if (!framebuffers_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "framebuffers == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "framebuffers == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(framebuffers_ref) - offset;
     if (_remaining < n) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
         goto exit;
     }
     framebuffers_base = (GLuint *)
@@ -1982,7 +1960,7 @@
     framebuffers = (GLuint *)getPointer(_env, framebuffers_buf, &_array, &_remaining);
     if (_remaining < n) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
         goto exit;
     }
     glGenFramebuffersOES(
@@ -2043,18 +2021,18 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
     if (_remaining < 1) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
         goto exit;
     }
     params_base = (GLint *)
@@ -2087,7 +2065,7 @@
     params = (GLint *)getPointer(_env, params_buf, &_array, &_remaining);
     if (_remaining < 1) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
         goto exit;
     }
     glGetFramebufferAttachmentParameterivOES(
@@ -2221,11 +2199,11 @@
     GLfloat *equation = (GLfloat *) 0;
 
     if (!equation_ref) {
-        _env->ThrowNew(IAEClass, "equation == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "equation == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(equation_ref) - offset;
@@ -2274,18 +2252,18 @@
 
     if (!eqn_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "eqn == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "eqn == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(eqn_ref) - offset;
     if (_remaining < 4) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < 4");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 4");
         goto exit;
     }
     eqn_base = (GLfloat *)
@@ -2316,7 +2294,7 @@
     eqn = (GLfloat *)getPointer(_env, eqn_buf, &_array, &_remaining);
     if (_remaining < 4) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < 4");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 4");
         goto exit;
     }
     glGetClipPlanefOES(
@@ -2359,11 +2337,11 @@
     GLfloat *params = (GLfloat *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2423,11 +2401,11 @@
     GLint *params = (GLint *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2487,11 +2465,11 @@
     GLfixed *params = (GLfixed *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2542,12 +2520,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2599,12 +2577,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2656,12 +2634,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
diff --git a/core/jni/android_opengl_GLES20.cpp b/core/jni/android_opengl_GLES20.cpp
index 7ac0f6e..a53e4d7 100644
--- a/core/jni/android_opengl_GLES20.cpp
+++ b/core/jni/android_opengl_GLES20.cpp
@@ -2,21 +2,23 @@
 **
 ** 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 
+** 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 
+**     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 
+** 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.
 */
 
 // This source file is automatically generated
 
+#include "jni.h"
+#include "JNIHelp.h"
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/misc.h>
 
@@ -28,10 +30,6 @@
 
 static jclass nioAccessClass;
 static jclass bufferClass;
-static jclass OOMEClass;
-static jclass UOEClass;
-static jclass IAEClass;
-static jclass AIOOBEClass;
 static jmethodID getBasePointerID;
 static jmethodID getBaseArrayID;
 static jmethodID getBaseArrayOffsetID;
@@ -42,7 +40,7 @@
 /* Cache method IDs each time the class is loaded. */
 
 static void
-nativeClassInitBuffer(JNIEnv *_env)
+nativeClassInit(JNIEnv *_env, jclass glImplClass)
 {
     jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
     nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
@@ -64,26 +62,6 @@
 }
 
 
-static void
-nativeClassInit(JNIEnv *_env, jclass glImplClass)
-{
-    nativeClassInitBuffer(_env);
-
-    jclass IAEClassLocal =
-        _env->FindClass("java/lang/IllegalArgumentException");
-    jclass OOMEClassLocal =
-         _env->FindClass("java/lang/OutOfMemoryError");
-    jclass UOEClassLocal =
-         _env->FindClass("java/lang/UnsupportedOperationException");
-    jclass AIOOBEClassLocal =
-         _env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
-
-    IAEClass = (jclass) _env->NewGlobalRef(IAEClassLocal);
-    OOMEClass = (jclass) _env->NewGlobalRef(OOMEClassLocal);
-    UOEClass = (jclass) _env->NewGlobalRef(UOEClassLocal);
-    AIOOBEClass = (jclass) _env->NewGlobalRef(AIOOBEClassLocal);
-}
-
 static void *
 getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
 {
@@ -104,13 +82,13 @@
         *array = NULL;
         return (void *) (jint) pointer;
     }
-    
+
     *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
             getBaseArrayID, buffer);
     offset = _env->CallStaticIntMethod(nioAccessClass,
             getBaseArrayOffsetID, buffer);
     data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0);
-    
+
     return (void *) ((char *) data + offset);
 }
 
@@ -130,7 +108,8 @@
         jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
         buf += position << elementSizeShift;
     } else {
-        _env->ThrowNew(IAEClass, "Must use a native order direct Buffer");
+        jniThrowException(_env, "java/lang/IllegalArgumentException",
+                          "Must use a native order direct Buffer");
     }
     return (void*) buf;
 }
@@ -148,7 +127,6 @@
 }
 
 // --------------------------------------------------------------------------
-
 /* void glActiveTexture ( GLenum texture ) */
 static void
 android_glActiveTexture__I
@@ -175,7 +153,7 @@
     const char* _nativename = 0;
 
     if (!name) {
-        _env->ThrowNew(IAEClass, "name == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "name == null");
         goto exit;
     }
     _nativename = _env->GetStringUTFChars(name, 0);
@@ -297,7 +275,7 @@
     if (data_buf) {
         data = (GLvoid *)getPointer(_env, data_buf, &_array, &_remaining);
         if (_remaining < size) {
-            _env->ThrowNew(IAEClass, "remaining() < size");
+            jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < size");
             goto exit;
         }
     }
@@ -324,7 +302,7 @@
 
     data = (GLvoid *)getPointer(_env, data_buf, &_array, &_remaining);
     if (_remaining < size) {
-        _env->ThrowNew(IAEClass, "remaining() < size");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < size");
         goto exit;
     }
     glBufferSubData(
@@ -530,16 +508,16 @@
     GLuint *buffers = (GLuint *) 0;
 
     if (!buffers_ref) {
-        _env->ThrowNew(IAEClass, "buffers == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "buffers == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(buffers_ref) - offset;
     if (_remaining < n) {
-        _env->ThrowNew(IAEClass, "length - offset < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
         goto exit;
     }
     buffers_base = (GLuint *)
@@ -568,7 +546,7 @@
 
     buffers = (GLuint *)getPointer(_env, buffers_buf, &_array, &_remaining);
     if (_remaining < n) {
-        _env->ThrowNew(IAEClass, "remaining() < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
         goto exit;
     }
     glDeleteBuffers(
@@ -591,11 +569,11 @@
     GLuint *framebuffers = (GLuint *) 0;
 
     if (!framebuffers_ref) {
-        _env->ThrowNew(IAEClass, "framebuffers == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "framebuffers == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(framebuffers_ref) - offset;
@@ -651,11 +629,11 @@
     GLuint *renderbuffers = (GLuint *) 0;
 
     if (!renderbuffers_ref) {
-        _env->ThrowNew(IAEClass, "renderbuffers == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "renderbuffers == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(renderbuffers_ref) - offset;
@@ -711,16 +689,16 @@
     GLuint *textures = (GLuint *) 0;
 
     if (!textures_ref) {
-        _env->ThrowNew(IAEClass, "textures == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "textures == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(textures_ref) - offset;
     if (_remaining < n) {
-        _env->ThrowNew(IAEClass, "length - offset < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
         goto exit;
     }
     textures_base = (GLuint *)
@@ -749,7 +727,7 @@
 
     textures = (GLuint *)getPointer(_env, textures_buf, &_array, &_remaining);
     if (_remaining < n) {
-        _env->ThrowNew(IAEClass, "remaining() < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
         goto exit;
     }
     glDeleteTextures(
@@ -852,7 +830,7 @@
 
     indices = (GLvoid *)getPointer(_env, indices_buf, &_array, &_remaining);
     if (_remaining < count) {
-        _env->ThrowNew(AIOOBEClass, "remaining() < count");
+        jniThrowException(_env, "java/lang/ArrayIndexOutOfBoundsException", "remaining() < count");
         goto exit;
     }
     glDrawElements(
@@ -945,18 +923,18 @@
 
     if (!buffers_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "buffers == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "buffers == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(buffers_ref) - offset;
     if (_remaining < n) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
         goto exit;
     }
     buffers_base = (GLuint *)
@@ -987,7 +965,7 @@
     buffers = (GLuint *)getPointer(_env, buffers_buf, &_array, &_remaining);
     if (_remaining < n) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
         goto exit;
     }
     glGenBuffers(
@@ -1021,12 +999,12 @@
 
     if (!framebuffers_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "framebuffers == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "framebuffers == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(framebuffers_ref) - offset;
@@ -1076,12 +1054,12 @@
 
     if (!renderbuffers_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "renderbuffers == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "renderbuffers == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(renderbuffers_ref) - offset;
@@ -1131,18 +1109,18 @@
 
     if (!textures_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "textures == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "textures == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(textures_ref) - offset;
     if (_remaining < n) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
         goto exit;
     }
     textures_base = (GLuint *)
@@ -1173,7 +1151,7 @@
     textures = (GLuint *)getPointer(_env, textures_buf, &_array, &_remaining);
     if (_remaining < n) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
         goto exit;
     }
     glGenTextures(
@@ -1207,12 +1185,12 @@
 
     if (!length_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length == null");
         goto exit;
     }
     if (lengthOffset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "lengthOffset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "lengthOffset < 0");
         goto exit;
     }
     _lengthRemaining = _env->GetArrayLength(length_ref) - lengthOffset;
@@ -1222,12 +1200,12 @@
 
     if (!size_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "size == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "size == null");
         goto exit;
     }
     if (sizeOffset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "sizeOffset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "sizeOffset < 0");
         goto exit;
     }
     _sizeRemaining = _env->GetArrayLength(size_ref) - sizeOffset;
@@ -1237,12 +1215,12 @@
 
     if (!type_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "type == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "type == null");
         goto exit;
     }
     if (typeOffset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "typeOffset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "typeOffset < 0");
         goto exit;
     }
     _typeRemaining = _env->GetArrayLength(type_ref) - typeOffset;
@@ -1252,12 +1230,12 @@
 
     if (!name_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "name == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "name == null");
         goto exit;
     }
     if (nameOffset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "nameOffset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "nameOffset < 0");
         goto exit;
     }
     _nameRemaining = _env->GetArrayLength(name_ref) - nameOffset;
@@ -1352,12 +1330,12 @@
 
     if (!length_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length == null");
         goto exit;
     }
     if (lengthOffset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "lengthOffset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "lengthOffset < 0");
         goto exit;
     }
     _lengthRemaining = _env->GetArrayLength(length_ref) - lengthOffset;
@@ -1367,12 +1345,12 @@
 
     if (!size_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "size == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "size == null");
         goto exit;
     }
     if (sizeOffset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "sizeOffset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "sizeOffset < 0");
         goto exit;
     }
     _sizeRemaining = _env->GetArrayLength(size_ref) - sizeOffset;
@@ -1382,12 +1360,12 @@
 
     if (!type_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "type == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "type == null");
         goto exit;
     }
     if (typeOffset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "typeOffset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "typeOffset < 0");
         goto exit;
     }
     _typeRemaining = _env->GetArrayLength(type_ref) - typeOffset;
@@ -1397,12 +1375,12 @@
 
     if (!name_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "name == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "name == null");
         goto exit;
     }
     if (nameOffset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "nameOffset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "nameOffset < 0");
         goto exit;
     }
     _nameRemaining = _env->GetArrayLength(name_ref) - nameOffset;
@@ -1491,12 +1469,12 @@
 
     if (!count_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "count == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "count == null");
         goto exit;
     }
     if (countOffset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "countOffset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "countOffset < 0");
         goto exit;
     }
     _countRemaining = _env->GetArrayLength(count_ref) - countOffset;
@@ -1506,12 +1484,12 @@
 
     if (!shaders_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "shaders == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "shaders == null");
         goto exit;
     }
     if (shadersOffset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "shadersOffset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "shadersOffset < 0");
         goto exit;
     }
     _shadersRemaining = _env->GetArrayLength(shaders_ref) - shadersOffset;
@@ -1573,7 +1551,7 @@
     const char* _nativename = 0;
 
     if (!name) {
-        _env->ThrowNew(IAEClass, "name == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "name == null");
         goto exit;
     }
     _nativename = _env->GetStringUTFChars(name, 0);
@@ -1602,12 +1580,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1657,18 +1635,18 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
     if (_remaining < 1) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
         goto exit;
     }
     params_base = (GLint *)
@@ -1700,7 +1678,7 @@
     params = (GLint *)getPointer(_env, params_buf, &_array, &_remaining);
     if (_remaining < 1) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
         goto exit;
     }
     glGetBufferParameteriv(
@@ -1735,12 +1713,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1790,12 +1768,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1849,12 +1827,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2191,7 +2169,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLint *)
@@ -2553,7 +2531,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glGetIntegerv(
@@ -2578,12 +2556,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2624,32 +2602,24 @@
     }
 }
 
-#include <string.h>
+#include <stdlib.h>
 
 /* void glGetProgramInfoLog ( GLuint shader, GLsizei maxLength, GLsizei* length, GLchar* infoLog ) */
-static
-jstring
-android_glGetProgramInfoLog (JNIEnv *_env, jobject _this, jint shader) {
+static jstring android_glGetProgramInfoLog(JNIEnv *_env, jobject, jint shader) {
     GLint infoLen = 0;
-    jstring _result = 0;
-    char* buf = 0;
     glGetProgramiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
-    if (infoLen) {
-        char* buf = (char*) malloc(infoLen);
-        if (buf == 0) {
-            _env->ThrowNew(IAEClass, "out of memory");
-            goto exit;
-        }
-        glGetProgramInfoLog(shader, infoLen, NULL, buf);
-        _result = _env->NewStringUTF(buf);
-    } else {
-        _result = _env->NewStringUTF("");
+    if (!infoLen) {
+        return _env->NewStringUTF("");
     }
-exit:
-    if (buf) {
-            free(buf);
+    char* buf = (char*) malloc(infoLen);
+    if (buf == NULL) {
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "out of memory");
+        return NULL;
     }
-    return _result;
+    glGetProgramInfoLog(shader, infoLen, NULL, buf);
+    jstring result = _env->NewStringUTF(buf);
+    free(buf);
+    return result;
 }
 /* void glGetRenderbufferParameteriv ( GLenum target, GLenum pname, GLint *params ) */
 static void
@@ -2662,12 +2632,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2719,12 +2689,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2765,32 +2735,24 @@
     }
 }
 
-#include <string.h>
+#include <stdlib.h>
 
 /* void glGetShaderInfoLog ( GLuint shader, GLsizei maxLength, GLsizei* length, GLchar* infoLog ) */
-static
-jstring
-android_glGetShaderInfoLog (JNIEnv *_env, jobject _this, jint shader) {
+static jstring android_glGetShaderInfoLog(JNIEnv *_env, jobject, jint shader) {
     GLint infoLen = 0;
-    jstring _result = 0;
-    char* buf = 0;
     glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
-    if (infoLen) {
-        char* buf = (char*) malloc(infoLen);
-        if (buf == 0) {
-            _env->ThrowNew(IAEClass, "out of memory");
-            goto exit;
-        }
-        glGetShaderInfoLog(shader, infoLen, NULL, buf);
-        _result = _env->NewStringUTF(buf);
-    } else {
-        _result = _env->NewStringUTF("");
+    if (!infoLen) {
+        return _env->NewStringUTF("");
     }
-exit:
-    if (buf) {
-            free(buf);
+    char* buf = (char*) malloc(infoLen);
+    if (buf == NULL) {
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "out of memory");
+        return NULL;
     }
-    return _result;
+    glGetShaderInfoLog(shader, infoLen, NULL, buf);
+    jstring result = _env->NewStringUTF(buf);
+    free(buf);
+    return result;
 }
 /* void glGetShaderPrecisionFormat ( GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision ) */
 static void
@@ -2806,12 +2768,12 @@
 
     if (!range_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "range == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "range == null");
         goto exit;
     }
     if (rangeOffset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "rangeOffset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "rangeOffset < 0");
         goto exit;
     }
     _rangeRemaining = _env->GetArrayLength(range_ref) - rangeOffset;
@@ -2821,12 +2783,12 @@
 
     if (!precision_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "precision == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "precision == null");
         goto exit;
     }
     if (precisionOffset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "precisionOffset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "precisionOffset < 0");
         goto exit;
     }
     _precisionRemaining = _env->GetArrayLength(precision_ref) - precisionOffset;
@@ -2894,12 +2856,12 @@
 
     if (!length_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length == null");
         goto exit;
     }
     if (lengthOffset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "lengthOffset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "lengthOffset < 0");
         goto exit;
     }
     _lengthRemaining = _env->GetArrayLength(length_ref) - lengthOffset;
@@ -2909,12 +2871,12 @@
 
     if (!source_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "source == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "source == null");
         goto exit;
     }
     if (sourceOffset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "sourceOffset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "sourceOffset < 0");
         goto exit;
     }
     _sourceRemaining = _env->GetArrayLength(source_ref) - sourceOffset;
@@ -2961,16 +2923,10 @@
     }
 }
 
-#include <string.h>
-
 /* const GLubyte * glGetString ( GLenum name ) */
-static
-jstring
-android_glGetString
-  (JNIEnv *_env, jobject _this, jint name) {
-    const char * chars = (const char *)glGetString((GLenum)name);
-    jstring output = _env->NewStringUTF(chars);
-    return output;
+static jstring android_glGetString(JNIEnv* _env, jobject, jint name) {
+    const char* chars = (const char*) glGetString((GLenum) name);
+    return _env->NewStringUTF(chars);
 }
 /* void glGetTexParameterfv ( GLenum target, GLenum pname, GLfloat *params ) */
 static void
@@ -2983,18 +2939,18 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
     if (_remaining < 1) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
         goto exit;
     }
     params_base = (GLfloat *)
@@ -3026,7 +2982,7 @@
     params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining);
     if (_remaining < 1) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
         goto exit;
     }
     glGetTexParameterfv(
@@ -3052,18 +3008,18 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
     if (_remaining < 1) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
         goto exit;
     }
     params_base = (GLint *)
@@ -3095,7 +3051,7 @@
     params = (GLint *)getPointer(_env, params_buf, &_array, &_remaining);
     if (_remaining < 1) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
         goto exit;
     }
     glGetTexParameteriv(
@@ -3121,12 +3077,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -3178,12 +3134,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -3232,7 +3188,7 @@
     const char* _nativename = 0;
 
     if (!name) {
-        _env->ThrowNew(IAEClass, "name == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "name == null");
         goto exit;
     }
     _nativename = _env->GetStringUTFChars(name, 0);
@@ -3261,12 +3217,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -3318,12 +3274,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -3566,11 +3522,11 @@
     GLvoid *binary = (GLvoid *) 0;
 
     if (!shaders_ref) {
-        _env->ThrowNew(IAEClass, "shaders == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "shaders == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _shadersRemaining = _env->GetArrayLength(shaders_ref) - offset;
@@ -3633,7 +3589,7 @@
     (JNIEnv *_env, jobject _this, jint shader, jstring string) {
 
     if (!string) {
-        _env->ThrowNew(IAEClass, "string == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "string == null");
         return;
     }
 
@@ -3754,16 +3710,16 @@
     GLfloat *params = (GLfloat *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
     if (_remaining < 1) {
-        _env->ThrowNew(IAEClass, "length - offset < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
         goto exit;
     }
     params_base = (GLfloat *)
@@ -3793,7 +3749,7 @@
 
     params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining);
     if (_remaining < 1) {
-        _env->ThrowNew(IAEClass, "remaining() < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
         goto exit;
     }
     glTexParameterfv(
@@ -3828,16 +3784,16 @@
     GLint *params = (GLint *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
     if (_remaining < 1) {
-        _env->ThrowNew(IAEClass, "length - offset < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
         goto exit;
     }
     params_base = (GLint *)
@@ -3867,7 +3823,7 @@
 
     params = (GLint *)getPointer(_env, params_buf, &_array, &_remaining);
     if (_remaining < 1) {
-        _env->ThrowNew(IAEClass, "remaining() < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
         goto exit;
     }
     glTexParameteriv(
@@ -3928,11 +3884,11 @@
     GLfloat *v = (GLfloat *) 0;
 
     if (!v_ref) {
-        _env->ThrowNew(IAEClass, "v == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "v == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(v_ref) - offset;
@@ -3991,11 +3947,11 @@
     GLint *v = (GLint *) 0;
 
     if (!v_ref) {
-        _env->ThrowNew(IAEClass, "v == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "v == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(v_ref) - offset;
@@ -4055,11 +4011,11 @@
     GLfloat *v = (GLfloat *) 0;
 
     if (!v_ref) {
-        _env->ThrowNew(IAEClass, "v == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "v == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(v_ref) - offset;
@@ -4119,11 +4075,11 @@
     GLint *v = (GLint *) 0;
 
     if (!v_ref) {
-        _env->ThrowNew(IAEClass, "v == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "v == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(v_ref) - offset;
@@ -4184,11 +4140,11 @@
     GLfloat *v = (GLfloat *) 0;
 
     if (!v_ref) {
-        _env->ThrowNew(IAEClass, "v == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "v == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(v_ref) - offset;
@@ -4249,11 +4205,11 @@
     GLint *v = (GLint *) 0;
 
     if (!v_ref) {
-        _env->ThrowNew(IAEClass, "v == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "v == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(v_ref) - offset;
@@ -4315,11 +4271,11 @@
     GLfloat *v = (GLfloat *) 0;
 
     if (!v_ref) {
-        _env->ThrowNew(IAEClass, "v == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "v == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(v_ref) - offset;
@@ -4381,11 +4337,11 @@
     GLint *v = (GLint *) 0;
 
     if (!v_ref) {
-        _env->ThrowNew(IAEClass, "v == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "v == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(v_ref) - offset;
@@ -4434,11 +4390,11 @@
     GLfloat *value = (GLfloat *) 0;
 
     if (!value_ref) {
-        _env->ThrowNew(IAEClass, "value == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "value == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(value_ref) - offset;
@@ -4489,11 +4445,11 @@
     GLfloat *value = (GLfloat *) 0;
 
     if (!value_ref) {
-        _env->ThrowNew(IAEClass, "value == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "value == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(value_ref) - offset;
@@ -4544,11 +4500,11 @@
     GLfloat *value = (GLfloat *) 0;
 
     if (!value_ref) {
-        _env->ThrowNew(IAEClass, "value == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "value == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(value_ref) - offset;
@@ -4627,11 +4583,11 @@
     GLfloat *values = (GLfloat *) 0;
 
     if (!values_ref) {
-        _env->ThrowNew(IAEClass, "values == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "values == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(values_ref) - offset;
@@ -4689,11 +4645,11 @@
     GLfloat *values = (GLfloat *) 0;
 
     if (!values_ref) {
-        _env->ThrowNew(IAEClass, "values == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "values == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(values_ref) - offset;
@@ -4752,11 +4708,11 @@
     GLfloat *values = (GLfloat *) 0;
 
     if (!values_ref) {
-        _env->ThrowNew(IAEClass, "values == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "values == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(values_ref) - offset;
@@ -4816,11 +4772,11 @@
     GLfloat *values = (GLfloat *) 0;
 
     if (!values_ref) {
-        _env->ThrowNew(IAEClass, "values == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "values == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(values_ref) - offset;
diff --git a/core/jni/android_os_FileUtils.cpp b/core/jni/android_os_FileUtils.cpp
index 2b4a955..89dce89 100644
--- a/core/jni/android_os_FileUtils.cpp
+++ b/core/jni/android_os_FileUtils.cpp
@@ -2,16 +2,16 @@
 **
 ** Copyright 2006, 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 
+** 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 
+**     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 
+** 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.
 */
 
@@ -145,7 +145,7 @@
 jboolean android_os_FileUtils_getFileStatus(JNIEnv* env, jobject clazz, jstring path, jobject fileStatus) {
     const char* pathStr = env->GetStringUTFChars(path, NULL);
     jboolean ret = false;
-    
+
     struct stat s;
     int res = stat(pathStr, &s);
     if (res == 0) {
@@ -165,9 +165,9 @@
             env->SetLongField(fileStatus, gFileStatusCtimeFieldID, s.st_ctime);
         }
     }
-    
+
     env->ReleaseStringUTFChars(path, pathStr);
-    
+
     return ret;
 }
 
@@ -183,11 +183,6 @@
 
 int register_android_os_FileUtils(JNIEnv* env)
 {
-    jclass clazz;
-
-    clazz = env->FindClass(kFileUtilsPathName);
-    LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.FileUtils");
-    
     jclass fileStatusClass = env->FindClass("android/os/FileUtils$FileStatus");
     LOG_FATAL_IF(fileStatusClass == NULL, "Unable to find class android.os.FileUtils$FileStatus");
 
diff --git a/core/jni/android_os_MemoryFile.cpp b/core/jni/android_os_MemoryFile.cpp
index ee8d836..7134191 100644
--- a/core/jni/android_os_MemoryFile.cpp
+++ b/core/jni/android_os_MemoryFile.cpp
@@ -148,17 +148,10 @@
             (void*)android_os_MemoryFile_get_size}
 };
 
-static const char* const kClassPathName = "android/os/MemoryFile";
-
 int register_android_os_MemoryFile(JNIEnv* env)
 {
-    jclass clazz;
-
-    clazz = env->FindClass(kClassPathName);
-    LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.FileUtils");
-
     return AndroidRuntime::registerNativeMethods(
-        env, kClassPathName,
+        env, "android/os/MemoryFile",
         methods, NELEM(methods));
 }
 
diff --git a/core/jni/android_os_ParcelFileDescriptor.cpp b/core/jni/android_os_ParcelFileDescriptor.cpp
index 1f737f9..4ec131c 100644
--- a/core/jni/android_os_ParcelFileDescriptor.cpp
+++ b/core/jni/android_os_ParcelFileDescriptor.cpp
@@ -29,43 +29,11 @@
 namespace android
 {
 
-static struct file_descriptor_offsets_t
-{
-    jclass mClass;
-    jmethodID mConstructor;
-    jfieldID mDescriptor;
-} gFileDescriptorOffsets;
-
-static struct socket_offsets_t
-{
-    jfieldID mSocketImpl;
-} gSocketOffsets;
-
-static struct socket_impl_offsets_t
-{
-    jfieldID mFileDescriptor;
-} gSocketImplOffsets;
-
 static struct parcel_file_descriptor_offsets_t
 {
-    jclass mClass;
     jfieldID mFileDescriptor;
 } gParcelFileDescriptorOffsets;
 
-static jobject android_os_ParcelFileDescriptor_getFileDescriptorFromSocket(JNIEnv* env,
-    jobject clazz, jobject object)
-{
-    jobject socketImpl = env->GetObjectField(object, gSocketOffsets.mSocketImpl);
-    jobject fileDescriptor = env->GetObjectField(socketImpl, gSocketImplOffsets.mFileDescriptor);
-    jint fd = env->GetIntField(fileDescriptor, gFileDescriptorOffsets.mDescriptor);
-    jobject fileDescriptorClone = env->NewObject(gFileDescriptorOffsets.mClass,
-        gFileDescriptorOffsets.mConstructor);
-    if (fileDescriptorClone != NULL) {
-        env->SetIntField(fileDescriptorClone, gFileDescriptorOffsets.mDescriptor, dup(fd));
-    }
-    return fileDescriptorClone;
-}
-
 static int android_os_ParcelFileDescriptor_createPipeNative(JNIEnv* env,
     jobject clazz, jobjectArray outFds)
 {
@@ -75,11 +43,7 @@
     }
 
     for (int i=0; i<2; i++) {
-        jobject fdObj = env->NewObject(gFileDescriptorOffsets.mClass,
-                gFileDescriptorOffsets.mConstructor);
-        if (fdObj != NULL) {
-            env->SetIntField(fdObj, gFileDescriptorOffsets.mDescriptor, fds[i]);
-        }
+        jobject fdObj = jniCreateFileDescriptor(env, fds[i]);
         env->SetObjectArrayElement(outFds, i, fdObj);
     }
 
@@ -90,7 +54,7 @@
 {
     jobject descriptor = env->GetObjectField(clazz, gParcelFileDescriptorOffsets.mFileDescriptor);
     if (descriptor == NULL) return -1;
-    return env->GetIntField(descriptor, gFileDescriptorOffsets.mDescriptor);
+    return jniGetFDFromFileDescriptor(env, descriptor);
 }
 
 static jlong android_os_ParcelFileDescriptor_getStatSize(JNIEnv* env,
@@ -101,16 +65,16 @@
         jniThrowException(env, "java/lang/IllegalArgumentException", "bad file descriptor");
         return -1;
     }
-    
+
     struct stat st;
     if (fstat(fd, &st) != 0) {
         return -1;
     }
-    
+
     if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
         return st.st_size;
     }
-    
+
     return -1;
 }
 
@@ -122,7 +86,7 @@
         jniThrowException(env, "java/lang/IllegalArgumentException", "bad file descriptor");
         return -1;
     }
-    
+
     return lseek(fd, pos, SEEK_SET);
 }
 
@@ -138,8 +102,6 @@
 }
 
 static const JNINativeMethod gParcelFileDescriptorMethods[] = {
-    {"getFileDescriptorFromSocket", "(Ljava/net/Socket;)Ljava/io/FileDescriptor;",
-        (void*)android_os_ParcelFileDescriptor_getFileDescriptorFromSocket},
     {"createPipeNative", "([Ljava/io/FileDescriptor;)I",
         (void*)android_os_ParcelFileDescriptor_createPipeNative},
     {"getStatSize", "()J",
@@ -154,31 +116,8 @@
 
 int register_android_os_ParcelFileDescriptor(JNIEnv* env)
 {
-    jclass clazz;
-
-    clazz = env->FindClass("java/net/Socket");
-    LOG_FATAL_IF(clazz == NULL, "Unable to find class java.net.Socket");
-    gSocketOffsets.mSocketImpl = env->GetFieldID(clazz, "impl", "Ljava/net/SocketImpl;");
-    LOG_FATAL_IF(gSocketOffsets.mSocketImpl == NULL,
-        "Unable to find impl field in java.net.Socket");
-
-    clazz = env->FindClass("java/net/SocketImpl");
-    LOG_FATAL_IF(clazz == NULL, "Unable to find class java.net.SocketImpl");
-    gSocketImplOffsets.mFileDescriptor = env->GetFieldID(clazz, "fd", "Ljava/io/FileDescriptor;");
-    LOG_FATAL_IF(gSocketImplOffsets.mFileDescriptor == NULL,
-                 "Unable to find fd field in java.net.SocketImpl");
-
-    clazz = env->FindClass("java/io/FileDescriptor");
-    LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
-    gFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
-    gFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "()V");
-    gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I");
-    LOG_FATAL_IF(gFileDescriptorOffsets.mDescriptor == NULL,
-                 "Unable to find descriptor field in java.io.FileDescriptor");
-    
-    clazz = env->FindClass(kParcelFileDescriptorPathName);
+    jclass clazz = env->FindClass(kParcelFileDescriptorPathName);
     LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor");
-    gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
     gParcelFileDescriptorOffsets.mFileDescriptor = env->GetFieldID(clazz, "mFileDescriptor", "Ljava/io/FileDescriptor;");
     LOG_FATAL_IF(gParcelFileDescriptorOffsets.mFileDescriptor == NULL,
                  "Unable to find mFileDescriptor field in android.os.ParcelFileDescriptor");
diff --git a/core/jni/android_pim_EventRecurrence.cpp b/core/jni/android_pim_EventRecurrence.cpp
index 9056c90..44e898d 100644
--- a/core/jni/android_pim_EventRecurrence.cpp
+++ b/core/jni/android_pim_EventRecurrence.cpp
@@ -28,7 +28,6 @@
     jfieldID count;

 };

 

-static jclass clazz;

 static jfieldID freq_field;

 static jfieldID until_field;

 static jfieldID count_field;

@@ -87,8 +86,7 @@
         jniThrowNullPointerException(env, "EventRecurrence.parse str parameter null");

         return ;

     }

-    jboolean isCopy;

-    const jchar* jchars = env->GetStringChars(jstr, &isCopy);

+    const jchar* jchars = env->GetStringChars(jstr, NULL);

     jsize len = env->GetStringLength(jstr);

     String16 str(jchars, len);

     env->ReleaseStringChars(jstr, jchars);

@@ -156,7 +154,7 @@
 

 int register_android_pim_EventRecurrence(JNIEnv* env)

 {

-    clazz = env->FindClass(CLASS_NAME);

+    jclass clazz = env->FindClass(CLASS_NAME);

     if (clazz == NULL) {

         LOGE("Field lookup unable to find class '%s'\n", CLASS_NAME);

         return -1;

diff --git a/core/jni/android_text_AndroidBidi.cpp b/core/jni/android_text_AndroidBidi.cpp
index 53028c3..d50a69f 100644
--- a/core/jni/android_text_AndroidBidi.cpp
+++ b/core/jni/android_text_AndroidBidi.cpp
@@ -2,16 +2,16 @@
 **
 ** Copyright 2010, 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 
+** 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 
+**     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 
+** 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.
 */
 
@@ -24,8 +24,8 @@
 #include "unicode/ubidi.h"
 
 namespace android {
-    
-static jint runBidi(JNIEnv* env, jobject obj, jint dir, jcharArray chsArray, 
+
+static jint runBidi(JNIEnv* env, jobject obj, jint dir, jcharArray chsArray,
                     jbyteArray infoArray, int n, jboolean haveInfo)
 {
     // Parameters are checked on java side
@@ -63,9 +63,6 @@
 
 int register_android_text_AndroidBidi(JNIEnv* env)
 {
-    jclass clazz = env->FindClass("android/text/AndroidBidi");
-    LOG_ASSERT(clazz, "Cannot find android/text/AndroidBidi");
-    
     return AndroidRuntime::registerNativeMethods(env, "android/text/AndroidBidi",
             gMethods, NELEM(gMethods));
 }
diff --git a/core/jni/android_text_AndroidCharacter.cpp b/core/jni/android_text_AndroidCharacter.cpp
index 0d0f5fa..dacbe41 100644
--- a/core/jni/android_text_AndroidCharacter.cpp
+++ b/core/jni/android_text_AndroidCharacter.cpp
@@ -191,9 +191,6 @@
 
 int register_android_text_AndroidCharacter(JNIEnv* env)
 {
-    jclass clazz = env->FindClass("android/text/AndroidCharacter");
-    LOG_ASSERT(clazz, "Cannot find android/text/AndroidCharacter");
-
     return AndroidRuntime::registerNativeMethods(env, "android/text/AndroidCharacter",
             gMethods, NELEM(gMethods));
 }
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 8ea7e90..b0e92e4 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -169,7 +169,7 @@
 
     env->ReleasePrimitiveArrayCritical(outOffsets, offsets, 0);
 
-    jobject fileDesc = newFileDescriptor(env, fd);
+    jobject fileDesc = jniCreateFileDescriptor(env, fd);
     if (fileDesc == NULL) {
         close(fd);
         return NULL;
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 8618b79..0681195 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -131,13 +131,6 @@
     jmethodID mLogE;
 } gLogOffsets;
 
-static struct file_descriptor_offsets_t
-{
-    jclass mClass;
-    jmethodID mConstructor;
-    jfieldID mDescriptor;
-} gFileDescriptorOffsets;
-
 static struct parcel_file_descriptor_offsets_t
 {
     jclass mClass;
@@ -591,17 +584,6 @@
     return NULL;
 }
 
-jobject newFileDescriptor(JNIEnv* env, int fd)
-{
-    jobject object = env->NewObject(
-            gFileDescriptorOffsets.mClass, gFileDescriptorOffsets.mConstructor);
-    if (object != NULL) {
-        //LOGI("Created new FileDescriptor %p with fd %d\n", object, fd);
-        env->SetIntField(object, gFileDescriptorOffsets.mDescriptor, fd);
-    }
-    return object;
-}
-
 jobject newParcelFileDescriptor(JNIEnv* env, jobject fileDesc)
 {
     return env->NewObject(
@@ -1358,8 +1340,8 @@
 {
     Parcel* parcel = parcelForJavaObject(env, clazz);
     if (parcel != NULL) {
-        const status_t err = parcel->writeDupFileDescriptor(
-                env->GetIntField(object, gFileDescriptorOffsets.mDescriptor));
+        const status_t err =
+                parcel->writeDupFileDescriptor(jniGetFDFromFileDescriptor(env, object));
         if (err != NO_ERROR) {
             jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
         }
@@ -1459,13 +1441,7 @@
         if (fd < 0) return NULL;
         fd = dup(fd);
         if (fd < 0) return NULL;
-        jobject object = env->NewObject(
-                gFileDescriptorOffsets.mClass, gFileDescriptorOffsets.mConstructor);
-        if (object != NULL) {
-            //LOGI("Created new FileDescriptor %p with fd %d\n", object, fd);
-            env->SetIntField(object, gFileDescriptorOffsets.mDescriptor, fd);
-        }
-        return object;
+        return jniCreateFileDescriptor(env, fd);
     }
     return NULL;
 }
@@ -1512,7 +1488,7 @@
         jniThrowException(env, "java/io/FileNotFoundException", strerror(errno));
         return NULL;
     }
-    jobject object = newFileDescriptor(env, fd);
+    jobject object = jniCreateFileDescriptor(env, fd);
     if (object == NULL) {
         close(fd);
     }
@@ -1525,7 +1501,7 @@
         jniThrowNullPointerException(env, NULL);
         return NULL;
     }
-    int origfd = env->GetIntField(orig, gFileDescriptorOffsets.mDescriptor);
+    int origfd = jniGetFDFromFileDescriptor(env, orig);
     if (origfd < 0) {
         jniThrowException(env, "java/lang/IllegalArgumentException", "bad FileDescriptor");
         return NULL;
@@ -1536,7 +1512,7 @@
         jniThrowIOException(env, errno);
         return NULL;
     }
-    jobject object = newFileDescriptor(env, fd);
+    jobject object = jniCreateFileDescriptor(env, fd);
     if (object == NULL) {
         close(fd);
     }
@@ -1549,9 +1525,9 @@
         jniThrowNullPointerException(env, NULL);
         return;
     }
-    int fd = env->GetIntField(object, gFileDescriptorOffsets.mDescriptor);
+    int fd = jniGetFDFromFileDescriptor(env, object);
     if (fd >= 0) {
-        env->SetIntField(object, gFileDescriptorOffsets.mDescriptor, -1);
+        jniSetFileDescriptorOfFD(env, object, -1);
         //LOGI("Closing ParcelFileDescriptor %d\n", fd);
         close(fd);
     }
@@ -1563,9 +1539,9 @@
         jniThrowNullPointerException(env, NULL);
         return;
     }
-    int fd = env->GetIntField(object, gFileDescriptorOffsets.mDescriptor);
+    int fd = jniGetFDFromFileDescriptor(env, object);
     if (fd >= 0) {
-        env->SetIntField(object, gFileDescriptorOffsets.mDescriptor, -1);
+        jniSetFileDescriptorOfFD(env, object, -1);
     }
 }
 
@@ -1794,15 +1770,6 @@
         clazz, "e", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I");
     assert(gLogOffsets.mLogE);
 
-    clazz = env->FindClass("java/io/FileDescriptor");
-    LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
-    gFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
-    gFileDescriptorOffsets.mConstructor
-        = env->GetMethodID(clazz, "<init>", "()V");
-    gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I");
-    LOG_FATAL_IF(gFileDescriptorOffsets.mDescriptor == NULL,
-                 "Unable to find descriptor field in java.io.FileDescriptor");
-
     clazz = env->FindClass("android/os/ParcelFileDescriptor");
     LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor");
     gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
@@ -1842,13 +1809,3 @@
         return -1;
     return 0;
 }
-
-namespace android {
-
-// Returns the Unix file descriptor for a ParcelFileDescriptor object
-int getParcelFileDescriptorFD(JNIEnv* env, jobject object)
-{
-    return env->GetIntField(object, gFileDescriptorOffsets.mDescriptor);
-}
-
-}
diff --git a/core/jni/android_util_Binder.h b/core/jni/android_util_Binder.h
index 495e76a..0122691 100644
--- a/core/jni/android_util_Binder.h
+++ b/core/jni/android_util_Binder.h
@@ -2,16 +2,16 @@
 **
 ** Copyright 2006, 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 
+** 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 
+**     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 
+** 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.
 */
 
@@ -29,7 +29,6 @@
 // Note: does not type checking; must guarantee jobject is a Java Parcel
 extern Parcel* parcelForJavaObject(JNIEnv* env, jobject obj);
 
-extern jobject newFileDescriptor(JNIEnv* env, int fd);
 extern jobject newParcelFileDescriptor(JNIEnv* env, jobject fileDesc);
 
 }
diff --git a/core/jni/android_util_EventLog.cpp b/core/jni/android_util_EventLog.cpp
index 91d37d3..5d51110 100644
--- a/core/jni/android_util_EventLog.cpp
+++ b/core/jni/android_util_EventLog.cpp
@@ -35,9 +35,6 @@
 static jclass gIntegerClass;
 static jfieldID gIntegerValueID;
 
-static jclass gListClass;
-static jfieldID gListItemsID;
-
 static jclass gLongClass;
 static jfieldID gLongValueID;
 
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 0068de7..e5c28489 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -103,17 +103,6 @@
     }
 }
 
-
-static void fakeProcessEntry(void* arg)
-{
-    String8* cls = (String8*)arg;
-
-    AndroidRuntime* jr = AndroidRuntime::getRuntime();
-    jr->callMain(cls->string(), 0, NULL);
-
-    delete cls;
-}
-
 jint android_os_Process_myPid(JNIEnv* env, jobject clazz)
 {
     return getpid();
@@ -915,11 +904,6 @@
 
 int register_android_os_Process(JNIEnv* env)
 {
-    jclass clazz;
-
-    clazz = env->FindClass(kProcessPathName);
-    LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.Process");
-
     return AndroidRuntime::registerNativeMethods(
         env, kProcessPathName,
         methods, NELEM(methods));
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index f31bba9..314c2ee 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -595,13 +595,11 @@
 // Logging
 // ----------------------------------------------------------------------------
 
-jfieldID gFileDescriptorField;
-
 static void
 android_app_ActivityThread_dumpGraphics(JNIEnv* env, jobject clazz, jobject javaFileDescriptor)
 {
 #ifdef USE_OPENGL_RENDERER
-    int fd = env->GetIntField(javaFileDescriptor, gFileDescriptorField);
+    int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
     android::uirenderer::DisplayList::outputLogBuffer(fd);
 #endif // USE_OPENGL_RENDERER
 }
@@ -736,12 +734,6 @@
 
 int register_android_app_ActivityThread(JNIEnv* env)
 {
-    jclass fileDescriptorClass = env->FindClass("java/io/FileDescriptor");
-    LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
-    gFileDescriptorField = env->GetFieldID(fileDescriptorClass, "descriptor", "I");
-    LOG_FATAL_IF(gFileDescriptorField == NULL,
-                 "Unable to find descriptor field in java.io.FileDescriptor");
-
     return AndroidRuntime::registerNativeMethods(
             env, kActivityThreadPathName,
             gActivityThreadMethods, NELEM(gActivityThreadMethods));
diff --git a/core/jni/com_android_internal_graphics_NativeUtils.cpp b/core/jni/com_android_internal_graphics_NativeUtils.cpp
index 319946f..9cc43606 100644
--- a/core/jni/com_android_internal_graphics_NativeUtils.cpp
+++ b/core/jni/com_android_internal_graphics_NativeUtils.cpp
@@ -29,11 +29,6 @@
 namespace android
 {
 
-static jclass class_fileDescriptor;
-static jfieldID field_fileDescriptor_descriptor;
-static jmethodID method_fileDescriptor_init;
-
-
 static jboolean scrollRect(JNIEnv* env, jobject graphics2D, jobject canvas, jobject rect, int dx, int dy) {
     if (canvas == NULL) {
         jniThrowNullPointerException(env, NULL);
diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
index 61efcf2..5f2065a 100644
--- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
@@ -29,9 +29,6 @@
 
 namespace android {
 
-static jclass gDisplay_class;
-static jclass gContext_class;
-static jclass gSurface_class;
 static jclass gConfig_class;
 
 static jmethodID gConfig_ctorID;
@@ -44,21 +41,6 @@
 static jfieldID gSurface_SurfaceFieldID;
 static jfieldID gBitmap_NativeBitmapFieldID;
 
-static __attribute__((noinline))
-bool hasException(JNIEnv *env) {
-    if (env->ExceptionCheck() != 0) {
-        env->ExceptionDescribe();
-        return true;
-    }
-    return false;
-}
-
-static __attribute__((noinline))
-jclass make_globalref(JNIEnv* env, const char classname[]) {
-    jclass c = env->FindClass(classname);
-    return (jclass)env->NewGlobalRef(c);
-}
-
 static inline EGLDisplay getDisplay(JNIEnv* env, jobject o) {
     if (!o) return EGL_NO_DISPLAY;
     return (EGLDisplay)env->GetIntField(o, gDisplay_EGLDisplayFieldID);
@@ -77,18 +59,20 @@
 }
 static void nativeClassInit(JNIEnv *_env, jclass eglImplClass)
 {
-    gDisplay_class = make_globalref(_env, "com/google/android/gles_jni/EGLDisplayImpl");
-    gContext_class = make_globalref(_env, "com/google/android/gles_jni/EGLContextImpl");
-    gSurface_class = make_globalref(_env, "com/google/android/gles_jni/EGLSurfaceImpl");
-    gConfig_class  = make_globalref(_env, "com/google/android/gles_jni/EGLConfigImpl");
+    jclass config_class = _env->FindClass("com/google/android/gles_jni/EGLConfigImpl");
+    gConfig_class = (jclass) _env->NewGlobalRef(config_class);
+    gConfig_ctorID = _env->GetMethodID(gConfig_class,  "<init>", "(I)V");
+    gConfig_EGLConfigFieldID = _env->GetFieldID(gConfig_class,  "mEGLConfig",  "I");
 
-    gConfig_ctorID  = _env->GetMethodID(gConfig_class,  "<init>", "(I)V");
+    jclass display_class = _env->FindClass("com/google/android/gles_jni/EGLDisplayImpl");
+    gDisplay_EGLDisplayFieldID = _env->GetFieldID(display_class, "mEGLDisplay", "I");
 
-    gDisplay_EGLDisplayFieldID = _env->GetFieldID(gDisplay_class, "mEGLDisplay", "I");
-    gContext_EGLContextFieldID = _env->GetFieldID(gContext_class, "mEGLContext", "I");
-    gSurface_EGLSurfaceFieldID = _env->GetFieldID(gSurface_class, "mEGLSurface", "I");
-    gSurface_NativePixelRefFieldID = _env->GetFieldID(gSurface_class, "mNativePixelRef", "I");
-    gConfig_EGLConfigFieldID   = _env->GetFieldID(gConfig_class,  "mEGLConfig",  "I");
+    jclass context_class = _env->FindClass("com/google/android/gles_jni/EGLContextImpl");
+    gContext_EGLContextFieldID = _env->GetFieldID(context_class, "mEGLContext", "I");
+
+    jclass surface_class = _env->FindClass("com/google/android/gles_jni/EGLSurfaceImpl");
+    gSurface_EGLSurfaceFieldID = _env->GetFieldID(surface_class, "mEGLSurface", "I");
+    gSurface_NativePixelRefFieldID = _env->GetFieldID(surface_class, "mNativePixelRef", "I");
 
     jclass bitmap_class = _env->FindClass("android/graphics/Bitmap");
     gBitmap_NativeBitmapFieldID = _env->GetFieldID(bitmap_class, "mNativeBitmap", "I");
diff --git a/core/jni/com_google_android_gles_jni_GLImpl.cpp b/core/jni/com_google_android_gles_jni_GLImpl.cpp
index bf613e1..8777131 100644
--- a/core/jni/com_google_android_gles_jni_GLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_GLImpl.cpp
@@ -2,21 +2,23 @@
 **
 ** Copyright 2006, 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 
+** 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 
+**     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 
+** 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.
 */
 
 // This source file is automatically generated
 
+#include "jni.h"
+#include "JNIHelp.h"
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/misc.h>
 
@@ -64,10 +66,6 @@
 
 static jclass nioAccessClass;
 static jclass bufferClass;
-static jclass OOMEClass;
-static jclass UOEClass;
-static jclass IAEClass;
-static jclass AIOOBEClass;
 static jclass G11ImplClass;
 static jmethodID getBasePointerID;
 static jmethodID getBaseArrayID;
@@ -85,7 +83,7 @@
 /* Cache method IDs each time the class is loaded. */
 
 static void
-nativeClassInitBuffer(JNIEnv *_env)
+nativeClassInit(JNIEnv *_env, jclass glImplClass)
 {
     jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
     nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
@@ -115,26 +113,6 @@
         _env->GetFieldID(bufferClass, "_elementSizeShift", "I");
 }
 
-static void
-nativeClassInit(JNIEnv *_env, jclass glImplClass)
-{
-    nativeClassInitBuffer(_env);
-
-    jclass IAEClassLocal =
-        _env->FindClass("java/lang/IllegalArgumentException");
-    jclass OOMEClassLocal =
-         _env->FindClass("java/lang/OutOfMemoryError");
-    jclass UOEClassLocal =
-         _env->FindClass("java/lang/UnsupportedOperationException");
-    jclass AIOOBEClassLocal =
-         _env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
-
-    IAEClass = (jclass) _env->NewGlobalRef(IAEClassLocal);
-    OOMEClass = (jclass) _env->NewGlobalRef(OOMEClassLocal);
-    UOEClass = (jclass) _env->NewGlobalRef(UOEClassLocal);
-    AIOOBEClass = (jclass) _env->NewGlobalRef(AIOOBEClassLocal);
-}
-
 static void *
 getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
 {
@@ -155,7 +133,7 @@
         *array = NULL;
         return (void *) (jint) pointer;
     }
-    
+
     *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
             getBaseArrayID, buffer);
     if (*array == NULL) {
@@ -164,7 +142,7 @@
     offset = _env->CallStaticIntMethod(nioAccessClass,
             getBaseArrayOffsetID, buffer);
     data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0);
-    
+
     return (void *) ((char *) data + offset);
 }
 
@@ -208,7 +186,8 @@
                 releasePointer(_env, array, buf, 0);
             }
         } else {
-            _env->ThrowNew(IAEClass, "Must use a native order direct Buffer");
+            jniThrowException(_env, "java/lang/IllegalArgumentException",
+                              "Must use a native order direct Buffer");
         }
     }
     return buf;
@@ -251,7 +230,7 @@
         }
     }
 }
-    
+
 static bool
 checkForExtension(const GLubyte* pExtensions, const GLubyte* pExtension) {
     for (;*pExtensions != '\0'; pExtensions = nextExtension(pExtensions)) {
@@ -280,7 +259,6 @@
 }
 
 // --------------------------------------------------------------------------
-
 /* void glActiveTexture ( GLenum texture ) */
 static void
 android_glActiveTexture__I
@@ -557,16 +535,16 @@
     GLuint *textures = (GLuint *) 0;
 
     if (!textures_ref) {
-        _env->ThrowNew(IAEClass, "textures == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "textures == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(textures_ref) - offset;
     if (_remaining < n) {
-        _env->ThrowNew(IAEClass, "length - offset < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
         goto exit;
     }
     textures_base = (GLuint *)
@@ -595,7 +573,7 @@
 
     textures = (GLuint *)getPointer(_env, textures_buf, &_array, &_remaining);
     if (_remaining < n) {
-        _env->ThrowNew(IAEClass, "remaining() < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
         goto exit;
     }
     glDeleteTextures(
@@ -686,7 +664,7 @@
 
     indices = (GLvoid *)getPointer(_env, indices_buf, &_array, &_remaining);
     if (_remaining < count) {
-        _env->ThrowNew(AIOOBEClass, "remaining() < count");
+        jniThrowException(_env, "java/lang/ArrayIndexOutOfBoundsException", "remaining() < count");
         goto exit;
     }
     glDrawElements(
@@ -753,11 +731,11 @@
     GLfloat *params = (GLfloat *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -787,7 +765,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfloat *)
@@ -841,7 +819,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glFogfv(
@@ -874,11 +852,11 @@
     GLfixed *params = (GLfixed *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -908,7 +886,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfixed *)
@@ -962,7 +940,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glFogxv(
@@ -1024,18 +1002,18 @@
 
     if (!textures_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "textures == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "textures == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(textures_ref) - offset;
     if (_remaining < n) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
         goto exit;
     }
     textures_base = (GLuint *)
@@ -1066,7 +1044,7 @@
     textures = (GLuint *)getPointer(_env, textures_buf, &_array, &_remaining);
     if (_remaining < n) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
         goto exit;
     }
     glGenTextures(
@@ -1100,12 +1078,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1442,7 +1420,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLint *)
@@ -1804,7 +1782,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glGetIntegerv(
@@ -1818,16 +1796,10 @@
     }
 }
 
-#include <string.h>
-
 /* const GLubyte * glGetString ( GLenum name ) */
-static
-jstring
-android_glGetString
-  (JNIEnv *_env, jobject _this, jint name) {
-    const char * chars = (const char *)glGetString((GLenum)name);
-    jstring output = _env->NewStringUTF(chars);
-    return output;
+static jstring android_glGetString(JNIEnv *_env, jobject, jint name) {
+    const char* chars = (const char*) glGetString((GLenum) name);
+    return _env->NewStringUTF(chars);
 }
 /* void glHint ( GLenum target, GLenum mode ) */
 static void
@@ -1858,11 +1830,11 @@
     GLfloat *params = (GLfloat *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1883,7 +1855,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfloat *)
@@ -1928,7 +1900,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glLightModelfv(
@@ -1961,11 +1933,11 @@
     GLfixed *params = (GLfixed *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -1986,7 +1958,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfixed *)
@@ -2031,7 +2003,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glLightModelxv(
@@ -2065,11 +2037,11 @@
     GLfloat *params = (GLfloat *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2116,7 +2088,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfloat *)
@@ -2188,7 +2160,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glLightfv(
@@ -2223,11 +2195,11 @@
     GLfixed *params = (GLfixed *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2274,7 +2246,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfixed *)
@@ -2346,7 +2318,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glLightxv(
@@ -2395,11 +2367,11 @@
     GLfloat *m = (GLfloat *) 0;
 
     if (!m_ref) {
-        _env->ThrowNew(IAEClass, "m == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "m == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(m_ref) - offset;
@@ -2444,11 +2416,11 @@
     GLfixed *m = (GLfixed *) 0;
 
     if (!m_ref) {
-        _env->ThrowNew(IAEClass, "m == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "m == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(m_ref) - offset;
@@ -2513,11 +2485,11 @@
     GLfloat *params = (GLfloat *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2550,7 +2522,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfloat *)
@@ -2608,7 +2580,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glMaterialfv(
@@ -2643,11 +2615,11 @@
     GLfixed *params = (GLfixed *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -2680,7 +2652,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfixed *)
@@ -2738,7 +2710,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glMaterialxv(
@@ -2771,11 +2743,11 @@
     GLfloat *m = (GLfloat *) 0;
 
     if (!m_ref) {
-        _env->ThrowNew(IAEClass, "m == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "m == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(m_ref) - offset;
@@ -2820,11 +2792,11 @@
     GLfixed *m = (GLfixed *) 0;
 
     if (!m_ref) {
-        _env->ThrowNew(IAEClass, "m == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "m == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(m_ref) - offset;
@@ -3205,11 +3177,11 @@
     GLfloat *params = (GLfloat *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -3236,7 +3208,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfloat *)
@@ -3288,7 +3260,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glTexEnvfv(
@@ -3323,11 +3295,11 @@
     GLfixed *params = (GLfixed *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -3354,7 +3326,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfixed *)
@@ -3406,7 +3378,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glTexEnvxv(
@@ -3569,18 +3541,18 @@
 
     if (!mantissa_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "mantissa == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "mantissa == null");
         goto exit;
     }
     if (mantissaOffset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "mantissaOffset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "mantissaOffset < 0");
         goto exit;
     }
     _mantissaRemaining = _env->GetArrayLength(mantissa_ref) - mantissaOffset;
     if (_mantissaRemaining < 16) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - mantissaOffset < 16");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - mantissaOffset < 16");
         goto exit;
     }
     mantissa_base = (GLfixed *)
@@ -3589,18 +3561,18 @@
 
     if (!exponent_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "exponent == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "exponent == null");
         goto exit;
     }
     if (exponentOffset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "exponentOffset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "exponentOffset < 0");
         goto exit;
     }
     _exponentRemaining = _env->GetArrayLength(exponent_ref) - exponentOffset;
     if (_exponentRemaining < 16) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - exponentOffset < 16");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - exponentOffset < 16");
         goto exit;
     }
     exponent_base = (GLint *)
@@ -3640,13 +3612,13 @@
     mantissa = (GLfixed *)getPointer(_env, mantissa_buf, &_mantissaArray, &_mantissaRemaining);
     if (_mantissaRemaining < 16) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < 16");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 16");
         goto exit;
     }
     exponent = (GLint *)getPointer(_env, exponent_buf, &_exponentArray, &_exponentRemaining);
     if (_exponentRemaining < 16) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < 16");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 16");
         goto exit;
     }
     _returnValue = glQueryMatrixxOES(
@@ -3685,7 +3657,7 @@
     if (data_buf) {
         data = (GLvoid *)getPointer(_env, data_buf, &_array, &_remaining);
         if (_remaining < size) {
-            _env->ThrowNew(IAEClass, "remaining() < size");
+            jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < size");
             goto exit;
         }
     }
@@ -3712,7 +3684,7 @@
 
     data = (GLvoid *)getPointer(_env, data_buf, &_array, &_remaining);
     if (_remaining < size) {
-        _env->ThrowNew(IAEClass, "remaining() < size");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < size");
         goto exit;
     }
     glBufferSubData(
@@ -3737,16 +3709,16 @@
     GLfloat *equation = (GLfloat *) 0;
 
     if (!equation_ref) {
-        _env->ThrowNew(IAEClass, "equation == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "equation == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(equation_ref) - offset;
     if (_remaining < 4) {
-        _env->ThrowNew(IAEClass, "length - offset < 4");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 4");
         goto exit;
     }
     equation_base = (GLfloat *)
@@ -3775,7 +3747,7 @@
 
     equation = (GLfloat *)getPointer(_env, equation_buf, &_array, &_remaining);
     if (_remaining < 4) {
-        _env->ThrowNew(IAEClass, "remaining() < 4");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 4");
         goto exit;
     }
     glClipPlanef(
@@ -3798,16 +3770,16 @@
     GLfixed *equation = (GLfixed *) 0;
 
     if (!equation_ref) {
-        _env->ThrowNew(IAEClass, "equation == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "equation == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(equation_ref) - offset;
     if (_remaining < 4) {
-        _env->ThrowNew(IAEClass, "length - offset < 4");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 4");
         goto exit;
     }
     equation_base = (GLfixed *)
@@ -3836,7 +3808,7 @@
 
     equation = (GLfixed *)getPointer(_env, equation_buf, &_array, &_remaining);
     if (_remaining < 4) {
-        _env->ThrowNew(IAEClass, "remaining() < 4");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 4");
         goto exit;
     }
     glClipPlanex(
@@ -3883,16 +3855,16 @@
     GLuint *buffers = (GLuint *) 0;
 
     if (!buffers_ref) {
-        _env->ThrowNew(IAEClass, "buffers == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "buffers == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(buffers_ref) - offset;
     if (_remaining < n) {
-        _env->ThrowNew(IAEClass, "length - offset < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
         goto exit;
     }
     buffers_base = (GLuint *)
@@ -3921,7 +3893,7 @@
 
     buffers = (GLuint *)getPointer(_env, buffers_buf, &_array, &_remaining);
     if (_remaining < n) {
-        _env->ThrowNew(IAEClass, "remaining() < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
         goto exit;
     }
     glDeleteBuffers(
@@ -3958,18 +3930,18 @@
 
     if (!buffers_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "buffers == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "buffers == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(buffers_ref) - offset;
     if (_remaining < n) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
         goto exit;
     }
     buffers_base = (GLuint *)
@@ -4000,7 +3972,7 @@
     buffers = (GLuint *)getPointer(_env, buffers_buf, &_array, &_remaining);
     if (_remaining < n) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
         goto exit;
     }
     glGenBuffers(
@@ -4025,12 +3997,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -4073,7 +4045,7 @@
 static void
 android_glGetBufferParameteriv__II_3II
   (JNIEnv *_env, jobject _this, jint target, jint pname, jintArray params_ref, jint offset) {
-    _env->ThrowNew(UOEClass,
+    jniThrowException(_env, "java/lang/UnsupportedOperationException",
         "glGetBufferParameteriv");
 }
 
@@ -4081,7 +4053,7 @@
 static void
 android_glGetBufferParameteriv__IILjava_nio_IntBuffer_2
   (JNIEnv *_env, jobject _this, jint target, jint pname, jobject params_buf) {
-    _env->ThrowNew(UOEClass,
+    jniThrowException(_env, "java/lang/UnsupportedOperationException",
         "glGetBufferParameteriv");
 }
 
@@ -4096,12 +4068,12 @@
 
     if (!eqn_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "eqn == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "eqn == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(eqn_ref) - offset;
@@ -4151,12 +4123,12 @@
 
     if (!eqn_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "eqn == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "eqn == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(eqn_ref) - offset;
@@ -4206,12 +4178,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -4261,12 +4233,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -4316,12 +4288,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -4369,7 +4341,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfloat *)
@@ -4443,7 +4415,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glGetLightfv(
@@ -4469,12 +4441,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -4522,7 +4494,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfixed *)
@@ -4596,7 +4568,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glGetLightxv(
@@ -4622,12 +4594,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -4661,7 +4633,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfloat *)
@@ -4721,7 +4693,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glGetMaterialfv(
@@ -4747,12 +4719,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -4786,7 +4758,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfixed *)
@@ -4846,7 +4818,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glGetMaterialxv(
@@ -4872,12 +4844,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -4905,7 +4877,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLint *)
@@ -4959,7 +4931,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glGetTexEnviv(
@@ -4985,12 +4957,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -5018,7 +4990,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLfixed *)
@@ -5072,7 +5044,7 @@
     }
     if (_remaining < _needed) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glGetTexEnvxv(
@@ -5098,18 +5070,18 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
     if (_remaining < 1) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
         goto exit;
     }
     params_base = (GLfloat *)
@@ -5141,7 +5113,7 @@
     params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining);
     if (_remaining < 1) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
         goto exit;
     }
     glGetTexParameterfv(
@@ -5167,18 +5139,18 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
     if (_remaining < 1) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
         goto exit;
     }
     params_base = (GLint *)
@@ -5210,7 +5182,7 @@
     params = (GLint *)getPointer(_env, params_buf, &_array, &_remaining);
     if (_remaining < 1) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
         goto exit;
     }
     glGetTexParameteriv(
@@ -5236,18 +5208,18 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
     if (_remaining < 1) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
         goto exit;
     }
     params_base = (GLfixed *)
@@ -5279,7 +5251,7 @@
     params = (GLfixed *)getPointer(_env, params_buf, &_array, &_remaining);
     if (_remaining < 1) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
         goto exit;
     }
     glGetTexParameterxv(
@@ -5357,16 +5329,16 @@
     GLfloat *params = (GLfloat *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
     if (_remaining < 1) {
-        _env->ThrowNew(IAEClass, "length - offset < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
         goto exit;
     }
     params_base = (GLfloat *)
@@ -5395,7 +5367,7 @@
 
     params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining);
     if (_remaining < 1) {
-        _env->ThrowNew(IAEClass, "remaining() < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
         goto exit;
     }
     glPointParameterfv(
@@ -5428,16 +5400,16 @@
     GLfixed *params = (GLfixed *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
     if (_remaining < 1) {
-        _env->ThrowNew(IAEClass, "length - offset < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
         goto exit;
     }
     params_base = (GLfixed *)
@@ -5466,7 +5438,7 @@
 
     params = (GLfixed *)getPointer(_env, params_buf, &_array, &_remaining);
     if (_remaining < 1) {
-        _env->ThrowNew(IAEClass, "remaining() < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
         goto exit;
     }
     glPointParameterxv(
@@ -5534,11 +5506,11 @@
     GLint *params = (GLint *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -5565,7 +5537,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "length - offset < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < needed");
         goto exit;
     }
     params_base = (GLint *)
@@ -5617,7 +5589,7 @@
             break;
     }
     if (_remaining < _needed) {
-        _env->ThrowNew(IAEClass, "remaining() < needed");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < needed");
         goto exit;
     }
     glTexEnviv(
@@ -5641,16 +5613,16 @@
     GLfloat *params = (GLfloat *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
     if (_remaining < 1) {
-        _env->ThrowNew(IAEClass, "length - offset < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
         goto exit;
     }
     params_base = (GLfloat *)
@@ -5680,7 +5652,7 @@
 
     params = (GLfloat *)getPointer(_env, params_buf, &_array, &_remaining);
     if (_remaining < 1) {
-        _env->ThrowNew(IAEClass, "remaining() < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
         goto exit;
     }
     glTexParameterfv(
@@ -5715,16 +5687,16 @@
     GLint *params = (GLint *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
     if (_remaining < 1) {
-        _env->ThrowNew(IAEClass, "length - offset < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
         goto exit;
     }
     params_base = (GLint *)
@@ -5754,7 +5726,7 @@
 
     params = (GLint *)getPointer(_env, params_buf, &_array, &_remaining);
     if (_remaining < 1) {
-        _env->ThrowNew(IAEClass, "remaining() < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
         goto exit;
     }
     glTexParameteriv(
@@ -5778,16 +5750,16 @@
     GLfixed *params = (GLfixed *) 0;
 
     if (!params_ref) {
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
     if (_remaining < 1) {
-        _env->ThrowNew(IAEClass, "length - offset < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 1");
         goto exit;
     }
     params_base = (GLfixed *)
@@ -5817,7 +5789,7 @@
 
     params = (GLfixed *)getPointer(_env, params_buf, &_array, &_remaining);
     if (_remaining < 1) {
-        _env->ThrowNew(IAEClass, "remaining() < 1");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 1");
         goto exit;
     }
     glTexParameterxv(
@@ -5875,16 +5847,16 @@
     GLfloat *coords = (GLfloat *) 0;
 
     if (!coords_ref) {
-        _env->ThrowNew(IAEClass, "coords == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "coords == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(coords_ref) - offset;
     if (_remaining < 5) {
-        _env->ThrowNew(IAEClass, "length - offset < 5");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 5");
         goto exit;
     }
     coords_base = (GLfloat *)
@@ -5912,7 +5884,7 @@
 
     coords = (GLfloat *)getPointer(_env, coords_buf, &_array, &_remaining);
     if (_remaining < 5) {
-        _env->ThrowNew(IAEClass, "remaining() < 5");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 5");
         goto exit;
     }
     glDrawTexfvOES(
@@ -5947,16 +5919,16 @@
     GLint *coords = (GLint *) 0;
 
     if (!coords_ref) {
-        _env->ThrowNew(IAEClass, "coords == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "coords == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(coords_ref) - offset;
     if (_remaining < 5) {
-        _env->ThrowNew(IAEClass, "length - offset < 5");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 5");
         goto exit;
     }
     coords_base = (GLint *)
@@ -5984,7 +5956,7 @@
 
     coords = (GLint *)getPointer(_env, coords_buf, &_array, &_remaining);
     if (_remaining < 5) {
-        _env->ThrowNew(IAEClass, "remaining() < 5");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 5");
         goto exit;
     }
     glDrawTexivOES(
@@ -6019,16 +5991,16 @@
     GLshort *coords = (GLshort *) 0;
 
     if (!coords_ref) {
-        _env->ThrowNew(IAEClass, "coords == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "coords == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(coords_ref) - offset;
     if (_remaining < 5) {
-        _env->ThrowNew(IAEClass, "length - offset < 5");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 5");
         goto exit;
     }
     coords_base = (GLshort *)
@@ -6056,7 +6028,7 @@
 
     coords = (GLshort *)getPointer(_env, coords_buf, &_array, &_remaining);
     if (_remaining < 5) {
-        _env->ThrowNew(IAEClass, "remaining() < 5");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 5");
         goto exit;
     }
     glDrawTexsvOES(
@@ -6091,16 +6063,16 @@
     GLfixed *coords = (GLfixed *) 0;
 
     if (!coords_ref) {
-        _env->ThrowNew(IAEClass, "coords == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "coords == null");
         goto exit;
     }
     if (offset < 0) {
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(coords_ref) - offset;
     if (_remaining < 5) {
-        _env->ThrowNew(IAEClass, "length - offset < 5");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < 5");
         goto exit;
     }
     coords_base = (GLfixed *)
@@ -6128,7 +6100,7 @@
 
     coords = (GLfixed *)getPointer(_env, coords_buf, &_array, &_remaining);
     if (_remaining < 5) {
-        _env->ThrowNew(IAEClass, "remaining() < 5");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < 5");
         goto exit;
     }
     glDrawTexxvOES(
@@ -6223,7 +6195,7 @@
 android_glBindFramebufferOES__II
   (JNIEnv *_env, jobject _this, jint target, jint framebuffer) {
     if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glBindFramebufferOES");
             return;
     }
@@ -6238,7 +6210,7 @@
 android_glBindRenderbufferOES__II
   (JNIEnv *_env, jobject _this, jint target, jint renderbuffer) {
     if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glBindRenderbufferOES");
             return;
     }
@@ -6253,7 +6225,7 @@
 android_glBlendEquation__I
   (JNIEnv *_env, jobject _this, jint mode) {
     if (! supportsExtension(_env, _this, have_OES_blend_subtractID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glBlendEquation");
             return;
     }
@@ -6267,7 +6239,7 @@
 android_glBlendEquationSeparate__II
   (JNIEnv *_env, jobject _this, jint modeRGB, jint modeAlpha) {
     if (! supportsExtension(_env, _this, have_OES_blend_equation_separateID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glBlendEquationSeparate");
             return;
     }
@@ -6282,7 +6254,7 @@
 android_glBlendFuncSeparate__IIII
   (JNIEnv *_env, jobject _this, jint srcRGB, jint dstRGB, jint srcAlpha, jint dstAlpha) {
     if (! supportsExtension(_env, _this, have_OES_blend_equation_separateID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glBlendFuncSeparate");
             return;
     }
@@ -6299,7 +6271,7 @@
 android_glCheckFramebufferStatusOES__I
   (JNIEnv *_env, jobject _this, jint target) {
     if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glCheckFramebufferStatusOES");
             return 0;
     }
@@ -6315,7 +6287,7 @@
 android_glDeleteFramebuffersOES__I_3II
   (JNIEnv *_env, jobject _this, jint n, jintArray framebuffers_ref, jint offset) {
     if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glDeleteFramebuffersOES");
             return;
     }
@@ -6326,18 +6298,18 @@
 
     if (!framebuffers_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "framebuffers == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "framebuffers == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(framebuffers_ref) - offset;
     if (_remaining < n) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
         goto exit;
     }
     framebuffers_base = (GLuint *)
@@ -6361,7 +6333,7 @@
 android_glDeleteFramebuffersOES__ILjava_nio_IntBuffer_2
   (JNIEnv *_env, jobject _this, jint n, jobject framebuffers_buf) {
     if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glDeleteFramebuffersOES");
             return;
     }
@@ -6373,7 +6345,7 @@
     framebuffers = (GLuint *)getPointer(_env, framebuffers_buf, &_array, &_remaining);
     if (_remaining < n) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
         goto exit;
     }
     glDeleteFramebuffersOES(
@@ -6392,7 +6364,7 @@
 android_glDeleteRenderbuffersOES__I_3II
   (JNIEnv *_env, jobject _this, jint n, jintArray renderbuffers_ref, jint offset) {
     if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glDeleteRenderbuffersOES");
             return;
     }
@@ -6403,18 +6375,18 @@
 
     if (!renderbuffers_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "renderbuffers == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "renderbuffers == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(renderbuffers_ref) - offset;
     if (_remaining < n) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
         goto exit;
     }
     renderbuffers_base = (GLuint *)
@@ -6438,7 +6410,7 @@
 android_glDeleteRenderbuffersOES__ILjava_nio_IntBuffer_2
   (JNIEnv *_env, jobject _this, jint n, jobject renderbuffers_buf) {
     if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glDeleteRenderbuffersOES");
             return;
     }
@@ -6450,7 +6422,7 @@
     renderbuffers = (GLuint *)getPointer(_env, renderbuffers_buf, &_array, &_remaining);
     if (_remaining < n) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
         goto exit;
     }
     glDeleteRenderbuffersOES(
@@ -6469,7 +6441,7 @@
 android_glFramebufferRenderbufferOES__IIII
   (JNIEnv *_env, jobject _this, jint target, jint attachment, jint renderbuffertarget, jint renderbuffer) {
     if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glFramebufferRenderbufferOES");
             return;
     }
@@ -6486,7 +6458,7 @@
 android_glFramebufferTexture2DOES__IIIII
   (JNIEnv *_env, jobject _this, jint target, jint attachment, jint textarget, jint texture, jint level) {
     if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glFramebufferTexture2DOES");
             return;
     }
@@ -6504,7 +6476,7 @@
 android_glGenerateMipmapOES__I
   (JNIEnv *_env, jobject _this, jint target) {
     if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glGenerateMipmapOES");
             return;
     }
@@ -6518,7 +6490,7 @@
 android_glGenFramebuffersOES__I_3II
   (JNIEnv *_env, jobject _this, jint n, jintArray framebuffers_ref, jint offset) {
     if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glGenFramebuffersOES");
             return;
     }
@@ -6529,18 +6501,18 @@
 
     if (!framebuffers_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "framebuffers == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "framebuffers == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(framebuffers_ref) - offset;
     if (_remaining < n) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
         goto exit;
     }
     framebuffers_base = (GLuint *)
@@ -6564,7 +6536,7 @@
 android_glGenFramebuffersOES__ILjava_nio_IntBuffer_2
   (JNIEnv *_env, jobject _this, jint n, jobject framebuffers_buf) {
     if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glGenFramebuffersOES");
             return;
     }
@@ -6576,7 +6548,7 @@
     framebuffers = (GLuint *)getPointer(_env, framebuffers_buf, &_array, &_remaining);
     if (_remaining < n) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
         goto exit;
     }
     glGenFramebuffersOES(
@@ -6595,7 +6567,7 @@
 android_glGenRenderbuffersOES__I_3II
   (JNIEnv *_env, jobject _this, jint n, jintArray renderbuffers_ref, jint offset) {
     if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glGenRenderbuffersOES");
             return;
     }
@@ -6606,18 +6578,18 @@
 
     if (!renderbuffers_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "renderbuffers == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "renderbuffers == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(renderbuffers_ref) - offset;
     if (_remaining < n) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "length - offset < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "length - offset < n");
         goto exit;
     }
     renderbuffers_base = (GLuint *)
@@ -6641,7 +6613,7 @@
 android_glGenRenderbuffersOES__ILjava_nio_IntBuffer_2
   (JNIEnv *_env, jobject _this, jint n, jobject renderbuffers_buf) {
     if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glGenRenderbuffersOES");
             return;
     }
@@ -6653,7 +6625,7 @@
     renderbuffers = (GLuint *)getPointer(_env, renderbuffers_buf, &_array, &_remaining);
     if (_remaining < n) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "remaining() < n");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "remaining() < n");
         goto exit;
     }
     glGenRenderbuffersOES(
@@ -6672,7 +6644,7 @@
 android_glGetFramebufferAttachmentParameterivOES__III_3II
   (JNIEnv *_env, jobject _this, jint target, jint attachment, jint pname, jintArray params_ref, jint offset) {
     if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glGetFramebufferAttachmentParameterivOES");
             return;
     }
@@ -6683,12 +6655,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -6715,7 +6687,7 @@
 android_glGetFramebufferAttachmentParameterivOES__IIILjava_nio_IntBuffer_2
   (JNIEnv *_env, jobject _this, jint target, jint attachment, jint pname, jobject params_buf) {
     if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glGetFramebufferAttachmentParameterivOES");
             return;
     }
@@ -6741,7 +6713,7 @@
 android_glGetRenderbufferParameterivOES__II_3II
   (JNIEnv *_env, jobject _this, jint target, jint pname, jintArray params_ref, jint offset) {
     if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glGetRenderbufferParameterivOES");
             return;
     }
@@ -6752,12 +6724,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -6783,7 +6755,7 @@
 android_glGetRenderbufferParameterivOES__IILjava_nio_IntBuffer_2
   (JNIEnv *_env, jobject _this, jint target, jint pname, jobject params_buf) {
     if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glGetRenderbufferParameterivOES");
             return;
     }
@@ -6808,7 +6780,7 @@
 android_glGetTexGenfv__II_3FI
   (JNIEnv *_env, jobject _this, jint coord, jint pname, jfloatArray params_ref, jint offset) {
     if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glGetTexGenfv");
             return;
     }
@@ -6819,12 +6791,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -6850,7 +6822,7 @@
 android_glGetTexGenfv__IILjava_nio_FloatBuffer_2
   (JNIEnv *_env, jobject _this, jint coord, jint pname, jobject params_buf) {
     if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glGetTexGenfv");
             return;
     }
@@ -6875,7 +6847,7 @@
 android_glGetTexGeniv__II_3II
   (JNIEnv *_env, jobject _this, jint coord, jint pname, jintArray params_ref, jint offset) {
     if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glGetTexGeniv");
             return;
     }
@@ -6886,12 +6858,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -6917,7 +6889,7 @@
 android_glGetTexGeniv__IILjava_nio_IntBuffer_2
   (JNIEnv *_env, jobject _this, jint coord, jint pname, jobject params_buf) {
     if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glGetTexGeniv");
             return;
     }
@@ -6942,7 +6914,7 @@
 android_glGetTexGenxv__II_3II
   (JNIEnv *_env, jobject _this, jint coord, jint pname, jintArray params_ref, jint offset) {
     if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glGetTexGenxv");
             return;
     }
@@ -6953,12 +6925,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -6984,7 +6956,7 @@
 android_glGetTexGenxv__IILjava_nio_IntBuffer_2
   (JNIEnv *_env, jobject _this, jint coord, jint pname, jobject params_buf) {
     if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glGetTexGenxv");
             return;
     }
@@ -7009,7 +6981,7 @@
 android_glIsFramebufferOES__I
   (JNIEnv *_env, jobject _this, jint framebuffer) {
     if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glIsFramebufferOES");
             return JNI_FALSE;
     }
@@ -7025,7 +6997,7 @@
 android_glIsRenderbufferOES__I
   (JNIEnv *_env, jobject _this, jint renderbuffer) {
     if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glIsRenderbufferOES");
             return JNI_FALSE;
     }
@@ -7041,7 +7013,7 @@
 android_glRenderbufferStorageOES__IIII
   (JNIEnv *_env, jobject _this, jint target, jint internalformat, jint width, jint height) {
     if (! supportsExtension(_env, _this, have_OES_framebuffer_objectID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glRenderbufferStorageOES");
             return;
     }
@@ -7058,7 +7030,7 @@
 android_glTexGenf__IIF
   (JNIEnv *_env, jobject _this, jint coord, jint pname, jfloat param) {
     if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glTexGenf");
             return;
     }
@@ -7074,7 +7046,7 @@
 android_glTexGenfv__II_3FI
   (JNIEnv *_env, jobject _this, jint coord, jint pname, jfloatArray params_ref, jint offset) {
     if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glTexGenfv");
             return;
     }
@@ -7085,12 +7057,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -7116,7 +7088,7 @@
 android_glTexGenfv__IILjava_nio_FloatBuffer_2
   (JNIEnv *_env, jobject _this, jint coord, jint pname, jobject params_buf) {
     if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glTexGenfv");
             return;
     }
@@ -7141,7 +7113,7 @@
 android_glTexGeni__III
   (JNIEnv *_env, jobject _this, jint coord, jint pname, jint param) {
     if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glTexGeni");
             return;
     }
@@ -7157,7 +7129,7 @@
 android_glTexGeniv__II_3II
   (JNIEnv *_env, jobject _this, jint coord, jint pname, jintArray params_ref, jint offset) {
     if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glTexGeniv");
             return;
     }
@@ -7168,12 +7140,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -7199,7 +7171,7 @@
 android_glTexGeniv__IILjava_nio_IntBuffer_2
   (JNIEnv *_env, jobject _this, jint coord, jint pname, jobject params_buf) {
     if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glTexGeniv");
             return;
     }
@@ -7224,7 +7196,7 @@
 android_glTexGenx__III
   (JNIEnv *_env, jobject _this, jint coord, jint pname, jint param) {
     if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glTexGenx");
             return;
     }
@@ -7240,7 +7212,7 @@
 android_glTexGenxv__II_3II
   (JNIEnv *_env, jobject _this, jint coord, jint pname, jintArray params_ref, jint offset) {
     if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glTexGenxv");
             return;
     }
@@ -7251,12 +7223,12 @@
 
     if (!params_ref) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "params == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "params == null");
         goto exit;
     }
     if (offset < 0) {
         _exception = 1;
-        _env->ThrowNew(IAEClass, "offset < 0");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "offset < 0");
         goto exit;
     }
     _remaining = _env->GetArrayLength(params_ref) - offset;
@@ -7282,7 +7254,7 @@
 android_glTexGenxv__IILjava_nio_IntBuffer_2
   (JNIEnv *_env, jobject _this, jint coord, jint pname, jobject params_buf) {
     if (! supportsExtension(_env, _this, have_OES_texture_cube_mapID)) {
-        _env->ThrowNew(UOEClass,
+        jniThrowException(_env, "java/lang/UnsupportedOperationException",
             "glTexGenxv");
             return;
     }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 2a4d1b2..7f18121 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -607,6 +607,13 @@
         android:label="@string/permlab_reorderTasks"
         android:description="@string/permdesc_reorderTasks" />
 
+    <!-- Allows an application to change to remove/kill tasks -->
+    <permission android:name="android.permission.REMOVE_TASKS"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="signature"
+        android:label="@string/permlab_removeTasks"
+        android:description="@string/permdesc_removeTasks" />
+
     <!-- Allows an application to modify the current configuration, such
          as locale. -->
     <permission android:name="android.permission.CHANGE_CONFIGURATION"
diff --git a/core/res/res/raw/execute_script_android.js b/core/res/res/raw/execute_script_android.js
new file mode 100644
index 0000000..d145754
--- /dev/null
+++ b/core/res/res/raw/execute_script_android.js
@@ -0,0 +1,8 @@
+function(){return function(){function h(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(c=="[object Window]")return"object";if(c=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(c=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
+else if(b=="function"&&typeof a.call=="undefined")return"object";return b}function i(a){var b=h(a);return b=="array"||b=="object"&&typeof a.length=="number"}function j(a){a=h(a);return a=="object"||a=="array"||a=="function"}var k=Date.now||function(){return+new Date};function l(a,b){function c(){}c.prototype=b.prototype;a.c=b.prototype;a.prototype=new c};function m(a){this.stack=Error().stack||"";if(a)this.message=String(a)}l(m,Error);m.prototype.name="CustomError";function n(a,b,c){var d={};for(var e in a)if(b.call(c,a[e],e,a))d[e]=a[e];return d}function o(a,b,c){var d={};for(var e in a)d[e]=b.call(c,a[e],e,a);return d}function p(a,b,c){for(var d in a)if(b.call(c,a[d],d,a))return d};function q(a,b){m.call(this,b);this.code=a;this.name=r[a]||r[13]}l(q,m);var r,s={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},t={};for(var u in s)t[s[u]]=u;r=t;
+q.prototype.toString=function(){return"["+this.name+"] "+this.message};function v(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a};function w(a,b){b.unshift(a);m.call(this,v.apply(null,b));b.shift();this.b=a}l(w,m);w.prototype.name="AssertionError";function x(a,b){if(!a){var c=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(b){d+=": "+b;var e=c}throw new w(""+d,e||[]);}return a};var y=Array.prototype,z=y.map?function(a,b,c){x(a.length!=null);return y.map.call(a,b,c)}:function(a,b,c){var d=a.length,e=Array(d),f=typeof a=="string"?a.split(""):a;for(var g=0;g<d;g++)if(g in f)e[g]=b.call(c,f[g],g,a);return e};var A="",B;if(B=/WebKit\/(\S+)/){var C=B.exec(this.navigator?this.navigator.userAgent:null);A=C?C[1]:""};function D(){}
+function E(a,b,c){switch(typeof b){case "string":F(a,b,c);break;case "number":c.push(isFinite(b)&&!isNaN(b)?b:"null");break;case "boolean":c.push(b);break;case "undefined":c.push("null");break;case "object":if(b==null){c.push("null");break}if(h(b)=="array"){var d=b.length;c.push("[");var e="";for(var f=0;f<d;f++){c.push(e);E(a,b[f],c);e=","}c.push("]");break}c.push("{");d="";for(e in b)if(Object.prototype.hasOwnProperty.call(b,e)){f=b[e];if(typeof f!="function"){c.push(d);F(a,e,c);c.push(":");E(a,
+f,c);d=","}}c.push("}");break;case "function":break;default:throw Error("Unknown type: "+typeof b);}}var G={'"':'\\"',"\\":"\\\\","/":"\\/","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\u000b":"\\u000b"},H=/\uffff/.test("\uffff")?/[\\\"\x00-\x1f\x7f-\uffff]/g:/[\\\"\x00-\x1f\x7f-\xff]/g;
+function F(a,b,c){c.push('"',b.replace(H,function(d){if(d in G)return G[d];var e=d.charCodeAt(0),f="\\u";if(e<16)f+="000";else if(e<256)f+="00";else if(e<4096)f+="0";return G[d]=f+e.toString(16)}),'"')};function I(a){switch(h(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return z(a,I);case "object":a=a;if("tagName"in a&&"nodeType"in a&&a.nodeType==1){var b={};b.ELEMENT=J(a);return b}if(i(a))return z(a,I);a=n(a,function(c,d){return typeof d=="number"||typeof d=="string"});return o(a,I);default:return null}}
+function K(a,b){if(h(a)=="array")return z(a,function(c){return K(c,b)});else if(j(a))return"ELEMENT"in a?L(a.ELEMENT,b):o(a,function(c){return K(c,b)});return a}function M(a){a=a||document;var b=a.$wdc_;if(!b){b=a.$wdc_={};b.a=k()}return b}function J(a){var b=M(a.ownerDocument),c=p(b,function(d){return d==a});if(!c){c=":wdc:"+b.a++;b[c]=a}return c}
+function L(a,b){a=decodeURIComponent(a);var c=b||document,d=M(c);if(!(a in d))throw new q(10,"Element does not exist in cache");var e=d[a];for(var f=e;f;){if(f==c.documentElement)return e;f=f.parentNode}delete d[a];throw new q(10,"Element is no longer attached to the DOM");};function N(a,b,c){var d;try{if(typeof a=="string")a=new Function(a);var e=K(b),f=a.apply(null,e);d={status:0,value:I(f)}}catch(g){d={status:"code"in g?g.code:13,value:{message:g.message}}}if(c){a=[];E(new D,d,a);d=a.join("")}else d=d;return d}var O="_".split("."),P=this;!(O[0]in P)&&P.execScript&&P.execScript("var "+O[0]);for(var Q;O.length&&(Q=O.shift());)if(!O.length&&N!==undefined)P[Q]=N;else P=P[Q]?P[Q]:P[Q]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/find_element_android.js b/core/res/res/raw/find_element_android.js
new file mode 100644
index 0000000..f493223
--- /dev/null
+++ b/core/res/res/raw/find_element_android.js
@@ -0,0 +1,26 @@
+function(){return function(){var i=this;
+function j(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(c=="[object Window]")return"object";if(c=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(c=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if(b==
+"function"&&typeof a.call=="undefined")return"object";return b}function aa(a){var b=j(a);return b=="array"||b=="object"&&typeof a.length=="number"}function k(a){return typeof a=="string"}function l(a){return j(a)=="function"}function ba(a){a=j(a);return a=="object"||a=="array"||a=="function"}var ca=Date.now||function(){return+new Date};function m(a,b){function c(){}c.prototype=b.prototype;a.m=b.prototype;a.prototype=new c};function n(a){this.stack=Error().stack||"";if(a)this.message=String(a)}m(n,Error);n.prototype.name="CustomError";function da(a,b,c){var d={};for(var e in a)if(b.call(c,a[e],e,a))d[e]=a[e];return d}function ea(a,b,c){var d={};for(var e in a)d[e]=b.call(c,a[e],e,a);return d}function fa(a,b,c){for(var d in a)if(b.call(c,a[d],d,a))return d};function o(a,b){n.call(this,b);this.code=a;this.name=p[a]||p[13]}m(o,n);var p,ga={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},ha={};for(var ia in ga)ha[ga[ia]]=ia;p=ha;
+o.prototype.toString=function(){return"["+this.name+"] "+this.message};function ja(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a}function q(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")}function r(a,b){if(a<b)return-1;else if(a>b)return 1;return 0}var ka={};function la(a){return ka[a]||(ka[a]=String(a).replace(/\-([a-z])/g,function(b,c){return c.toUpperCase()}))};function s(a,b){b.unshift(a);n.call(this,ja.apply(null,b));b.shift();this.n=a}m(s,n);s.prototype.name="AssertionError";function t(a,b){if(!a){var c=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(b){d+=": "+b;var e=c}throw new s(""+d,e||[]);}return a};var u=Array.prototype,ma=u.indexOf?function(a,b,c){t(a.length!=null);return u.indexOf.call(a,b,c)}:function(a,b,c){c=c==null?0:c<0?Math.max(0,a.length+c):c;if(k(a)){if(!k(b)||b.length!=1)return-1;return a.indexOf(b,c)}for(c=c;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},na=u.forEach?function(a,b,c){t(a.length!=null);u.forEach.call(a,b,c)}:function(a,b,c){var d=a.length,e=k(a)?a.split(""):a;for(var f=0;f<d;f++)f in e&&b.call(c,e[f],f,a)},v=u.filter?function(a,b,c){t(a.length!=null);return u.filter.call(a,
+b,c)}:function(a,b,c){var d=a.length,e=[],f=0,g=k(a)?a.split(""):a;for(var h=0;h<d;h++)if(h in g){var G=g[h];if(b.call(c,G,h,a))e[f++]=G}return e},w=u.map?function(a,b,c){t(a.length!=null);return u.map.call(a,b,c)}:function(a,b,c){var d=a.length,e=Array(d),f=k(a)?a.split(""):a;for(var g=0;g<d;g++)if(g in f)e[g]=b.call(c,f[g],g,a);return e},oa=u.some?function(a,b,c){t(a.length!=null);return u.some.call(a,b,c)}:function(a,b,c){var d=a.length,e=k(a)?a.split(""):a;for(var f=0;f<d;f++)if(f in e&&b.call(c,
+e[f],f,a))return true;return false};function x(a,b,c){a:{var d=a.length,e=k(a)?a.split(""):a;for(var f=0;f<d;f++)if(f in e&&b.call(c,e[f],f,a)){b=f;break a}b=-1}return b<0?null:k(a)?a.charAt(b):a[b]};var A=true,pa,qa="",B;if(A)B=/WebKit\/(\S+)/;if(B){var ra=B.exec(i.navigator?i.navigator.userAgent:null);qa=ra?ra[1]:""}pa=qa;var sa={};var ta;function C(a,b){this.width=a;this.height=b}C.prototype.toString=function(){return"("+this.width+" x "+this.height+")"};function D(a){return a?new E(F(a)):ta||(ta=new E)}function H(a,b){if(a.contains&&b.nodeType==1)return a==b||a.contains(b);if(typeof a.compareDocumentPosition!="undefined")return a==b||Boolean(a.compareDocumentPosition(b)&16);for(;b&&a!=b;)b=b.parentNode;return b==a}function F(a){return a.nodeType==9?a:a.ownerDocument||a.document}function ua(a,b){var c=[];return va(a,b,c,true)?c[0]:undefined}
+function va(a,b,c,d){if(a!=null){var e=0;for(var f;f=a.childNodes[e];e++){if(b(f)){c.push(f);if(d)return true}if(va(f,b,c,d))return true}}return false}function wa(a,b,c,d){if(!c)a=a.parentNode;c=d==null;for(var e=0;a&&(c||e<=d);){if(b(a))return a;a=a.parentNode;e++}return null}function E(a){this.g=a||i.document||document}
+function I(a,b,c,d){a=d||a.g;b=b&&b!="*"?b.toUpperCase():"";if(d=a.querySelectorAll){if(d=a.querySelector){if(!(d=!A)){if(!(d=document.compatMode=="CSS1Compat")){if(!(d=sa["528"])){d=0;var e=q(String(pa)).split("."),f=q(String("528")).split("."),g=Math.max(e.length,f.length);for(var h=0;d==0&&h<g;h++){var G=e[h]||"",Oa=f[h]||"",Pa=RegExp("(\\d*)(\\D*)","g"),Qa=RegExp("(\\d*)(\\D*)","g");do{var y=Pa.exec(G)||["","",""],z=Qa.exec(Oa)||["","",""];if(y[0].length==0&&z[0].length==0)break;d=r(y[1].length==
+0?0:parseInt(y[1],10),z[1].length==0?0:parseInt(z[1],10))||r(y[2].length==0,z[2].length==0)||r(y[2],z[2])}while(d==0)}d=sa["528"]=d>=0}d=d}d=d}d=d}d=d}if(d&&(b||c))c=a.querySelectorAll(b+(c?"."+c:""));else if(c&&a.getElementsByClassName){a=a.getElementsByClassName(c);if(b){d={};f=e=0;for(;g=a[f];f++)if(b==g.nodeName)d[e++]=g;d.length=e;c=d}else c=a}else{a=a.getElementsByTagName(b||"*");if(c){d={};e=0;for(f=0;g=a[f];f++){b=g.className;if(typeof b.split=="function"&&ma(b.split(/\s+/),c)>=0)d[e++]=g}d.length=
+e;c=d}else c=a}return c}E.prototype.contains=H;function xa(){}
+function J(a,b,c){switch(typeof b){case "string":ya(a,b,c);break;case "number":c.push(isFinite(b)&&!isNaN(b)?b:"null");break;case "boolean":c.push(b);break;case "undefined":c.push("null");break;case "object":if(b==null){c.push("null");break}if(j(b)=="array"){var d=b.length;c.push("[");var e="";for(var f=0;f<d;f++){c.push(e);J(a,b[f],c);e=","}c.push("]");break}c.push("{");d="";for(e in b)if(Object.prototype.hasOwnProperty.call(b,e)){f=b[e];if(typeof f!="function"){c.push(d);ya(a,e,c);c.push(":");J(a,
+f,c);d=","}}c.push("}");break;case "function":break;default:throw Error("Unknown type: "+typeof b);}}var K={'"':'\\"',"\\":"\\\\","/":"\\/","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\u000b":"\\u000b"},za=/\uffff/.test("\uffff")?/[\\\"\x00-\x1f\x7f-\uffff]/g:/[\\\"\x00-\x1f\x7f-\xff]/g;
+function ya(a,b,c){c.push('"',b.replace(za,function(d){if(d in K)return K[d];var e=d.charCodeAt(0),f="\\u";if(e<16)f+="000";else if(e<256)f+="00";else if(e<4096)f+="0";return K[d]=f+e.toString(16)}),'"')};function L(a){switch(j(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return w(a,L);case "object":a=a;if("tagName"in a&&"nodeType"in a&&a.nodeType==1){var b={};b.ELEMENT=Aa(a);return b}if(aa(a))return w(a,L);a=da(a,function(c,d){return typeof d=="number"||k(d)});return ea(a,L);default:return null}}
+function M(a,b){if(j(a)=="array")return w(a,function(c){return M(c,b)});else if(ba(a))return"ELEMENT"in a?Ba(a.ELEMENT,b):ea(a,function(c){return M(c,b)});return a}function Ca(a){a=a||document;var b=a.$wdc_;if(!b){b=a.$wdc_={};b.l=ca()}return b}function Aa(a){var b=Ca(a.ownerDocument),c=fa(b,function(d){return d==a});if(!c){c=":wdc:"+b.l++;b[c]=a}return c}
+function Ba(a,b){a=decodeURIComponent(a);var c=b||document,d=Ca(c);if(!(a in d))throw new o(10,"Element does not exist in cache");var e=d[a];for(var f=e;f;){if(f==c.documentElement)return e;f=f.parentNode}delete d[a];throw new o(10,"Element is no longer attached to the DOM");};var Da=window;var N={};N.b=function(a,b){if(!a)throw Error("No class name specified");a=q(a);if(a.split(/\s+/).length>1)throw Error("Compound class names not permitted");var c=I(D(b),"*",a,b);return c.length?c[0]:null};N.e=function(a,b){if(!a)throw Error("No class name specified");a=q(a);if(a.split(/\s+/).length>1)throw Error("Compound class names not permitted");return I(D(b),"*",a,b)};var O={};O.b=function(a,b){if(!l(b.querySelector)&&0)throw Error("CSS selection is not supported");if(!a)throw Error("No selector specified");if(a.split(/,/).length>1)throw Error("Compound selectors not permitted");a=q(a);var c=b.querySelector(a);return c&&c.nodeType==1?c:null};O.e=function(a,b){if(!l(b.querySelectorAll)&&0)throw Error("CSS selection is not supported");if(!a)throw Error("No selector specified");if(a.split(/,/).length>1)throw Error("Compound selectors not permitted");a=q(a);return b.querySelectorAll(a)};function Ea(a,b){if(typeof a.selectNodes!="undefined"){var c=F(a);typeof c.setProperty!="undefined"&&c.setProperty("SelectionLanguage","XPath");return a.selectNodes(b)}else if(document.implementation.hasFeature("XPath","3.0")){c=F(a);var d=c.createNSResolver(c.documentElement);c=c.evaluate(b,a,d,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);d=[];var e=c.snapshotLength;for(var f=0;f<e;f++)d.push(c.snapshotItem(f));return d}else return[]};var P={};P.b=function(a,b){try{var c;if(typeof b.selectSingleNode!="undefined"){var d=F(b);typeof d.setProperty!="undefined"&&d.setProperty("SelectionLanguage","XPath");c=b.selectSingleNode(a)}else if(document.implementation.hasFeature("XPath","3.0")){d=F(b);var e=d.createNSResolver(d.documentElement);c=d.evaluate(a,b,e,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue}else c=null}catch(f){return null}if(!c)return null;if(c.nodeType!=1)throw Error("Returned node is not an element: "+a);return c};
+P.e=function(a,b){var c=Ea(b,a);na(c,function(d){if(d.nodeType!=1)throw Error("Returned nodes must be elements: "+a);});return c};var Fa="StopIteration"in i?i.StopIteration:Error("StopIteration");function Ga(){}Ga.prototype.next=function(){throw Fa;};function Q(a,b,c,d,e){this.a=!!b;a&&R(this,a,d);this.f=e!=undefined?e:this.d||0;if(this.a)this.f*=-1;this.k=!c}m(Q,Ga);Q.prototype.c=null;Q.prototype.d=0;Q.prototype.j=false;function R(a,b,c,d){if(a.c=b)a.d=typeof c=="number"?c:a.c.nodeType!=1?0:a.a?-1:1;if(typeof d=="number")a.f=d}
+Q.prototype.next=function(){var a;if(this.j){if(!this.c||this.k&&this.f==0)throw Fa;a=this.c;var b=this.a?-1:1;if(this.d==b){var c=this.a?a.lastChild:a.firstChild;c?R(this,c):R(this,a,b*-1)}else(c=this.a?a.previousSibling:a.nextSibling)?R(this,c):R(this,a.parentNode,b*-1);this.f+=this.d*(this.a?-1:1)}else this.j=true;a=this.c;if(!this.c)throw Fa;return a};
+Q.prototype.splice=function(){var a=this.c,b=this.a?1:-1;if(this.d==b){this.d=b*-1;this.f+=this.d*(this.a?-1:1)}this.a=!this.a;Q.prototype.next.call(this);this.a=!this.a;b=aa(arguments[0])?arguments[0]:arguments;for(var c=b.length-1;c>=0;c--)a.parentNode&&a.parentNode.insertBefore(b[c],a.nextSibling);a&&a.parentNode&&a.parentNode.removeChild(a)};function Ha(a,b,c,d){Q.call(this,a,b,c,null,d)}m(Ha,Q);Ha.prototype.next=function(){do Ha.m.next.call(this);while(this.d==-1);return this.c};function Ia(a,b){var c=F(a);if(c.defaultView&&c.defaultView.getComputedStyle)if(c=c.defaultView.getComputedStyle(a,null))return c[b]||c.getPropertyValue(b);return""};function S(a,b){return!!a&&a.nodeType==1&&(!b||a.tagName.toUpperCase()==b)}
+var Ja=["async","autofocus","autoplay","checked","compact","complete","controls","declare","defaultchecked","defaultselected","defer","disabled","draggable","ended","formnovalidate","hidden","indeterminate","iscontenteditable","ismap","itemscope","loop","multiple","muted","nohref","noresize","noshade","novalidate","nowrap","open","paused","pubdate","readonly","required","reversed","scoped","seamless","seeking","selected","spellcheck","truespeed","willvalidate"];
+function T(a,b){if(8==a.nodeType)return null;b=b.toLowerCase();if(b=="style"){var c=q(a.style.cssText).toLowerCase();return c.charAt(c.length-1)==";"?c:c+";"}c=a.getAttributeNode(b);if(!c)return null;if(ma(Ja,b)>=0)return"true";return c.specified?c.value:null}function U(a){for(a=a.parentNode;a&&a.nodeType!=1&&a.nodeType!=9&&a.nodeType!=11;)a=a.parentNode;return S(a)?a:null}function V(a,b){b=la(String(b));return Ia(a,b)||Ka(a,b)}
+function Ka(a,b){var c=(a.currentStyle||a.style)[b];if(c!="inherit")return c!==undefined?c:null;return(c=U(a))?Ka(c,b):null}
+function La(a){if(l(a.getBBox))return a.getBBox();var b;if((Ia(a,"display")||(a.currentStyle?a.currentStyle.display:null)||a.style.display)!="none")b=new C(a.offsetWidth,a.offsetHeight);else{b=a.style;var c=b.display,d=b.visibility,e=b.position;b.visibility="hidden";b.position="absolute";b.display="inline";var f;f=a.offsetWidth;a=a.offsetHeight;b.display=c;b.position=e;b.visibility=d;b=new C(f,a)}return b}
+function W(a,b){function c(f){if(V(f,"display")=="none")return false;f=U(f);return!f||c(f)}function d(f){var g=La(f);if(g.height>0&&g.width>0)return true;if(f.innerText||f.textContent)if(Ma.test(f.innerText||f.textContent))return true;return A&&oa(f.childNodes,function(h){return S(h)&&d(h)})}if(!S(a))throw Error("Argument to isShown must be of type Element");if(S(a,"TITLE"))return(F(a)?F(a).parentWindow||F(a).defaultView:window)==Da;if(S(a,"OPTION")||S(a,"OPTGROUP")){var e=wa(a,function(f){return S(f,
+"SELECT")});return!!e&&W(e)}if(S(a,"MAP")){if(!a.name)return false;e=F(a);e=e.evaluate?P.b('/descendant::*[@usemap = "#'+a.name+'"]',e):ua(e,function(f){return S(f)&&T(f,"usemap")=="#"+a.name});return!!e&&W(e)}if(S(a,"AREA")){e=wa(a,function(f){return S(f,"MAP")});return!!e&&W(e)}if(S(a,"INPUT")&&a.type.toLowerCase()=="hidden")return false;if(V(a,"visibility")=="hidden")return false;if(!c(a))return false;if(!b&&Na(a)==0)return false;if(!d(a))return false;return true}
+function Ra(a){var b=[""];Sa(a,b);b=w(b,q);return q(b.join("\n"))}function Sa(a,b){if(S(a,"BR"))b.push("");else{var c=Ta(a);c&&b[b.length-1]&&b.push("");na(a.childNodes,function(d){if(d.nodeType==3){var e=U(d);if(e){W(e);if(e&&W(e)){d=d.nodeValue.replace(Ua," ");e=b.pop();var f=e.length-1;if(f>=0&&e.indexOf(" ",f)==f&&d.lastIndexOf(" ",0)==0)d=d.substr(1);b.push(e+d)}}}else S(d)&&Sa(d,b)});c&&b[b.length-1]&&b.push("")}}function Ta(a){a=V(a,"display");return a=="block"||a=="inline-block"}
+var Va="[\\s\\xa0"+String.fromCharCode(160)+"]+",Ua=RegExp(Va,"g"),Ma=RegExp("^"+Va+"$");function Na(a){var b=1,c=V(a,"opacity");if(c)b=Number(c);if(a=U(a))b*=Na(a);return b};var Wa={};Wa.b=function(a,b){var c=D(b),d=k(a)?c.g.getElementById(a):a;if(!d)return null;if(T(d,"id")==a&&H(b,d))return d;c=I(c,"*");return x(c,function(e){return T(e,"id")==a&&H(b,e)})};Wa.e=function(a,b){var c=I(D(b),"*",null,b);return v(c,function(d){return T(d,"id")==a})};var X={},Xa={};X.i=function(a,b,c){b=I(D(b),"A",null,b);return x(b,function(d){d=Ra(d);return c&&d.indexOf(a)!=-1||d==a})};X.h=function(a,b,c){b=I(D(b),"A",null,b);return v(b,function(d){d=Ra(d);return c&&d.indexOf(a)!=-1||d==a})};X.b=function(a,b){return X.i(a,b,false)};X.e=function(a,b){return X.h(a,b,false)};Xa.b=function(a,b){return X.i(a,b,true)};Xa.e=function(a,b){return X.h(a,b,true)};var Ya={};Ya.b=function(a,b){var c=I(D(b),"*",null,b);return x(c,function(d){return T(d,"name")==a})};Ya.e=function(a,b){var c=I(D(b),"*",null,b);return v(c,function(d){return T(d,"name")==a})};var Za={};Za.b=function(a,b){return I(D(b),a,null,b)[0]||null};Za.e=function(a,b){return I(D(b),a,null,b)};var $a={className:N,css:O,id:Wa,linkText:X,name:Ya,partialLinkText:Xa,tagName:Za,xpath:P};function ab(a,b){var c;a:{for(c in a){c=c;break a}c=void 0}if(c){var d=$a[c];if(d&&l(d.b))return d.b(a[c],b||F(Da))}throw Error("Unsupported locator strategy: "+c);};function bb(a,b,c){var d={};d[a]=b;a=ab;c=[d,c];var e;try{if(k(a))a=new Function(a);var f=M(c),g=a.apply(null,f);e={status:0,value:L(g)}}catch(h){e={status:"code"in h?h.code:13,value:{message:h.message}}}f=[];J(new xa,e,f);return f.join("")}var Y="_".split("."),Z=i;!(Y[0]in Z)&&Z.execScript&&Z.execScript("var "+Y[0]);for(var $;Y.length&&($=Y.shift());)if(!Y.length&&bb!==undefined)Z[$]=bb;else Z=Z[$]?Z[$]:Z[$]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/find_elements_android.js b/core/res/res/raw/find_elements_android.js
new file mode 100644
index 0000000..796da58
--- /dev/null
+++ b/core/res/res/raw/find_elements_android.js
@@ -0,0 +1,26 @@
+function(){return function(){var i=this;
+function j(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(c=="[object Window]")return"object";if(c=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(c=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if(b==
+"function"&&typeof a.call=="undefined")return"object";return b}function aa(a){var b=j(a);return b=="array"||b=="object"&&typeof a.length=="number"}function k(a){return typeof a=="string"}function l(a){return j(a)=="function"}function ba(a){a=j(a);return a=="object"||a=="array"||a=="function"}var ca=Date.now||function(){return+new Date};function m(a,b){function c(){}c.prototype=b.prototype;a.m=b.prototype;a.prototype=new c};function n(a){this.stack=Error().stack||"";if(a)this.message=String(a)}m(n,Error);n.prototype.name="CustomError";function da(a,b,c){var d={};for(var e in a)if(b.call(c,a[e],e,a))d[e]=a[e];return d}function ea(a,b,c){var d={};for(var e in a)d[e]=b.call(c,a[e],e,a);return d}function fa(a,b,c){for(var d in a)if(b.call(c,a[d],d,a))return d};function o(a,b){n.call(this,b);this.code=a;this.name=p[a]||p[13]}m(o,n);var p,ga={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},ha={};for(var ia in ga)ha[ga[ia]]=ia;p=ha;
+o.prototype.toString=function(){return"["+this.name+"] "+this.message};function ja(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a}function q(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")}function r(a,b){if(a<b)return-1;else if(a>b)return 1;return 0}var ka={};function la(a){return ka[a]||(ka[a]=String(a).replace(/\-([a-z])/g,function(b,c){return c.toUpperCase()}))};function s(a,b){b.unshift(a);n.call(this,ja.apply(null,b));b.shift();this.n=a}m(s,n);s.prototype.name="AssertionError";function t(a,b){if(!a){var c=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(b){d+=": "+b;var e=c}throw new s(""+d,e||[]);}return a};var u=Array.prototype,ma=u.indexOf?function(a,b,c){t(a.length!=null);return u.indexOf.call(a,b,c)}:function(a,b,c){c=c==null?0:c<0?Math.max(0,a.length+c):c;if(k(a)){if(!k(b)||b.length!=1)return-1;return a.indexOf(b,c)}for(c=c;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},na=u.forEach?function(a,b,c){t(a.length!=null);u.forEach.call(a,b,c)}:function(a,b,c){var d=a.length,e=k(a)?a.split(""):a;for(var f=0;f<d;f++)f in e&&b.call(c,e[f],f,a)},v=u.filter?function(a,b,c){t(a.length!=null);return u.filter.call(a,
+b,c)}:function(a,b,c){var d=a.length,e=[],f=0,g=k(a)?a.split(""):a;for(var h=0;h<d;h++)if(h in g){var G=g[h];if(b.call(c,G,h,a))e[f++]=G}return e},w=u.map?function(a,b,c){t(a.length!=null);return u.map.call(a,b,c)}:function(a,b,c){var d=a.length,e=Array(d),f=k(a)?a.split(""):a;for(var g=0;g<d;g++)if(g in f)e[g]=b.call(c,f[g],g,a);return e},oa=u.some?function(a,b,c){t(a.length!=null);return u.some.call(a,b,c)}:function(a,b,c){var d=a.length,e=k(a)?a.split(""):a;for(var f=0;f<d;f++)if(f in e&&b.call(c,
+e[f],f,a))return true;return false};function x(a,b,c){a:{var d=a.length,e=k(a)?a.split(""):a;for(var f=0;f<d;f++)if(f in e&&b.call(c,e[f],f,a)){b=f;break a}b=-1}return b<0?null:k(a)?a.charAt(b):a[b]};var A=true,pa,qa="",B;if(A)B=/WebKit\/(\S+)/;if(B){var ra=B.exec(i.navigator?i.navigator.userAgent:null);qa=ra?ra[1]:""}pa=qa;var sa={};var ta;function C(a,b){this.width=a;this.height=b}C.prototype.toString=function(){return"("+this.width+" x "+this.height+")"};function D(a){return a?new E(F(a)):ta||(ta=new E)}function H(a,b){if(a.contains&&b.nodeType==1)return a==b||a.contains(b);if(typeof a.compareDocumentPosition!="undefined")return a==b||Boolean(a.compareDocumentPosition(b)&16);for(;b&&a!=b;)b=b.parentNode;return b==a}function F(a){return a.nodeType==9?a:a.ownerDocument||a.document}function ua(a,b){var c=[];return va(a,b,c,true)?c[0]:undefined}
+function va(a,b,c,d){if(a!=null){var e=0;for(var f;f=a.childNodes[e];e++){if(b(f)){c.push(f);if(d)return true}if(va(f,b,c,d))return true}}return false}function wa(a,b,c,d){if(!c)a=a.parentNode;c=d==null;for(var e=0;a&&(c||e<=d);){if(b(a))return a;a=a.parentNode;e++}return null}function E(a){this.g=a||i.document||document}
+function I(a,b,c,d){a=d||a.g;b=b&&b!="*"?b.toUpperCase():"";if(d=a.querySelectorAll){if(d=a.querySelector){if(!(d=!A)){if(!(d=document.compatMode=="CSS1Compat")){if(!(d=sa["528"])){d=0;var e=q(String(pa)).split("."),f=q(String("528")).split("."),g=Math.max(e.length,f.length);for(var h=0;d==0&&h<g;h++){var G=e[h]||"",Oa=f[h]||"",Pa=RegExp("(\\d*)(\\D*)","g"),Qa=RegExp("(\\d*)(\\D*)","g");do{var y=Pa.exec(G)||["","",""],z=Qa.exec(Oa)||["","",""];if(y[0].length==0&&z[0].length==0)break;d=r(y[1].length==
+0?0:parseInt(y[1],10),z[1].length==0?0:parseInt(z[1],10))||r(y[2].length==0,z[2].length==0)||r(y[2],z[2])}while(d==0)}d=sa["528"]=d>=0}d=d}d=d}d=d}d=d}if(d&&(b||c))c=a.querySelectorAll(b+(c?"."+c:""));else if(c&&a.getElementsByClassName){a=a.getElementsByClassName(c);if(b){d={};f=e=0;for(;g=a[f];f++)if(b==g.nodeName)d[e++]=g;d.length=e;c=d}else c=a}else{a=a.getElementsByTagName(b||"*");if(c){d={};e=0;for(f=0;g=a[f];f++){b=g.className;if(typeof b.split=="function"&&ma(b.split(/\s+/),c)>=0)d[e++]=g}d.length=
+e;c=d}else c=a}return c}E.prototype.contains=H;function xa(){}
+function J(a,b,c){switch(typeof b){case "string":ya(a,b,c);break;case "number":c.push(isFinite(b)&&!isNaN(b)?b:"null");break;case "boolean":c.push(b);break;case "undefined":c.push("null");break;case "object":if(b==null){c.push("null");break}if(j(b)=="array"){var d=b.length;c.push("[");var e="";for(var f=0;f<d;f++){c.push(e);J(a,b[f],c);e=","}c.push("]");break}c.push("{");d="";for(e in b)if(Object.prototype.hasOwnProperty.call(b,e)){f=b[e];if(typeof f!="function"){c.push(d);ya(a,e,c);c.push(":");J(a,
+f,c);d=","}}c.push("}");break;case "function":break;default:throw Error("Unknown type: "+typeof b);}}var K={'"':'\\"',"\\":"\\\\","/":"\\/","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\u000b":"\\u000b"},za=/\uffff/.test("\uffff")?/[\\\"\x00-\x1f\x7f-\uffff]/g:/[\\\"\x00-\x1f\x7f-\xff]/g;
+function ya(a,b,c){c.push('"',b.replace(za,function(d){if(d in K)return K[d];var e=d.charCodeAt(0),f="\\u";if(e<16)f+="000";else if(e<256)f+="00";else if(e<4096)f+="0";return K[d]=f+e.toString(16)}),'"')};function L(a){switch(j(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return w(a,L);case "object":a=a;if("tagName"in a&&"nodeType"in a&&a.nodeType==1){var b={};b.ELEMENT=Aa(a);return b}if(aa(a))return w(a,L);a=da(a,function(c,d){return typeof d=="number"||k(d)});return ea(a,L);default:return null}}
+function M(a,b){if(j(a)=="array")return w(a,function(c){return M(c,b)});else if(ba(a))return"ELEMENT"in a?Ba(a.ELEMENT,b):ea(a,function(c){return M(c,b)});return a}function Ca(a){a=a||document;var b=a.$wdc_;if(!b){b=a.$wdc_={};b.l=ca()}return b}function Aa(a){var b=Ca(a.ownerDocument),c=fa(b,function(d){return d==a});if(!c){c=":wdc:"+b.l++;b[c]=a}return c}
+function Ba(a,b){a=decodeURIComponent(a);var c=b||document,d=Ca(c);if(!(a in d))throw new o(10,"Element does not exist in cache");var e=d[a];for(var f=e;f;){if(f==c.documentElement)return e;f=f.parentNode}delete d[a];throw new o(10,"Element is no longer attached to the DOM");};var Da=window;var N={};N.d=function(a,b){if(!a)throw Error("No class name specified");a=q(a);if(a.split(/\s+/).length>1)throw Error("Compound class names not permitted");var c=I(D(b),"*",a,b);return c.length?c[0]:null};N.b=function(a,b){if(!a)throw Error("No class name specified");a=q(a);if(a.split(/\s+/).length>1)throw Error("Compound class names not permitted");return I(D(b),"*",a,b)};var O={};O.d=function(a,b){if(!l(b.querySelector)&&0)throw Error("CSS selection is not supported");if(!a)throw Error("No selector specified");if(a.split(/,/).length>1)throw Error("Compound selectors not permitted");a=q(a);var c=b.querySelector(a);return c&&c.nodeType==1?c:null};O.b=function(a,b){if(!l(b.querySelectorAll)&&0)throw Error("CSS selection is not supported");if(!a)throw Error("No selector specified");if(a.split(/,/).length>1)throw Error("Compound selectors not permitted");a=q(a);return b.querySelectorAll(a)};function Ea(a,b){if(typeof a.selectNodes!="undefined"){var c=F(a);typeof c.setProperty!="undefined"&&c.setProperty("SelectionLanguage","XPath");return a.selectNodes(b)}else if(document.implementation.hasFeature("XPath","3.0")){c=F(a);var d=c.createNSResolver(c.documentElement);c=c.evaluate(b,a,d,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);d=[];var e=c.snapshotLength;for(var f=0;f<e;f++)d.push(c.snapshotItem(f));return d}else return[]};var P={};P.d=function(a,b){try{var c;if(typeof b.selectSingleNode!="undefined"){var d=F(b);typeof d.setProperty!="undefined"&&d.setProperty("SelectionLanguage","XPath");c=b.selectSingleNode(a)}else if(document.implementation.hasFeature("XPath","3.0")){d=F(b);var e=d.createNSResolver(d.documentElement);c=d.evaluate(a,b,e,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue}else c=null}catch(f){return null}if(!c)return null;if(c.nodeType!=1)throw Error("Returned node is not an element: "+a);return c};
+P.b=function(a,b){var c=Ea(b,a);na(c,function(d){if(d.nodeType!=1)throw Error("Returned nodes must be elements: "+a);});return c};var Fa="StopIteration"in i?i.StopIteration:Error("StopIteration");function Ga(){}Ga.prototype.next=function(){throw Fa;};function Q(a,b,c,d,e){this.a=!!b;a&&R(this,a,d);this.f=e!=undefined?e:this.e||0;if(this.a)this.f*=-1;this.k=!c}m(Q,Ga);Q.prototype.c=null;Q.prototype.e=0;Q.prototype.j=false;function R(a,b,c,d){if(a.c=b)a.e=typeof c=="number"?c:a.c.nodeType!=1?0:a.a?-1:1;if(typeof d=="number")a.f=d}
+Q.prototype.next=function(){var a;if(this.j){if(!this.c||this.k&&this.f==0)throw Fa;a=this.c;var b=this.a?-1:1;if(this.e==b){var c=this.a?a.lastChild:a.firstChild;c?R(this,c):R(this,a,b*-1)}else(c=this.a?a.previousSibling:a.nextSibling)?R(this,c):R(this,a.parentNode,b*-1);this.f+=this.e*(this.a?-1:1)}else this.j=true;a=this.c;if(!this.c)throw Fa;return a};
+Q.prototype.splice=function(){var a=this.c,b=this.a?1:-1;if(this.e==b){this.e=b*-1;this.f+=this.e*(this.a?-1:1)}this.a=!this.a;Q.prototype.next.call(this);this.a=!this.a;b=aa(arguments[0])?arguments[0]:arguments;for(var c=b.length-1;c>=0;c--)a.parentNode&&a.parentNode.insertBefore(b[c],a.nextSibling);a&&a.parentNode&&a.parentNode.removeChild(a)};function Ha(a,b,c,d){Q.call(this,a,b,c,null,d)}m(Ha,Q);Ha.prototype.next=function(){do Ha.m.next.call(this);while(this.e==-1);return this.c};function Ia(a,b){var c=F(a);if(c.defaultView&&c.defaultView.getComputedStyle)if(c=c.defaultView.getComputedStyle(a,null))return c[b]||c.getPropertyValue(b);return""};function S(a,b){return!!a&&a.nodeType==1&&(!b||a.tagName.toUpperCase()==b)}
+var Ja=["async","autofocus","autoplay","checked","compact","complete","controls","declare","defaultchecked","defaultselected","defer","disabled","draggable","ended","formnovalidate","hidden","indeterminate","iscontenteditable","ismap","itemscope","loop","multiple","muted","nohref","noresize","noshade","novalidate","nowrap","open","paused","pubdate","readonly","required","reversed","scoped","seamless","seeking","selected","spellcheck","truespeed","willvalidate"];
+function T(a,b){if(8==a.nodeType)return null;b=b.toLowerCase();if(b=="style"){var c=q(a.style.cssText).toLowerCase();return c.charAt(c.length-1)==";"?c:c+";"}c=a.getAttributeNode(b);if(!c)return null;if(ma(Ja,b)>=0)return"true";return c.specified?c.value:null}function U(a){for(a=a.parentNode;a&&a.nodeType!=1&&a.nodeType!=9&&a.nodeType!=11;)a=a.parentNode;return S(a)?a:null}function V(a,b){b=la(String(b));return Ia(a,b)||Ka(a,b)}
+function Ka(a,b){var c=(a.currentStyle||a.style)[b];if(c!="inherit")return c!==undefined?c:null;return(c=U(a))?Ka(c,b):null}
+function La(a){if(l(a.getBBox))return a.getBBox();var b;if((Ia(a,"display")||(a.currentStyle?a.currentStyle.display:null)||a.style.display)!="none")b=new C(a.offsetWidth,a.offsetHeight);else{b=a.style;var c=b.display,d=b.visibility,e=b.position;b.visibility="hidden";b.position="absolute";b.display="inline";var f;f=a.offsetWidth;a=a.offsetHeight;b.display=c;b.position=e;b.visibility=d;b=new C(f,a)}return b}
+function W(a,b){function c(f){if(V(f,"display")=="none")return false;f=U(f);return!f||c(f)}function d(f){var g=La(f);if(g.height>0&&g.width>0)return true;if(f.innerText||f.textContent)if(Ma.test(f.innerText||f.textContent))return true;return A&&oa(f.childNodes,function(h){return S(h)&&d(h)})}if(!S(a))throw Error("Argument to isShown must be of type Element");if(S(a,"TITLE"))return(F(a)?F(a).parentWindow||F(a).defaultView:window)==Da;if(S(a,"OPTION")||S(a,"OPTGROUP")){var e=wa(a,function(f){return S(f,
+"SELECT")});return!!e&&W(e)}if(S(a,"MAP")){if(!a.name)return false;e=F(a);e=e.evaluate?P.d('/descendant::*[@usemap = "#'+a.name+'"]',e):ua(e,function(f){return S(f)&&T(f,"usemap")=="#"+a.name});return!!e&&W(e)}if(S(a,"AREA")){e=wa(a,function(f){return S(f,"MAP")});return!!e&&W(e)}if(S(a,"INPUT")&&a.type.toLowerCase()=="hidden")return false;if(V(a,"visibility")=="hidden")return false;if(!c(a))return false;if(!b&&Na(a)==0)return false;if(!d(a))return false;return true}
+function Ra(a){var b=[""];Sa(a,b);b=w(b,q);return q(b.join("\n"))}function Sa(a,b){if(S(a,"BR"))b.push("");else{var c=Ta(a);c&&b[b.length-1]&&b.push("");na(a.childNodes,function(d){if(d.nodeType==3){var e=U(d);if(e){W(e);if(e&&W(e)){d=d.nodeValue.replace(Ua," ");e=b.pop();var f=e.length-1;if(f>=0&&e.indexOf(" ",f)==f&&d.lastIndexOf(" ",0)==0)d=d.substr(1);b.push(e+d)}}}else S(d)&&Sa(d,b)});c&&b[b.length-1]&&b.push("")}}function Ta(a){a=V(a,"display");return a=="block"||a=="inline-block"}
+var Va="[\\s\\xa0"+String.fromCharCode(160)+"]+",Ua=RegExp(Va,"g"),Ma=RegExp("^"+Va+"$");function Na(a){var b=1,c=V(a,"opacity");if(c)b=Number(c);if(a=U(a))b*=Na(a);return b};var Wa={};Wa.d=function(a,b){var c=D(b),d=k(a)?c.g.getElementById(a):a;if(!d)return null;if(T(d,"id")==a&&H(b,d))return d;c=I(c,"*");return x(c,function(e){return T(e,"id")==a&&H(b,e)})};Wa.b=function(a,b){var c=I(D(b),"*",null,b);return v(c,function(d){return T(d,"id")==a})};var X={},Xa={};X.i=function(a,b,c){b=I(D(b),"A",null,b);return x(b,function(d){d=Ra(d);return c&&d.indexOf(a)!=-1||d==a})};X.h=function(a,b,c){b=I(D(b),"A",null,b);return v(b,function(d){d=Ra(d);return c&&d.indexOf(a)!=-1||d==a})};X.d=function(a,b){return X.i(a,b,false)};X.b=function(a,b){return X.h(a,b,false)};Xa.d=function(a,b){return X.i(a,b,true)};Xa.b=function(a,b){return X.h(a,b,true)};var Ya={};Ya.d=function(a,b){var c=I(D(b),"*",null,b);return x(c,function(d){return T(d,"name")==a})};Ya.b=function(a,b){var c=I(D(b),"*",null,b);return v(c,function(d){return T(d,"name")==a})};var Za={};Za.d=function(a,b){return I(D(b),a,null,b)[0]||null};Za.b=function(a,b){return I(D(b),a,null,b)};var $a={className:N,css:O,id:Wa,linkText:X,name:Ya,partialLinkText:Xa,tagName:Za,xpath:P};function ab(a,b){var c;a:{for(c in a){c=c;break a}c=void 0}if(c){var d=$a[c];if(d&&l(d.b))return d.b(a[c],b||F(Da))}throw Error("Unsupported locator strategy: "+c);};function bb(a,b,c){var d={};d[a]=b;a=ab;c=[d,c];var e;try{if(k(a))a=new Function(a);var f=M(c),g=a.apply(null,f);e={status:0,value:L(g)}}catch(h){e={status:"code"in h?h.code:13,value:{message:h.message}}}f=[];J(new xa,e,f);return f.join("")}var Y="_".split("."),Z=i;!(Y[0]in Z)&&Z.execScript&&Z.execScript("var "+Y[0]);for(var $;Y.length&&($=Y.shift());)if(!Y.length&&bb!==undefined)Z[$]=bb;else Z=Z[$]?Z[$]:Z[$]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/get_attribute_value_android.js b/core/res/res/raw/get_attribute_value_android.js
new file mode 100644
index 0000000..e96a868
--- /dev/null
+++ b/core/res/res/raw/get_attribute_value_android.js
@@ -0,0 +1,11 @@
+function(){return function(){function g(a){var c=typeof a;if(c=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return c;var b=Object.prototype.toString.call(a);if(b=="[object Window]")return"object";if(b=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(b=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
+else if(c=="function"&&typeof a.call=="undefined")return"object";return c}function i(a){var c=g(a);return c=="array"||c=="object"&&typeof a.length=="number"}function j(a){return typeof a=="string"}function k(a){a=g(a);return a=="object"||a=="array"||a=="function"}Math.floor(Math.random()*2147483648).toString(36);var l=Date.now||function(){return+new Date};function m(a,c){function b(){}b.prototype=c.prototype;a.h=c.prototype;a.prototype=new b};function n(a){this.stack=Error().stack||"";if(a)this.message=String(a)}m(n,Error);n.prototype.name="CustomError";function o(a,c,b){var d={};for(var e in a)if(c.call(b,a[e],e,a))d[e]=a[e];return d}function p(a,c,b){var d={};for(var e in a)d[e]=c.call(b,a[e],e,a);return d}function q(a,c,b){for(var d in a)if(c.call(b,a[d],d,a))return d};function r(a,c){n.call(this,c);this.code=a;this.name=s[a]||s[13]}m(r,n);var s,t={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},u={};for(var v in t)u[t[v]]=v;s=u;
+r.prototype.toString=function(){return"["+this.name+"] "+this.message};function w(a){for(var c=1;c<arguments.length;c++){var b=String(arguments[c]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,b)}return a};function x(a,c){c.unshift(a);n.call(this,w.apply(null,c));c.shift();this.i=a}m(x,n);x.prototype.name="AssertionError";function y(a,c){if(!a){var b=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(c){d+=": "+c;var e=b}throw new x(""+d,e||[]);}return a};var z=Array.prototype,A=z.indexOf?function(a,c,b){y(a.length!=null);return z.indexOf.call(a,c,b)}:function(a,c,b){b=b==null?0:b<0?Math.max(0,a.length+b):b;if(j(a)){if(!j(c)||c.length!=1)return-1;return a.indexOf(c,b)}for(b=b;b<a.length;b++)if(b in a&&a[b]===c)return b;return-1},C=z.map?function(a,c,b){y(a.length!=null);return z.map.call(a,c,b)}:function(a,c,b){var d=a.length,e=Array(d),f=j(a)?a.split(""):a;for(var h=0;h<d;h++)if(h in f)e[h]=c.call(b,f[h],h,a);return e};var D="",E;if(E=/WebKit\/(\S+)/){var F=E.exec(this.navigator?this.navigator.userAgent:null);D=F?F[1]:""};var G="StopIteration"in this?this.StopIteration:Error("StopIteration");function H(){}H.prototype.next=function(){throw G;};function I(a,c,b,d,e){this.a=!!c;a&&J(this,a,d);this.d=e!=undefined?e:this.c||0;if(this.a)this.d*=-1;this.f=!b}m(I,H);I.prototype.b=null;I.prototype.c=0;I.prototype.e=false;function J(a,c,b,d){if(a.b=c)a.c=typeof b=="number"?b:a.b.nodeType!=1?0:a.a?-1:1;if(typeof d=="number")a.d=d}
+I.prototype.next=function(){var a;if(this.e){if(!this.b||this.f&&this.d==0)throw G;a=this.b;var c=this.a?-1:1;if(this.c==c){var b=this.a?a.lastChild:a.firstChild;b?J(this,b):J(this,a,c*-1)}else(b=this.a?a.previousSibling:a.nextSibling)?J(this,b):J(this,a.parentNode,c*-1);this.d+=this.c*(this.a?-1:1)}else this.e=true;a=this.b;if(!this.b)throw G;return a};
+I.prototype.splice=function(){var a=this.b,c=this.a?1:-1;if(this.c==c){this.c=c*-1;this.d+=this.c*(this.a?-1:1)}this.a=!this.a;I.prototype.next.call(this);this.a=!this.a;c=i(arguments[0])?arguments[0]:arguments;for(var b=c.length-1;b>=0;b--)a.parentNode&&a.parentNode.insertBefore(c[b],a.nextSibling);a&&a.parentNode&&a.parentNode.removeChild(a)};function K(a,c,b,d){I.call(this,a,c,b,null,d)}m(K,I);K.prototype.next=function(){do K.h.next.call(this);while(this.c==-1);return this.b};var L=["async","autofocus","autoplay","checked","compact","complete","controls","declare","defaultchecked","defaultselected","defer","disabled","draggable","ended","formnovalidate","hidden","indeterminate","iscontenteditable","ismap","itemscope","loop","multiple","muted","nohref","noresize","noshade","novalidate","nowrap","open","paused","pubdate","readonly","required","reversed","scoped","seamless","seeking","selected","spellcheck","truespeed","willvalidate"];
+function M(a,c){if(8==a.nodeType)return null;c=c.toLowerCase();if(c=="style"){var b=a.style.cssText.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").toLowerCase();return b.charAt(b.length-1)==";"?b:b+";"}b=a.getAttributeNode(c);if(!b)return null;if(A(L,c)>=0)return"true";return b.specified?b.value:null}String.fromCharCode(160);function N(){}
+function O(a,c,b){switch(typeof c){case "string":P(a,c,b);break;case "number":b.push(isFinite(c)&&!isNaN(c)?c:"null");break;case "boolean":b.push(c);break;case "undefined":b.push("null");break;case "object":if(c==null){b.push("null");break}if(g(c)=="array"){var d=c.length;b.push("[");var e="";for(var f=0;f<d;f++){b.push(e);O(a,c[f],b);e=","}b.push("]");break}b.push("{");d="";for(e in c)if(Object.prototype.hasOwnProperty.call(c,e)){f=c[e];if(typeof f!="function"){b.push(d);P(a,e,b);b.push(":");O(a,
+f,b);d=","}}b.push("}");break;case "function":break;default:throw Error("Unknown type: "+typeof c);}}var Q={'"':'\\"',"\\":"\\\\","/":"\\/","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\u000b":"\\u000b"},R=/\uffff/.test("\uffff")?/[\\\"\x00-\x1f\x7f-\uffff]/g:/[\\\"\x00-\x1f\x7f-\xff]/g;
+function P(a,c,b){b.push('"',c.replace(R,function(d){if(d in Q)return Q[d];var e=d.charCodeAt(0),f="\\u";if(e<16)f+="000";else if(e<256)f+="00";else if(e<4096)f+="0";return Q[d]=f+e.toString(16)}),'"')};function S(a){switch(g(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return C(a,S);case "object":a=a;if("tagName"in a&&"nodeType"in a&&a.nodeType==1){var c={};c.ELEMENT=T(a);return c}if(i(a))return C(a,S);a=o(a,function(b,d){return typeof d=="number"||j(d)});return p(a,S);default:return null}}
+function U(a,c){if(g(a)=="array")return C(a,function(b){return U(b,c)});else if(k(a))return"ELEMENT"in a?V(a.ELEMENT,c):p(a,function(b){return U(b,c)});return a}function W(a){a=a||document;var c=a.$wdc_;if(!c){c=a.$wdc_={};c.g=l()}return c}function T(a){var c=W(a.ownerDocument),b=q(c,function(d){return d==a});if(!b){b=":wdc:"+c.g++;c[b]=a}return b}
+function V(a,c){a=decodeURIComponent(a);var b=c||document,d=W(b);if(!(a in d))throw new r(10,"Element does not exist in cache");var e=d[a];for(var f=e;f;){if(f==b.documentElement)return e;f=f.parentNode}delete d[a];throw new r(10,"Element is no longer attached to the DOM");};function X(a,c){var b=M,d=[a,c],e;try{if(j(b))b=new Function(b);var f=U(d),h=b.apply(null,f);e={status:0,value:S(h)}}catch(B){e={status:"code"in B?B.code:13,value:{message:B.message}}}b=[];O(new N,e,b);return b.join("")}var Y="_".split("."),Z=this;!(Y[0]in Z)&&Z.execScript&&Z.execScript("var "+Y[0]);for(var $;Y.length&&($=Y.shift());)if(!Y.length&&X!==undefined)Z[$]=X;else Z=Z[$]?Z[$]:Z[$]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/get_size_android.js b/core/res/res/raw/get_size_android.js
new file mode 100644
index 0000000..78f61ee
--- /dev/null
+++ b/core/res/res/raw/get_size_android.js
@@ -0,0 +1,11 @@
+function(){return function(){function g(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(c=="[object Window]")return"object";if(c=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(c=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
+else if(b=="function"&&typeof a.call=="undefined")return"object";return b}function h(a){var b=g(a);return b=="array"||b=="object"&&typeof a.length=="number"}function i(a){a=g(a);return a=="object"||a=="array"||a=="function"}Math.floor(Math.random()*2147483648).toString(36);var j=Date.now||function(){return+new Date};function l(a,b){function c(){}c.prototype=b.prototype;a.h=b.prototype;a.prototype=new c};function m(a){this.stack=Error().stack||"";if(a)this.message=String(a)}l(m,Error);m.prototype.name="CustomError";function n(a,b,c){var d={};for(var e in a)if(b.call(c,a[e],e,a))d[e]=a[e];return d}function o(a,b,c){var d={};for(var e in a)d[e]=b.call(c,a[e],e,a);return d}function p(a,b,c){for(var d in a)if(b.call(c,a[d],d,a))return d};function q(a,b){m.call(this,b);this.code=a;this.name=r[a]||r[13]}l(q,m);var r,s={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},t={};for(var u in s)t[s[u]]=u;r=t;
+q.prototype.toString=function(){return"["+this.name+"] "+this.message};function v(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a};function w(a,b){b.unshift(a);m.call(this,v.apply(null,b));b.shift();this.i=a}l(w,m);w.prototype.name="AssertionError";function x(a,b){if(!a){var c=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(b){d+=": "+b;var e=c}throw new w(""+d,e||[]);}return a};var y=Array.prototype,z=y.map?function(a,b,c){x(a.length!=null);return y.map.call(a,b,c)}:function(a,b,c){var d=a.length,e=Array(d),f=typeof a=="string"?a.split(""):a;for(var k=0;k<d;k++)if(k in f)e[k]=b.call(c,f[k],k,a);return e};var A="",B;if(B=/WebKit\/(\S+)/){var C=B.exec(this.navigator?this.navigator.userAgent:null);A=C?C[1]:""};function D(a,b){this.width=a;this.height=b}D.prototype.toString=function(){return"("+this.width+" x "+this.height+")"};D.prototype.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};var E="StopIteration"in this?this.StopIteration:Error("StopIteration");function F(){}F.prototype.next=function(){throw E;};function G(a,b,c,d,e){this.a=!!b;a&&H(this,a,d);this.d=e!=undefined?e:this.c||0;if(this.a)this.d*=-1;this.f=!c}l(G,F);G.prototype.b=null;G.prototype.c=0;G.prototype.e=false;function H(a,b,c,d){if(a.b=b)a.c=typeof c=="number"?c:a.b.nodeType!=1?0:a.a?-1:1;if(typeof d=="number")a.d=d}
+G.prototype.next=function(){var a;if(this.e){if(!this.b||this.f&&this.d==0)throw E;a=this.b;var b=this.a?-1:1;if(this.c==b){var c=this.a?a.lastChild:a.firstChild;c?H(this,c):H(this,a,b*-1)}else(c=this.a?a.previousSibling:a.nextSibling)?H(this,c):H(this,a.parentNode,b*-1);this.d+=this.c*(this.a?-1:1)}else this.e=true;a=this.b;if(!this.b)throw E;return a};
+G.prototype.splice=function(){var a=this.b,b=this.a?1:-1;if(this.c==b){this.c=b*-1;this.d+=this.c*(this.a?-1:1)}this.a=!this.a;G.prototype.next.call(this);this.a=!this.a;b=h(arguments[0])?arguments[0]:arguments;for(var c=b.length-1;c>=0;c--)a.parentNode&&a.parentNode.insertBefore(b[c],a.nextSibling);a&&a.parentNode&&a.parentNode.removeChild(a)};function I(a,b,c,d){G.call(this,a,b,c,null,d)}l(I,G);I.prototype.next=function(){do I.h.next.call(this);while(this.c==-1);return this.b};function J(a){if(g(a.getBBox)=="function")return a.getBBox();var b;b:{b=a.nodeType==9?a:a.ownerDocument||a.document;if(b.defaultView&&b.defaultView.getComputedStyle)if(b=b.defaultView.getComputedStyle(a,null)){b=b.display||b.getPropertyValue("display");break b}b=""}if((b||(a.currentStyle?a.currentStyle.display:null)||a.style.display)!="none")a=new D(a.offsetWidth,a.offsetHeight);else{b=a.style;var c=b.display,d=b.visibility,e=b.position;b.visibility="hidden";b.position="absolute";b.display="inline";
+var f;f=a.offsetWidth;a=a.offsetHeight;b.display=c;b.position=e;b.visibility=d;a=new D(f,a)}return a}String.fromCharCode(160);function K(){}
+function L(a,b,c){switch(typeof b){case "string":M(a,b,c);break;case "number":c.push(isFinite(b)&&!isNaN(b)?b:"null");break;case "boolean":c.push(b);break;case "undefined":c.push("null");break;case "object":if(b==null){c.push("null");break}if(g(b)=="array"){var d=b.length;c.push("[");var e="";for(var f=0;f<d;f++){c.push(e);L(a,b[f],c);e=","}c.push("]");break}c.push("{");d="";for(e in b)if(Object.prototype.hasOwnProperty.call(b,e)){f=b[e];if(typeof f!="function"){c.push(d);M(a,e,c);c.push(":");L(a,
+f,c);d=","}}c.push("}");break;case "function":break;default:throw Error("Unknown type: "+typeof b);}}var N={'"':'\\"',"\\":"\\\\","/":"\\/","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\u000b":"\\u000b"},O=/\uffff/.test("\uffff")?/[\\\"\x00-\x1f\x7f-\uffff]/g:/[\\\"\x00-\x1f\x7f-\xff]/g;
+function M(a,b,c){c.push('"',b.replace(O,function(d){if(d in N)return N[d];var e=d.charCodeAt(0),f="\\u";if(e<16)f+="000";else if(e<256)f+="00";else if(e<4096)f+="0";return N[d]=f+e.toString(16)}),'"')};function P(a){switch(g(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return z(a,P);case "object":a=a;if("tagName"in a&&"nodeType"in a&&a.nodeType==1){var b={};b.ELEMENT=Q(a);return b}if(h(a))return z(a,P);a=n(a,function(c,d){return typeof d=="number"||typeof d=="string"});return o(a,P);default:return null}}
+function R(a,b){if(g(a)=="array")return z(a,function(c){return R(c,b)});else if(i(a))return"ELEMENT"in a?S(a.ELEMENT,b):o(a,function(c){return R(c,b)});return a}function T(a){a=a||document;var b=a.$wdc_;if(!b){b=a.$wdc_={};b.g=j()}return b}function Q(a){var b=T(a.ownerDocument),c=p(b,function(d){return d==a});if(!c){c=":wdc:"+b.g++;b[c]=a}return c}
+function S(a,b){a=decodeURIComponent(a);var c=b||document,d=T(c);if(!(a in d))throw new q(10,"Element does not exist in cache");var e=d[a];for(var f=e;f;){if(f==c.documentElement)return e;f=f.parentNode}delete d[a];throw new q(10,"Element is no longer attached to the DOM");};function U(a){var b=J;a=[a];var c;try{if(typeof b=="string")b=new Function(b);var d=R(a),e=b.apply(null,d);c={status:0,value:P(e)}}catch(f){c={status:"code"in f?f.code:13,value:{message:f.message}}}b=[];L(new K,c,b);return b.join("")}var V="_".split("."),W=this;!(V[0]in W)&&W.execScript&&W.execScript("var "+V[0]);for(var X;V.length&&(X=V.shift());)if(!V.length&&U!==undefined)W[X]=U;else W=W[X]?W[X]:W[X]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/get_text_android.js b/core/res/res/raw/get_text_android.js
new file mode 100644
index 0000000..75c3fed
--- /dev/null
+++ b/core/res/res/raw/get_text_android.js
@@ -0,0 +1,19 @@
+function(){return function(){function g(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(c=="[object Window]")return"object";if(c=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(c=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
+else if(b=="function"&&typeof a.call=="undefined")return"object";return b}function i(a){var b=g(a);return b=="array"||b=="object"&&typeof a.length=="number"}function j(a){return typeof a=="string"}function aa(a){a=g(a);return a=="object"||a=="array"||a=="function"}Math.floor(Math.random()*2147483648).toString(36);var ba=Date.now||function(){return+new Date};function k(a,b){function c(){}c.prototype=b.prototype;a.h=b.prototype;a.prototype=new c};function l(a){this.stack=Error().stack||"";if(a)this.message=String(a)}k(l,Error);l.prototype.name="CustomError";function ca(a,b,c){var d={};for(var e in a)if(b.call(c,a[e],e,a))d[e]=a[e];return d}function m(a,b,c){var d={};for(var e in a)d[e]=b.call(c,a[e],e,a);return d}function da(a,b,c){for(var d in a)if(b.call(c,a[d],d,a))return d};function n(a,b){l.call(this,b);this.code=a;this.name=o[a]||o[13]}k(n,l);var o,p={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},q={};for(var r in p)q[p[r]]=r;o=q;
+n.prototype.toString=function(){return"["+this.name+"] "+this.message};var ea=window;function fa(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a}function s(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")}var t={};function ga(a){return t[a]||(t[a]=String(a).replace(/\-([a-z])/g,function(b,c){return c.toUpperCase()}))};function u(a,b){b.unshift(a);l.call(this,fa.apply(null,b));b.shift();this.i=a}k(u,l);u.prototype.name="AssertionError";function v(a,b){if(!a){var c=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(b){d+=": "+b;var e=c}throw new u(""+d,e||[]);}return a};var w=Array.prototype,ha=w.indexOf?function(a,b,c){v(a.length!=null);return w.indexOf.call(a,b,c)}:function(a,b,c){c=c==null?0:c<0?Math.max(0,a.length+c):c;if(j(a)){if(!j(b)||b.length!=1)return-1;return a.indexOf(b,c)}for(c=c;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},ia=w.forEach?function(a,b,c){v(a.length!=null);w.forEach.call(a,b,c)}:function(a,b,c){var d=a.length,e=j(a)?a.split(""):a;for(var f=0;f<d;f++)f in e&&b.call(c,e[f],f,a)},x=w.map?function(a,b,c){v(a.length!=null);return w.map.call(a,
+b,c)}:function(a,b,c){var d=a.length,e=Array(d),f=j(a)?a.split(""):a;for(var h=0;h<d;h++)if(h in f)e[h]=b.call(c,f[h],h,a);return e},ja=w.some?function(a,b,c){v(a.length!=null);return w.some.call(a,b,c)}:function(a,b,c){var d=a.length,e=j(a)?a.split(""):a;for(var f=0;f<d;f++)if(f in e&&b.call(c,e[f],f,a))return true;return false};var y=true,ka="",z;if(y)z=/WebKit\/(\S+)/;if(z){var A=z.exec(this.navigator?this.navigator.userAgent:null);ka=A?A[1]:""};function B(a,b){this.width=a;this.height=b}B.prototype.toString=function(){return"("+this.width+" x "+this.height+")"};B.prototype.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};function C(a){return a.nodeType==9?a:a.ownerDocument||a.document}function la(a,b){var c=[];return D(a,b,c,true)?c[0]:undefined}function D(a,b,c,d){if(a!=null){var e=0;for(var f;f=a.childNodes[e];e++){if(b(f)){c.push(f);if(d)return true}if(D(f,b,c,d))return true}}return false}function E(a,b,c,d){if(!c)a=a.parentNode;c=d==null;for(var e=0;a&&(c||e<=d);){if(b(a))return a;a=a.parentNode;e++}return null};function ma(a,b){try{var c;if(typeof b.selectSingleNode!="undefined"){var d=C(b);typeof d.setProperty!="undefined"&&d.setProperty("SelectionLanguage","XPath");c=b.selectSingleNode(a)}else if(document.implementation.hasFeature("XPath","3.0")){d=C(b);var e=d.createNSResolver(d.documentElement);c=d.evaluate(a,b,e,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue}else c=null}catch(f){return null}if(!c)return null;if(c.nodeType!=1)throw Error("Returned node is not an element: "+a);return c};var F="StopIteration"in this?this.StopIteration:Error("StopIteration");function G(){}G.prototype.next=function(){throw F;};function H(a,b,c,d,e){this.a=!!b;a&&I(this,a,d);this.d=e!=undefined?e:this.c||0;if(this.a)this.d*=-1;this.f=!c}k(H,G);H.prototype.b=null;H.prototype.c=0;H.prototype.e=false;function I(a,b,c,d){if(a.b=b)a.c=typeof c=="number"?c:a.b.nodeType!=1?0:a.a?-1:1;if(typeof d=="number")a.d=d}
+H.prototype.next=function(){var a;if(this.e){if(!this.b||this.f&&this.d==0)throw F;a=this.b;var b=this.a?-1:1;if(this.c==b){var c=this.a?a.lastChild:a.firstChild;c?I(this,c):I(this,a,b*-1)}else(c=this.a?a.previousSibling:a.nextSibling)?I(this,c):I(this,a.parentNode,b*-1);this.d+=this.c*(this.a?-1:1)}else this.e=true;a=this.b;if(!this.b)throw F;return a};
+H.prototype.splice=function(){var a=this.b,b=this.a?1:-1;if(this.c==b){this.c=b*-1;this.d+=this.c*(this.a?-1:1)}this.a=!this.a;H.prototype.next.call(this);this.a=!this.a;b=i(arguments[0])?arguments[0]:arguments;for(var c=b.length-1;c>=0;c--)a.parentNode&&a.parentNode.insertBefore(b[c],a.nextSibling);a&&a.parentNode&&a.parentNode.removeChild(a)};function J(a,b,c,d){H.call(this,a,b,c,null,d)}k(J,H);J.prototype.next=function(){do J.h.next.call(this);while(this.c==-1);return this.b};function K(a,b){var c=C(a);if(c.defaultView&&c.defaultView.getComputedStyle)if(c=c.defaultView.getComputedStyle(a,null))return c[b]||c.getPropertyValue(b);return""};function L(a,b){return!!a&&a.nodeType==1&&(!b||a.tagName.toUpperCase()==b)}
+var na=["async","autofocus","autoplay","checked","compact","complete","controls","declare","defaultchecked","defaultselected","defer","disabled","draggable","ended","formnovalidate","hidden","indeterminate","iscontenteditable","ismap","itemscope","loop","multiple","muted","nohref","noresize","noshade","novalidate","nowrap","open","paused","pubdate","readonly","required","reversed","scoped","seamless","seeking","selected","spellcheck","truespeed","willvalidate"];
+function oa(a,b){if(8==a.nodeType)return null;b=b.toLowerCase();if(b=="style"){var c=s(a.style.cssText).toLowerCase();return c.charAt(c.length-1)==";"?c:c+";"}c=a.getAttributeNode(b);if(!c)return null;if(ha(na,b)>=0)return"true";return c.specified?c.value:null}function M(a){for(a=a.parentNode;a&&a.nodeType!=1&&a.nodeType!=9&&a.nodeType!=11;)a=a.parentNode;return L(a)?a:null}function N(a,b){b=ga(String(b));return K(a,b)||O(a,b)}
+function O(a,b){var c=(a.currentStyle||a.style)[b];if(c!="inherit")return c!==undefined?c:null;return(c=M(a))?O(c,b):null}
+function pa(a){if(g(a.getBBox)=="function")return a.getBBox();var b;if((K(a,"display")||(a.currentStyle?a.currentStyle.display:null)||a.style.display)!="none")b=new B(a.offsetWidth,a.offsetHeight);else{b=a.style;var c=b.display,d=b.visibility,e=b.position;b.visibility="hidden";b.position="absolute";b.display="inline";var f;f=a.offsetWidth;a=a.offsetHeight;b.display=c;b.position=e;b.visibility=d;b=new B(f,a)}return b}
+function P(a,b){function c(f){if(N(f,"display")=="none")return false;f=M(f);return!f||c(f)}function d(f){var h=pa(f);if(h.height>0&&h.width>0)return true;if(f.innerText||f.textContent)if(qa.test(f.innerText||f.textContent))return true;return y&&ja(f.childNodes,function(X){return L(X)&&d(X)})}if(!L(a))throw Error("Argument to isShown must be of type Element");if(L(a,"TITLE"))return(C(a)?C(a).parentWindow||C(a).defaultView:window)==ea;if(L(a,"OPTION")||L(a,"OPTGROUP")){var e=E(a,function(f){return L(f,
+"SELECT")});return!!e&&P(e)}if(L(a,"MAP")){if(!a.name)return false;e=C(a);e=e.evaluate?ma('/descendant::*[@usemap = "#'+a.name+'"]',e):la(e,function(f){return L(f)&&oa(f,"usemap")=="#"+a.name});return!!e&&P(e)}if(L(a,"AREA")){e=E(a,function(f){return L(f,"MAP")});return!!e&&P(e)}if(L(a,"INPUT")&&a.type.toLowerCase()=="hidden")return false;if(N(a,"visibility")=="hidden")return false;if(!c(a))return false;if(!b&&Q(a)==0)return false;if(!d(a))return false;return true}
+function ra(a){var b=[""];R(a,b);b=x(b,s);return s(b.join("\n"))}function R(a,b){if(L(a,"BR"))b.push("");else{var c=sa(a);c&&b[b.length-1]&&b.push("");ia(a.childNodes,function(d){if(d.nodeType==3){var e=M(d);if(e){P(e);if(e&&P(e)){d=d.nodeValue.replace(ta," ");e=b.pop();var f=e.length-1;if(f>=0&&e.indexOf(" ",f)==f&&d.lastIndexOf(" ",0)==0)d=d.substr(1);b.push(e+d)}}}else L(d)&&R(d,b)});c&&b[b.length-1]&&b.push("")}}function sa(a){a=N(a,"display");return a=="block"||a=="inline-block"}
+var S="[\\s\\xa0"+String.fromCharCode(160)+"]+",ta=RegExp(S,"g"),qa=RegExp("^"+S+"$");function Q(a){var b=1,c=N(a,"opacity");if(c)b=Number(c);if(a=M(a))b*=Q(a);return b};function ua(){}
+function T(a,b,c){switch(typeof b){case "string":va(a,b,c);break;case "number":c.push(isFinite(b)&&!isNaN(b)?b:"null");break;case "boolean":c.push(b);break;case "undefined":c.push("null");break;case "object":if(b==null){c.push("null");break}if(g(b)=="array"){var d=b.length;c.push("[");var e="";for(var f=0;f<d;f++){c.push(e);T(a,b[f],c);e=","}c.push("]");break}c.push("{");d="";for(e in b)if(Object.prototype.hasOwnProperty.call(b,e)){f=b[e];if(typeof f!="function"){c.push(d);va(a,e,c);c.push(":");T(a,
+f,c);d=","}}c.push("}");break;case "function":break;default:throw Error("Unknown type: "+typeof b);}}var U={'"':'\\"',"\\":"\\\\","/":"\\/","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\u000b":"\\u000b"},wa=/\uffff/.test("\uffff")?/[\\\"\x00-\x1f\x7f-\uffff]/g:/[\\\"\x00-\x1f\x7f-\xff]/g;
+function va(a,b,c){c.push('"',b.replace(wa,function(d){if(d in U)return U[d];var e=d.charCodeAt(0),f="\\u";if(e<16)f+="000";else if(e<256)f+="00";else if(e<4096)f+="0";return U[d]=f+e.toString(16)}),'"')};function V(a){switch(g(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return x(a,V);case "object":a=a;if("tagName"in a&&"nodeType"in a&&a.nodeType==1){var b={};b.ELEMENT=xa(a);return b}if(i(a))return x(a,V);a=ca(a,function(c,d){return typeof d=="number"||j(d)});return m(a,V);default:return null}}
+function W(a,b){if(g(a)=="array")return x(a,function(c){return W(c,b)});else if(aa(a))return"ELEMENT"in a?ya(a.ELEMENT,b):m(a,function(c){return W(c,b)});return a}function za(a){a=a||document;var b=a.$wdc_;if(!b){b=a.$wdc_={};b.g=ba()}return b}function xa(a){var b=za(a.ownerDocument),c=da(b,function(d){return d==a});if(!c){c=":wdc:"+b.g++;b[c]=a}return c}
+function ya(a,b){a=decodeURIComponent(a);var c=b||document,d=za(c);if(!(a in d))throw new n(10,"Element does not exist in cache");var e=d[a];for(var f=e;f;){if(f==c.documentElement)return e;f=f.parentNode}delete d[a];throw new n(10,"Element is no longer attached to the DOM");};function Aa(a){var b=ra;a=[a];var c;try{if(j(b))b=new Function(b);var d=W(a),e=b.apply(null,d);c={status:0,value:V(e)}}catch(f){c={status:"code"in f?f.code:13,value:{message:f.message}}}b=[];T(new ua,c,b);return b.join("")}var Y="_".split("."),Z=this;!(Y[0]in Z)&&Z.execScript&&Z.execScript("var "+Y[0]);for(var $;Y.length&&($=Y.shift());)if(!Y.length&&Aa!==undefined)Z[$]=Aa;else Z=Z[$]?Z[$]:Z[$]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/get_top_left_coordinates_android.js b/core/res/res/raw/get_top_left_coordinates_android.js
new file mode 100644
index 0000000..23f96afe
--- /dev/null
+++ b/core/res/res/raw/get_top_left_coordinates_android.js
@@ -0,0 +1,18 @@
+function(){return function(){var h=this;
+function j(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(c=="[object Window]")return"object";if(c=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(c=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if(b==
+"function"&&typeof a.call=="undefined")return"object";return b}function l(a){var b=j(a);return b=="array"||b=="object"&&typeof a.length=="number"}function aa(a){a=j(a);return a=="object"||a=="array"||a=="function"}Math.floor(Math.random()*2147483648).toString(36);var ba=Date.now||function(){return+new Date};function m(a,b){function c(){}c.prototype=b.prototype;a.j=b.prototype;a.prototype=new c};function n(a){this.stack=Error().stack||"";if(a)this.message=String(a)}m(n,Error);n.prototype.name="CustomError";function ca(a,b,c){var d={};for(var f in a)if(b.call(c,a[f],f,a))d[f]=a[f];return d}function o(a,b,c){var d={};for(var f in a)d[f]=b.call(c,a[f],f,a);return d}function da(a,b,c){for(var d in a)if(b.call(c,a[d],d,a))return d};function p(a,b){n.call(this,b);this.code=a;this.name=q[a]||q[13]}m(p,n);var q,r={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},s={};for(var t in r)s[r[t]]=t;q=s;
+p.prototype.toString=function(){return"["+this.name+"] "+this.message};function ea(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a}function y(a,b){if(a<b)return-1;else if(a>b)return 1;return 0};function z(a,b){b.unshift(a);n.call(this,ea.apply(null,b));b.shift();this.k=a}m(z,n);z.prototype.name="AssertionError";function fa(a,b){if(!a){var c=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(b){d+=": "+b;var f=c}throw new z(""+d,f||[]);}return a};var A=Array.prototype,B=A.map?function(a,b,c){fa(a.length!=null);return A.map.call(a,b,c)}:function(a,b,c){var d=a.length,f=Array(d),e=typeof a=="string"?a.split(""):a;for(var g=0;g<d;g++)if(g in e)f[g]=b.call(c,e[g],g,a);return f};var C,F="",G;if(G=/WebKit\/(\S+)/){var H=G.exec(h.navigator?h.navigator.userAgent:null);F=H?H[1]:""}C=F;var ga={};var ha;function I(a,b){this.x=a!==undefined?a:0;this.y=b!==undefined?b:0}I.prototype.toString=function(){return"("+this.x+", "+this.y+")"};function J(a,b){this.width=a;this.height=b}J.prototype.toString=function(){return"("+this.width+" x "+this.height+")"};J.prototype.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};function K(a){return a?new ia(L(a)):ha||(ha=new ia)}function L(a){return a.nodeType==9?a:a.ownerDocument||a.document}function ia(a){this.e=a||h.document||document}function ja(a){a=a.e.body;return new I(a.scrollLeft,a.scrollTop)};var M="StopIteration"in h?h.StopIteration:Error("StopIteration");function ka(){}ka.prototype.next=function(){throw M;};function N(a,b,c,d,f){this.a=!!b;a&&O(this,a,d);this.d=f!=undefined?f:this.c||0;if(this.a)this.d*=-1;this.h=!c}m(N,ka);N.prototype.b=null;N.prototype.c=0;N.prototype.g=false;function O(a,b,c,d){if(a.b=b)a.c=typeof c=="number"?c:a.b.nodeType!=1?0:a.a?-1:1;if(typeof d=="number")a.d=d}
+N.prototype.next=function(){var a;if(this.g){if(!this.b||this.h&&this.d==0)throw M;a=this.b;var b=this.a?-1:1;if(this.c==b){var c=this.a?a.lastChild:a.firstChild;c?O(this,c):O(this,a,b*-1)}else(c=this.a?a.previousSibling:a.nextSibling)?O(this,c):O(this,a.parentNode,b*-1);this.d+=this.c*(this.a?-1:1)}else this.g=true;a=this.b;if(!this.b)throw M;return a};
+N.prototype.splice=function(){var a=this.b,b=this.a?1:-1;if(this.c==b){this.c=b*-1;this.d+=this.c*(this.a?-1:1)}this.a=!this.a;N.prototype.next.call(this);this.a=!this.a;b=l(arguments[0])?arguments[0]:arguments;for(var c=b.length-1;c>=0;c--)a.parentNode&&a.parentNode.insertBefore(b[c],a.nextSibling);a&&a.parentNode&&a.parentNode.removeChild(a)};function P(a,b,c,d){N.call(this,a,b,c,null,d)}m(P,N);P.prototype.next=function(){do P.j.next.call(this);while(this.c==-1);return this.b};function la(a,b,c,d){this.top=a;this.right=b;this.bottom=c;this.left=d}la.prototype.toString=function(){return"("+this.top+"t, "+this.right+"r, "+this.bottom+"b, "+this.left+"l)"};function Q(a,b,c,d){this.left=a;this.top=b;this.width=c;this.height=d}Q.prototype.toString=function(){return"("+this.left+", "+this.top+" - "+this.width+"w x "+this.height+"h)"};function R(a,b){var c=L(a);if(c.defaultView&&c.defaultView.getComputedStyle)if(c=c.defaultView.getComputedStyle(a,null))return c[b]||c.getPropertyValue(b);return""}function S(a,b){return R(a,b)||(a.currentStyle?a.currentStyle[b]:null)||a.style[b]}
+function ma(a){var b=L(a),c=S(a,"position"),d=c=="fixed"||c=="absolute";for(a=a.parentNode;a&&a!=b;a=a.parentNode){c=S(a,"position");d=d&&c=="static"&&a!=b.documentElement&&a!=b.body;if(!d&&(a.scrollWidth>a.clientWidth||a.scrollHeight>a.clientHeight||c=="fixed"||c=="absolute"))return a}return null}
+function T(a){var b=L(a),c=S(a,"position"),d=new I(0,0),f=(b?b.nodeType==9?b:L(b):document).documentElement;if(a==f)return d;if(a.getBoundingClientRect){a=a.getBoundingClientRect();b=ja(K(b));d.x=a.left+b.x;d.y=a.top+b.y}else if(b.getBoxObjectFor){a=b.getBoxObjectFor(a);b=b.getBoxObjectFor(f);d.x=a.screenX-b.screenX;d.y=a.screenY-b.screenY}else{var e=a;do{d.x+=e.offsetLeft;d.y+=e.offsetTop;if(e!=a){d.x+=e.clientLeft||0;d.y+=e.clientTop||0}if(S(e,"position")=="fixed"){d.x+=b.body.scrollLeft;d.y+=b.body.scrollTop;
+break}e=e.offsetParent}while(e&&e!=a);if(c=="absolute")d.y-=b.body.offsetTop;for(e=a;(e=ma(e))&&e!=b.body&&e!=f;){d.x-=e.scrollLeft;d.y-=e.scrollTop}}return d};String.fromCharCode(160);function na(a,b){b.scrollLeft+=Math.min(a.left,Math.max(a.left-a.width,0));b.scrollTop+=Math.min(a.top,Math.max(a.top-a.height,0))}
+function oa(a,b){var c;c=b?new Q(b.left,b.top,b.width,b.height):new Q(0,0,a.offsetWidth,a.offsetHeight);var d=L(a);for(var f=a.parentNode;f&&f!=d.body&&f!=d.documentElement;){var e=c,g=f,u=T(a),v=T(g),i=void 0;i=void 0;var k=void 0,D=void 0,E=void 0;E=R(g,"borderLeftWidth");D=R(g,"borderRightWidth");k=R(g,"borderTopWidth");i=R(g,"borderBottomWidth");i=new la(parseFloat(k),parseFloat(D),parseFloat(i),parseFloat(E));na(new Q(u.x+e.left-v.x-i.left,u.y+e.top-v.y-i.top,g.clientWidth-e.width,g.clientHeight-
+e.height),g);f=f.parentNode}f=T(a);e=K(d);e=(e.e.parentWindow||e.e.defaultView||window).document;if(!ga["500"]){g=0;u=String(C).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split(".");v=String("500").replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split(".");i=Math.max(u.length,v.length);for(k=0;g==0&&k<i;k++){D=u[k]||"";E=v[k]||"";var sa=RegExp("(\\d*)(\\D*)","g"),ta=RegExp("(\\d*)(\\D*)","g");do{var w=sa.exec(D)||["","",""],x=ta.exec(E)||["","",""];if(w[0].length==0&&x[0].length==0)break;g=y(w[1].length==0?0:parseInt(w[1],
+10),x[1].length==0?0:parseInt(x[1],10))||y(w[2].length==0,x[2].length==0)||y(w[2],x[2])}while(g==0)}ga["500"]=g>=0}e=e.compatMode=="CSS1Compat"?e.documentElement:e.body;e=new J(e.clientWidth,e.clientHeight);na(new Q(f.x+c.left-d.body.scrollLeft,f.y+c.top-d.body.scrollTop,e.width-c.width,e.height-c.height),d.body);d=new I;if(a.nodeType==1)if(a.getBoundingClientRect){f=a.getBoundingClientRect();d.x=f.left;d.y=f.top}else{f=ja(K(a));e=T(a);d.x=e.x-f.x;d.y=e.y-f.y}else{f=j(a.f)=="function";e=a;if(a.targetTouches)e=
+a.targetTouches[0];else if(f&&a.f().targetTouches)e=a.f().targetTouches[0];d.x=e.clientX;d.y=e.clientY}return new I(d.x+c.left,d.y+c.top)};function pa(){}
+function U(a,b,c){switch(typeof b){case "string":qa(a,b,c);break;case "number":c.push(isFinite(b)&&!isNaN(b)?b:"null");break;case "boolean":c.push(b);break;case "undefined":c.push("null");break;case "object":if(b==null){c.push("null");break}if(j(b)=="array"){var d=b.length;c.push("[");var f="";for(var e=0;e<d;e++){c.push(f);U(a,b[e],c);f=","}c.push("]");break}c.push("{");d="";for(f in b)if(Object.prototype.hasOwnProperty.call(b,f)){e=b[f];if(typeof e!="function"){c.push(d);qa(a,f,c);c.push(":");U(a,
+e,c);d=","}}c.push("}");break;case "function":break;default:throw Error("Unknown type: "+typeof b);}}var V={'"':'\\"',"\\":"\\\\","/":"\\/","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\u000b":"\\u000b"},ra=/\uffff/.test("\uffff")?/[\\\"\x00-\x1f\x7f-\uffff]/g:/[\\\"\x00-\x1f\x7f-\xff]/g;
+function qa(a,b,c){c.push('"',b.replace(ra,function(d){if(d in V)return V[d];var f=d.charCodeAt(0),e="\\u";if(f<16)e+="000";else if(f<256)e+="00";else if(f<4096)e+="0";return V[d]=e+f.toString(16)}),'"')};function W(a){switch(j(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return B(a,W);case "object":a=a;if("tagName"in a&&"nodeType"in a&&a.nodeType==1){var b={};b.ELEMENT=ua(a);return b}if(l(a))return B(a,W);a=ca(a,function(c,d){return typeof d=="number"||typeof d=="string"});return o(a,W);default:return null}}
+function X(a,b){if(j(a)=="array")return B(a,function(c){return X(c,b)});else if(aa(a))return"ELEMENT"in a?va(a.ELEMENT,b):o(a,function(c){return X(c,b)});return a}function wa(a){a=a||document;var b=a.$wdc_;if(!b){b=a.$wdc_={};b.i=ba()}return b}function ua(a){var b=wa(a.ownerDocument),c=da(b,function(d){return d==a});if(!c){c=":wdc:"+b.i++;b[c]=a}return c}
+function va(a,b){a=decodeURIComponent(a);var c=b||document,d=wa(c);if(!(a in d))throw new p(10,"Element does not exist in cache");var f=d[a];for(var e=f;e;){if(e==c.documentElement)return f;e=e.parentNode}delete d[a];throw new p(10,"Element is no longer attached to the DOM");};function xa(a){var b=oa;a=[a];var c;try{if(typeof b=="string")b=new Function(b);var d=X(a),f=b.apply(null,d);c={status:0,value:W(f)}}catch(e){c={status:"code"in e?e.code:13,value:{message:e.message}}}b=[];U(new pa,c,b);return b.join("")}var Y="_".split("."),Z=h;!(Y[0]in Z)&&Z.execScript&&Z.execScript("var "+Y[0]);for(var $;Y.length&&($=Y.shift());)if(!Y.length&&xa!==undefined)Z[$]=xa;else Z=Z[$]?Z[$]:Z[$]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/get_value_of_css_property_android.js b/core/res/res/raw/get_value_of_css_property_android.js
new file mode 100644
index 0000000..c156dec
--- /dev/null
+++ b/core/res/res/raw/get_value_of_css_property_android.js
@@ -0,0 +1,10 @@
+function(){return function(){function g(a){var c=typeof a;if(c=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return c;var b=Object.prototype.toString.call(a);if(b=="[object Window]")return"object";if(b=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(b=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
+else if(c=="function"&&typeof a.call=="undefined")return"object";return c}function i(a){var c=g(a);return c=="array"||c=="object"&&typeof a.length=="number"}function j(a){a=g(a);return a=="object"||a=="array"||a=="function"}Math.floor(Math.random()*2147483648).toString(36);var k=Date.now||function(){return+new Date};function l(a,c){function b(){}b.prototype=c.prototype;a.h=c.prototype;a.prototype=new b};function m(a){this.stack=Error().stack||"";if(a)this.message=String(a)}l(m,Error);m.prototype.name="CustomError";function n(a,c,b){var d={};for(var e in a)if(c.call(b,a[e],e,a))d[e]=a[e];return d}function o(a,c,b){var d={};for(var e in a)d[e]=c.call(b,a[e],e,a);return d}function p(a,c,b){for(var d in a)if(c.call(b,a[d],d,a))return d};function q(a,c){m.call(this,c);this.code=a;this.name=r[a]||r[13]}l(q,m);var r,s={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},t={};for(var u in s)t[s[u]]=u;r=t;
+q.prototype.toString=function(){return"["+this.name+"] "+this.message};function v(a){for(var c=1;c<arguments.length;c++){var b=String(arguments[c]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,b)}return a};function w(a,c){c.unshift(a);m.call(this,v.apply(null,c));c.shift();this.i=a}l(w,m);w.prototype.name="AssertionError";function x(a,c){if(!a){var b=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(c){d+=": "+c;var e=b}throw new w(""+d,e||[]);}return a};var y=Array.prototype,A=y.map?function(a,c,b){x(a.length!=null);return y.map.call(a,c,b)}:function(a,c,b){var d=a.length,e=Array(d),f=typeof a=="string"?a.split(""):a;for(var h=0;h<d;h++)if(h in f)e[h]=c.call(b,f[h],h,a);return e};var B="",C;if(C=/WebKit\/(\S+)/){var D=C.exec(this.navigator?this.navigator.userAgent:null);B=D?D[1]:""};var E="StopIteration"in this?this.StopIteration:Error("StopIteration");function F(){}F.prototype.next=function(){throw E;};function G(a,c,b,d,e){this.a=!!c;a&&H(this,a,d);this.d=e!=undefined?e:this.c||0;if(this.a)this.d*=-1;this.f=!b}l(G,F);G.prototype.b=null;G.prototype.c=0;G.prototype.e=false;function H(a,c,b,d){if(a.b=c)a.c=typeof b=="number"?b:a.b.nodeType!=1?0:a.a?-1:1;if(typeof d=="number")a.d=d}
+G.prototype.next=function(){var a;if(this.e){if(!this.b||this.f&&this.d==0)throw E;a=this.b;var c=this.a?-1:1;if(this.c==c){var b=this.a?a.lastChild:a.firstChild;b?H(this,b):H(this,a,c*-1)}else(b=this.a?a.previousSibling:a.nextSibling)?H(this,b):H(this,a.parentNode,c*-1);this.d+=this.c*(this.a?-1:1)}else this.e=true;a=this.b;if(!this.b)throw E;return a};
+G.prototype.splice=function(){var a=this.b,c=this.a?1:-1;if(this.c==c){this.c=c*-1;this.d+=this.c*(this.a?-1:1)}this.a=!this.a;G.prototype.next.call(this);this.a=!this.a;c=i(arguments[0])?arguments[0]:arguments;for(var b=c.length-1;b>=0;b--)a.parentNode&&a.parentNode.insertBefore(c[b],a.nextSibling);a&&a.parentNode&&a.parentNode.removeChild(a)};function I(a,c,b,d){G.call(this,a,c,b,null,d)}l(I,G);I.prototype.next=function(){do I.h.next.call(this);while(this.c==-1);return this.b};function J(a,c){var b=(a.currentStyle||a.style)[c];if(b!="inherit")return b!==undefined?b:null;for(b=a.parentNode;b&&b.nodeType!=1&&b.nodeType!=9&&b.nodeType!=11;)b=b.parentNode;return(b=b&&b.nodeType==1&&1?b:null)?J(b,c):null}String.fromCharCode(160);function K(){}
+function L(a,c,b){switch(typeof c){case "string":M(a,c,b);break;case "number":b.push(isFinite(c)&&!isNaN(c)?c:"null");break;case "boolean":b.push(c);break;case "undefined":b.push("null");break;case "object":if(c==null){b.push("null");break}if(g(c)=="array"){var d=c.length;b.push("[");var e="";for(var f=0;f<d;f++){b.push(e);L(a,c[f],b);e=","}b.push("]");break}b.push("{");d="";for(e in c)if(Object.prototype.hasOwnProperty.call(c,e)){f=c[e];if(typeof f!="function"){b.push(d);M(a,e,b);b.push(":");L(a,
+f,b);d=","}}b.push("}");break;case "function":break;default:throw Error("Unknown type: "+typeof c);}}var N={'"':'\\"',"\\":"\\\\","/":"\\/","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\u000b":"\\u000b"},O=/\uffff/.test("\uffff")?/[\\\"\x00-\x1f\x7f-\uffff]/g:/[\\\"\x00-\x1f\x7f-\xff]/g;
+function M(a,c,b){b.push('"',c.replace(O,function(d){if(d in N)return N[d];var e=d.charCodeAt(0),f="\\u";if(e<16)f+="000";else if(e<256)f+="00";else if(e<4096)f+="0";return N[d]=f+e.toString(16)}),'"')};function P(a){switch(g(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return A(a,P);case "object":a=a;if("tagName"in a&&"nodeType"in a&&a.nodeType==1){var c={};c.ELEMENT=Q(a);return c}if(i(a))return A(a,P);a=n(a,function(b,d){return typeof d=="number"||typeof d=="string"});return o(a,P);default:return null}}
+function R(a,c){if(g(a)=="array")return A(a,function(b){return R(b,c)});else if(j(a))return"ELEMENT"in a?S(a.ELEMENT,c):o(a,function(b){return R(b,c)});return a}function T(a){a=a||document;var c=a.$wdc_;if(!c){c=a.$wdc_={};c.g=k()}return c}function Q(a){var c=T(a.ownerDocument),b=p(c,function(d){return d==a});if(!b){b=":wdc:"+c.g++;c[b]=a}return b}
+function S(a,c){a=decodeURIComponent(a);var b=c||document,d=T(b);if(!(a in d))throw new q(10,"Element does not exist in cache");var e=d[a];for(var f=e;f;){if(f==b.documentElement)return e;f=f.parentNode}delete d[a];throw new q(10,"Element is no longer attached to the DOM");};function U(a,c){var b=J,d=[a,c],e;try{if(typeof b=="string")b=new Function(b);var f=R(d),h=b.apply(null,f);e={status:0,value:P(h)}}catch(z){e={status:"code"in z?z.code:13,value:{message:z.message}}}b=[];L(new K,e,b);return b.join("")}var V="_".split("."),W=this;!(V[0]in W)&&W.execScript&&W.execScript("var "+V[0]);for(var X;V.length&&(X=V.shift());)if(!V.length&&U!==undefined)W[X]=U;else W=W[X]?W[X]:W[X]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/is_enabled_android.js b/core/res/res/raw/is_enabled_android.js
new file mode 100644
index 0000000..3f799f3
--- /dev/null
+++ b/core/res/res/raw/is_enabled_android.js
@@ -0,0 +1,12 @@
+function(){return function(){function g(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(c=="[object Window]")return"object";if(c=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(c=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
+else if(b=="function"&&typeof a.call=="undefined")return"object";return b}function h(a){var b=g(a);return b=="array"||b=="object"&&typeof a.length=="number"}function i(a){return typeof a=="string"}function k(a){a=g(a);return a=="object"||a=="array"||a=="function"}Math.floor(Math.random()*2147483648).toString(36);var l=Date.now||function(){return+new Date};function m(a,b){function c(){}c.prototype=b.prototype;a.h=b.prototype;a.prototype=new c};function n(a){this.stack=Error().stack||"";if(a)this.message=String(a)}m(n,Error);n.prototype.name="CustomError";function o(a,b,c){var d={};for(var e in a)if(b.call(c,a[e],e,a))d[e]=a[e];return d}function p(a,b,c){var d={};for(var e in a)d[e]=b.call(c,a[e],e,a);return d}function q(a,b,c){for(var d in a)if(b.call(c,a[d],d,a))return d};function r(a,b){n.call(this,b);this.code=a;this.name=s[a]||s[13]}m(r,n);var s,t={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},u={};for(var v in t)u[t[v]]=v;s=u;
+r.prototype.toString=function(){return"["+this.name+"] "+this.message};function w(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a};function x(a,b){b.unshift(a);n.call(this,w.apply(null,b));b.shift();this.i=a}m(x,n);x.prototype.name="AssertionError";function y(a,b){if(!a){var c=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(b){d+=": "+b;var e=c}throw new x(""+d,e||[]);}return a};var z=Array.prototype,A=z.indexOf?function(a,b,c){y(a.length!=null);return z.indexOf.call(a,b,c)}:function(a,b,c){c=c==null?0:c<0?Math.max(0,a.length+c):c;if(i(a)){if(!i(b)||b.length!=1)return-1;return a.indexOf(b,c)}for(c=c;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},B=z.map?function(a,b,c){y(a.length!=null);return z.map.call(a,b,c)}:function(a,b,c){var d=a.length,e=Array(d),f=i(a)?a.split(""):a;for(var j=0;j<d;j++)if(j in f)e[j]=b.call(c,f[j],j,a);return e};var C="",D;if(D=/WebKit\/(\S+)/){var E=D.exec(this.navigator?this.navigator.userAgent:null);C=E?E[1]:""};var F="StopIteration"in this?this.StopIteration:Error("StopIteration");function G(){}G.prototype.next=function(){throw F;};function H(a,b,c,d,e){this.a=!!b;a&&I(this,a,d);this.d=e!=undefined?e:this.c||0;if(this.a)this.d*=-1;this.f=!c}m(H,G);H.prototype.b=null;H.prototype.c=0;H.prototype.e=false;function I(a,b,c,d){if(a.b=b)a.c=typeof c=="number"?c:a.b.nodeType!=1?0:a.a?-1:1;if(typeof d=="number")a.d=d}
+H.prototype.next=function(){var a;if(this.e){if(!this.b||this.f&&this.d==0)throw F;a=this.b;var b=this.a?-1:1;if(this.c==b){var c=this.a?a.lastChild:a.firstChild;c?I(this,c):I(this,a,b*-1)}else(c=this.a?a.previousSibling:a.nextSibling)?I(this,c):I(this,a.parentNode,b*-1);this.d+=this.c*(this.a?-1:1)}else this.e=true;a=this.b;if(!this.b)throw F;return a};
+H.prototype.splice=function(){var a=this.b,b=this.a?1:-1;if(this.c==b){this.c=b*-1;this.d+=this.c*(this.a?-1:1)}this.a=!this.a;H.prototype.next.call(this);this.a=!this.a;b=h(arguments[0])?arguments[0]:arguments;for(var c=b.length-1;c>=0;c--)a.parentNode&&a.parentNode.insertBefore(b[c],a.nextSibling);a&&a.parentNode&&a.parentNode.removeChild(a)};function J(a,b,c,d){H.call(this,a,b,c,null,d)}m(J,H);J.prototype.next=function(){do J.h.next.call(this);while(this.c==-1);return this.b};var K=["async","autofocus","autoplay","checked","compact","complete","controls","declare","defaultchecked","defaultselected","defer","disabled","draggable","ended","formnovalidate","hidden","indeterminate","iscontenteditable","ismap","itemscope","loop","multiple","muted","nohref","noresize","noshade","novalidate","nowrap","open","paused","pubdate","readonly","required","reversed","scoped","seamless","seeking","selected","spellcheck","truespeed","willvalidate"];
+function L(a,b){if(8==a.nodeType)return null;b=b.toLowerCase();if(b=="style"){var c=a.style.cssText.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").toLowerCase();return c.charAt(c.length-1)==";"?c:c+";"}c=a.getAttributeNode(b);if(!c)return null;if(A(K,b)>=0)return"true";return c.specified?c.value:null}var M=["BUTTON","INPUT","OPTGROUP","OPTION","SELECT","TEXTAREA"];
+function N(a){var b=a.tagName.toUpperCase();if(!(A(M,b)>=0))return true;if(L(a,"disabled"))return false;if(a.parentNode&&a.parentNode.nodeType==1&&"OPTGROUP"==b||"OPTION"==b)return N(a.parentNode);return true}String.fromCharCode(160);function O(){}
+function P(a,b,c){switch(typeof b){case "string":Q(a,b,c);break;case "number":c.push(isFinite(b)&&!isNaN(b)?b:"null");break;case "boolean":c.push(b);break;case "undefined":c.push("null");break;case "object":if(b==null){c.push("null");break}if(g(b)=="array"){var d=b.length;c.push("[");var e="";for(var f=0;f<d;f++){c.push(e);P(a,b[f],c);e=","}c.push("]");break}c.push("{");d="";for(e in b)if(Object.prototype.hasOwnProperty.call(b,e)){f=b[e];if(typeof f!="function"){c.push(d);Q(a,e,c);c.push(":");P(a,
+f,c);d=","}}c.push("}");break;case "function":break;default:throw Error("Unknown type: "+typeof b);}}var R={'"':'\\"',"\\":"\\\\","/":"\\/","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\u000b":"\\u000b"},S=/\uffff/.test("\uffff")?/[\\\"\x00-\x1f\x7f-\uffff]/g:/[\\\"\x00-\x1f\x7f-\xff]/g;
+function Q(a,b,c){c.push('"',b.replace(S,function(d){if(d in R)return R[d];var e=d.charCodeAt(0),f="\\u";if(e<16)f+="000";else if(e<256)f+="00";else if(e<4096)f+="0";return R[d]=f+e.toString(16)}),'"')};function T(a){switch(g(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return B(a,T);case "object":a=a;if("tagName"in a&&"nodeType"in a&&a.nodeType==1){var b={};b.ELEMENT=U(a);return b}if(h(a))return B(a,T);a=o(a,function(c,d){return typeof d=="number"||i(d)});return p(a,T);default:return null}}
+function V(a,b){if(g(a)=="array")return B(a,function(c){return V(c,b)});else if(k(a))return"ELEMENT"in a?aa(a.ELEMENT,b):p(a,function(c){return V(c,b)});return a}function W(a){a=a||document;var b=a.$wdc_;if(!b){b=a.$wdc_={};b.g=l()}return b}function U(a){var b=W(a.ownerDocument),c=q(b,function(d){return d==a});if(!c){c=":wdc:"+b.g++;b[c]=a}return c}
+function aa(a,b){a=decodeURIComponent(a);var c=b||document,d=W(c);if(!(a in d))throw new r(10,"Element does not exist in cache");var e=d[a];for(var f=e;f;){if(f==c.documentElement)return e;f=f.parentNode}delete d[a];throw new r(10,"Element is no longer attached to the DOM");};function X(a){var b=N;a=[a];var c;try{if(i(b))b=new Function(b);var d=V(a),e=b.apply(null,d);c={status:0,value:T(e)}}catch(f){c={status:"code"in f?f.code:13,value:{message:f.message}}}b=[];P(new O,c,b);return b.join("")}var Y="_".split("."),Z=this;!(Y[0]in Z)&&Z.execScript&&Z.execScript("var "+Y[0]);for(var $;Y.length&&($=Y.shift());)if(!Y.length&&X!==undefined)Z[$]=X;else Z=Z[$]?Z[$]:Z[$]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/is_selected_android.js b/core/res/res/raw/is_selected_android.js
new file mode 100644
index 0000000..0cc53b2
--- /dev/null
+++ b/core/res/res/raw/is_selected_android.js
@@ -0,0 +1,10 @@
+function(){return function(){function g(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(c=="[object Window]")return"object";if(c=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(c=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
+else if(b=="function"&&typeof a.call=="undefined")return"object";return b}function h(a){var b=g(a);return b=="array"||b=="object"&&typeof a.length=="number"}function i(a){return typeof a=="string"}function k(a){a=g(a);return a=="object"||a=="array"||a=="function"}Math.floor(Math.random()*2147483648).toString(36);var l=Date.now||function(){return+new Date};function m(a,b){function c(){}c.prototype=b.prototype;a.h=b.prototype;a.prototype=new c};function n(a){this.stack=Error().stack||"";if(a)this.message=String(a)}m(n,Error);n.prototype.name="CustomError";function o(a,b,c){var d={};for(var e in a)if(b.call(c,a[e],e,a))d[e]=a[e];return d}function p(a,b,c){var d={};for(var e in a)d[e]=b.call(c,a[e],e,a);return d}function q(a,b,c){for(var d in a)if(b.call(c,a[d],d,a))return d};function r(a,b){n.call(this,b);this.code=a;this.name=s[a]||s[13]}m(r,n);var s,t={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},u={};for(var v in t)u[t[v]]=v;s=u;
+r.prototype.toString=function(){return"["+this.name+"] "+this.message};function w(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a};function x(a,b){b.unshift(a);n.call(this,w.apply(null,b));b.shift();this.i=a}m(x,n);x.prototype.name="AssertionError";function y(a,b){if(!a){var c=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(b){d+=": "+b;var e=c}throw new x(""+d,e||[]);}return a};var z=Array.prototype,A=z.indexOf?function(a,b,c){y(a.length!=null);return z.indexOf.call(a,b,c)}:function(a,b,c){c=c==null?0:c<0?Math.max(0,a.length+c):c;if(i(a)){if(!i(b)||b.length!=1)return-1;return a.indexOf(b,c)}for(c=c;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},B=z.map?function(a,b,c){y(a.length!=null);return z.map.call(a,b,c)}:function(a,b,c){var d=a.length,e=Array(d),f=i(a)?a.split(""):a;for(var j=0;j<d;j++)if(j in f)e[j]=b.call(c,f[j],j,a);return e};var C="",D;if(D=/WebKit\/(\S+)/){var E=D.exec(this.navigator?this.navigator.userAgent:null);C=E?E[1]:""};var F="StopIteration"in this?this.StopIteration:Error("StopIteration");function G(){}G.prototype.next=function(){throw F;};function H(a,b,c,d,e){this.a=!!b;a&&I(this,a,d);this.d=e!=undefined?e:this.c||0;if(this.a)this.d*=-1;this.f=!c}m(H,G);H.prototype.b=null;H.prototype.c=0;H.prototype.e=false;function I(a,b,c,d){if(a.b=b)a.c=typeof c=="number"?c:a.b.nodeType!=1?0:a.a?-1:1;if(typeof d=="number")a.d=d}
+H.prototype.next=function(){var a;if(this.e){if(!this.b||this.f&&this.d==0)throw F;a=this.b;var b=this.a?-1:1;if(this.c==b){var c=this.a?a.lastChild:a.firstChild;c?I(this,c):I(this,a,b*-1)}else(c=this.a?a.previousSibling:a.nextSibling)?I(this,c):I(this,a.parentNode,b*-1);this.d+=this.c*(this.a?-1:1)}else this.e=true;a=this.b;if(!this.b)throw F;return a};
+H.prototype.splice=function(){var a=this.b,b=this.a?1:-1;if(this.c==b){this.c=b*-1;this.d+=this.c*(this.a?-1:1)}this.a=!this.a;H.prototype.next.call(this);this.a=!this.a;b=h(arguments[0])?arguments[0]:arguments;for(var c=b.length-1;c>=0;c--)a.parentNode&&a.parentNode.insertBefore(b[c],a.nextSibling);a&&a.parentNode&&a.parentNode.removeChild(a)};function J(a,b,c,d){H.call(this,a,b,c,null,d)}m(J,H);J.prototype.next=function(){do J.h.next.call(this);while(this.c==-1);return this.b};var K={"class":"className",readonly:"readOnly"},L=["checked","disabled","draggable","hidden"];String.fromCharCode(160);function M(a){var b;if(a&&a.nodeType==1&&a.tagName.toUpperCase()=="OPTION")b=true;else if(a&&a.nodeType==1&&a.tagName.toUpperCase()=="INPUT"){b=a.type.toLowerCase();b=b=="checkbox"||b=="radio"}else b=false;if(!b)throw new r(15,"Element is not selectable");b="selected";var c=a.type&&a.type.toLowerCase();if("checkbox"==c||"radio"==c)b="checked";b=K[b]||b;a=a[b];a=a===undefined&&A(L,b)>=0?false:a;return!!a};function N(){}
+function O(a,b,c){switch(typeof b){case "string":P(a,b,c);break;case "number":c.push(isFinite(b)&&!isNaN(b)?b:"null");break;case "boolean":c.push(b);break;case "undefined":c.push("null");break;case "object":if(b==null){c.push("null");break}if(g(b)=="array"){var d=b.length;c.push("[");var e="";for(var f=0;f<d;f++){c.push(e);O(a,b[f],c);e=","}c.push("]");break}c.push("{");d="";for(e in b)if(Object.prototype.hasOwnProperty.call(b,e)){f=b[e];if(typeof f!="function"){c.push(d);P(a,e,c);c.push(":");O(a,
+f,c);d=","}}c.push("}");break;case "function":break;default:throw Error("Unknown type: "+typeof b);}}var Q={'"':'\\"',"\\":"\\\\","/":"\\/","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\u000b":"\\u000b"},R=/\uffff/.test("\uffff")?/[\\\"\x00-\x1f\x7f-\uffff]/g:/[\\\"\x00-\x1f\x7f-\xff]/g;
+function P(a,b,c){c.push('"',b.replace(R,function(d){if(d in Q)return Q[d];var e=d.charCodeAt(0),f="\\u";if(e<16)f+="000";else if(e<256)f+="00";else if(e<4096)f+="0";return Q[d]=f+e.toString(16)}),'"')};function S(a){switch(g(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return B(a,S);case "object":a=a;if("tagName"in a&&"nodeType"in a&&a.nodeType==1){var b={};b.ELEMENT=T(a);return b}if(h(a))return B(a,S);a=o(a,function(c,d){return typeof d=="number"||i(d)});return p(a,S);default:return null}}
+function U(a,b){if(g(a)=="array")return B(a,function(c){return U(c,b)});else if(k(a))return"ELEMENT"in a?V(a.ELEMENT,b):p(a,function(c){return U(c,b)});return a}function W(a){a=a||document;var b=a.$wdc_;if(!b){b=a.$wdc_={};b.g=l()}return b}function T(a){var b=W(a.ownerDocument),c=q(b,function(d){return d==a});if(!c){c=":wdc:"+b.g++;b[c]=a}return c}
+function V(a,b){a=decodeURIComponent(a);var c=b||document,d=W(c);if(!(a in d))throw new r(10,"Element does not exist in cache");var e=d[a];for(var f=e;f;){if(f==c.documentElement)return e;f=f.parentNode}delete d[a];throw new r(10,"Element is no longer attached to the DOM");};function X(a){var b=M;a=[a];var c;try{if(i(b))b=new Function(b);var d=U(a),e=b.apply(null,d);c={status:0,value:S(e)}}catch(f){c={status:"code"in f?f.code:13,value:{message:f.message}}}b=[];O(new N,c,b);return b.join("")}var Y="_".split("."),Z=this;!(Y[0]in Z)&&Z.execScript&&Z.execScript("var "+Y[0]);for(var $;Y.length&&($=Y.shift());)if(!Y.length&&X!==undefined)Z[$]=X;else Z=Z[$]?Z[$]:Z[$]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/set_selected_android.js b/core/res/res/raw/set_selected_android.js
new file mode 100644
index 0000000..51774e0
--- /dev/null
+++ b/core/res/res/raw/set_selected_android.js
@@ -0,0 +1,26 @@
+function(){return function(){var l=this;
+function m(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(c=="[object Window]")return"object";if(c=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(c=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if(b==
+"function"&&typeof a.call=="undefined")return"object";return b}function o(a){var b=m(a);return b=="array"||b=="object"&&typeof a.length=="number"}function p(a){return typeof a=="string"}function q(a){a=m(a);return a=="object"||a=="array"||a=="function"}Math.floor(Math.random()*2147483648).toString(36);var aa=Date.now||function(){return+new Date};function r(a,b){function c(){}c.prototype=b.prototype;a.j=b.prototype;a.prototype=new c};function s(a){this.stack=Error().stack||"";if(a)this.message=String(a)}r(s,Error);s.prototype.name="CustomError";function ba(a,b,c){var d={};for(var f in a)if(b.call(c,a[f],f,a))d[f]=a[f];return d}function u(a,b,c){var d={};for(var f in a)d[f]=b.call(c,a[f],f,a);return d}function ca(a,b,c){for(var d in a)if(b.call(c,a[d],d,a))return d};function v(a,b){s.call(this,b);this.code=a;this.name=w[a]||w[13]}r(v,s);var w,da={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},ea={};for(var fa in da)ea[da[fa]]=fa;w=ea;
+v.prototype.toString=function(){return"["+this.name+"] "+this.message};var ga=window;function ha(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a}var ia={};function ja(a){return ia[a]||(ia[a]=String(a).replace(/\-([a-z])/g,function(b,c){return c.toUpperCase()}))};function x(a,b){b.unshift(a);s.call(this,ha.apply(null,b));b.shift();this.m=a}r(x,s);x.prototype.name="AssertionError";function y(a,b){if(!a){var c=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(b){d+=": "+b;var f=c}throw new x(""+d,f||[]);}return a};var z=Array.prototype,A=z.indexOf?function(a,b,c){y(a.length!=null);return z.indexOf.call(a,b,c)}:function(a,b,c){c=c==null?0:c<0?Math.max(0,a.length+c):c;if(p(a)){if(!p(b)||b.length!=1)return-1;return a.indexOf(b,c)}for(c=c;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},B=z.map?function(a,b,c){y(a.length!=null);return z.map.call(a,b,c)}:function(a,b,c){var d=a.length,f=Array(d),e=p(a)?a.split(""):a;for(var g=0;g<d;g++)if(g in e)f[g]=b.call(c,e[g],g,a);return f},ka=z.some?function(a,b,c){y(a.length!=
+null);return z.some.call(a,b,c)}:function(a,b,c){var d=a.length,f=p(a)?a.split(""):a;for(var e=0;e<d;e++)if(e in f&&b.call(c,f[e],e,a))return true;return false};var C=true,la="",D;if(C)D=/WebKit\/(\S+)/;if(D){var ma=D.exec(l.navigator?l.navigator.userAgent:null);la=ma?ma[1]:""};var E;function F(a,b){this.x=a!==undefined?a:0;this.y=b!==undefined?b:0}F.prototype.toString=function(){return"("+this.x+", "+this.y+")"};function G(a,b){this.width=a;this.height=b}G.prototype.toString=function(){return"("+this.width+" x "+this.height+")"};G.prototype.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};function H(a){return a.nodeType==9?a:a.ownerDocument||a.document}function na(a,b){var c=[];return oa(a,b,c,true)?c[0]:undefined}function oa(a,b,c,d){if(a!=null){var f=0;for(var e;e=a.childNodes[f];f++){if(b(e)){c.push(e);if(d)return true}if(oa(e,b,c,d))return true}}return false}function I(a,b,c,d){if(!c)a=a.parentNode;c=d==null;for(var f=0;a&&(c||f<=d);){if(b(a))return a;a=a.parentNode;f++}return null}function J(a){this.e=a||l.document||document}
+function pa(a){a=!C&&a.e.compatMode=="CSS1Compat"?a.e.documentElement:a.e.body;return new F(a.scrollLeft,a.scrollTop)};function qa(a,b){try{var c;if(typeof b.selectSingleNode!="undefined"){var d=H(b);typeof d.setProperty!="undefined"&&d.setProperty("SelectionLanguage","XPath");c=b.selectSingleNode(a)}else if(document.implementation.hasFeature("XPath","3.0")){d=H(b);var f=d.createNSResolver(d.documentElement);c=d.evaluate(a,b,f,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue}else c=null}catch(e){return null}if(!c)return null;if(c.nodeType!=1)throw Error("Returned node is not an element: "+a);return c};var K="StopIteration"in l?l.StopIteration:Error("StopIteration");function ra(){}ra.prototype.next=function(){throw K;};function L(a,b,c,d,f){this.a=!!b;a&&M(this,a,d);this.d=f!=undefined?f:this.c||0;if(this.a)this.d*=-1;this.h=!c}r(L,ra);L.prototype.b=null;L.prototype.c=0;L.prototype.g=false;function M(a,b,c,d){if(a.b=b)a.c=typeof c=="number"?c:a.b.nodeType!=1?0:a.a?-1:1;if(typeof d=="number")a.d=d}
+L.prototype.next=function(){var a;if(this.g){if(!this.b||this.h&&this.d==0)throw K;a=this.b;var b=this.a?-1:1;if(this.c==b){var c=this.a?a.lastChild:a.firstChild;c?M(this,c):M(this,a,b*-1)}else(c=this.a?a.previousSibling:a.nextSibling)?M(this,c):M(this,a.parentNode,b*-1);this.d+=this.c*(this.a?-1:1)}else this.g=true;a=this.b;if(!this.b)throw K;return a};
+L.prototype.splice=function(){var a=this.b,b=this.a?1:-1;if(this.c==b){this.c=b*-1;this.d+=this.c*(this.a?-1:1)}this.a=!this.a;L.prototype.next.call(this);this.a=!this.a;b=o(arguments[0])?arguments[0]:arguments;for(var c=b.length-1;c>=0;c--)a.parentNode&&a.parentNode.insertBefore(b[c],a.nextSibling);a&&a.parentNode&&a.parentNode.removeChild(a)};function N(a,b,c,d){L.call(this,a,b,c,null,d)}r(N,L);N.prototype.next=function(){do N.j.next.call(this);while(this.c==-1);return this.b};function sa(a,b){var c=H(a);if(c.defaultView&&c.defaultView.getComputedStyle)if(c=c.defaultView.getComputedStyle(a,null))return c[b]||c.getPropertyValue(b);return""}function O(a,b){return sa(a,b)||(a.currentStyle?a.currentStyle[b]:null)||a.style[b]}
+function ta(a){var b=H(a),c=O(a,"position"),d=c=="fixed"||c=="absolute";for(a=a.parentNode;a&&a!=b;a=a.parentNode){c=O(a,"position");d=d&&c=="static"&&a!=b.documentElement&&a!=b.body;if(!d&&(a.scrollWidth>a.clientWidth||a.scrollHeight>a.clientHeight||c=="fixed"||c=="absolute"))return a}return null};function P(a,b){return!!a&&a.nodeType==1&&(!b||a.tagName.toUpperCase()==b)}
+var ua={"class":"className",readonly:"readOnly"},va=["checked","disabled","draggable","hidden"],wa=["async","autofocus","autoplay","checked","compact","complete","controls","declare","defaultchecked","defaultselected","defer","disabled","draggable","ended","formnovalidate","hidden","indeterminate","iscontenteditable","ismap","itemscope","loop","multiple","muted","nohref","noresize","noshade","novalidate","nowrap","open","paused","pubdate","readonly","required","reversed","scoped","seamless","seeking",
+"selected","spellcheck","truespeed","willvalidate"];function xa(a,b){if(8==a.nodeType)return null;b=b.toLowerCase();if(b=="style"){var c=a.style.cssText.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").toLowerCase();return c.charAt(c.length-1)==";"?c:c+";"}c=a.getAttributeNode(b);if(!c)return null;if(A(wa,b)>=0)return"true";return c.specified?c.value:null}var ya=["BUTTON","INPUT","OPTGROUP","OPTION","SELECT","TEXTAREA"];
+function za(a){var b=a.tagName.toUpperCase();if(!(A(ya,b)>=0))return true;if(xa(a,"disabled"))return false;if(a.parentNode&&a.parentNode.nodeType==1&&"OPTGROUP"==b||"OPTION"==b)return za(a.parentNode);return true}function Q(a){for(a=a.parentNode;a&&a.nodeType!=1&&a.nodeType!=9&&a.nodeType!=11;)a=a.parentNode;return P(a)?a:null}function R(a,b){b=ja(String(b));return sa(a,b)||Aa(a,b)}
+function Aa(a,b){var c=(a.currentStyle||a.style)[b];if(c!="inherit")return c!==undefined?c:null;return(c=Q(a))?Aa(c,b):null}function Ba(a){if(m(a.getBBox)=="function")return a.getBBox();var b;if(O(a,"display")!="none")b=new G(a.offsetWidth,a.offsetHeight);else{b=a.style;var c=b.display,d=b.visibility,f=b.position;b.visibility="hidden";b.position="absolute";b.display="inline";var e;e=a.offsetWidth;a=a.offsetHeight;b.display=c;b.position=f;b.visibility=d;b=new G(e,a)}return b}
+function S(a,b){function c(e){if(R(e,"display")=="none")return false;e=Q(e);return!e||c(e)}function d(e){var g=Ba(e);if(g.height>0&&g.width>0)return true;if(e.innerText||e.textContent)if(Ca.test(e.innerText||e.textContent))return true;return C&&ka(e.childNodes,function(k){return P(k)&&d(k)})}if(!P(a))throw Error("Argument to isShown must be of type Element");if(P(a,"TITLE"))return(H(a)?H(a).parentWindow||H(a).defaultView:window)==ga;if(P(a,"OPTION")||P(a,"OPTGROUP")){var f=I(a,function(e){return P(e,
+"SELECT")});return!!f&&S(f)}if(P(a,"MAP")){if(!a.name)return false;f=H(a);f=f.evaluate?qa('/descendant::*[@usemap = "#'+a.name+'"]',f):na(f,function(e){return P(e)&&xa(e,"usemap")=="#"+a.name});return!!f&&S(f)}if(P(a,"AREA")){f=I(a,function(e){return P(e,"MAP")});return!!f&&S(f)}if(P(a,"INPUT")&&a.type.toLowerCase()=="hidden")return false;if(R(a,"visibility")=="hidden")return false;if(!c(a))return false;if(!b&&Da(a)==0)return false;if(!d(a))return false;return true}
+var Ea="[\\s\\xa0"+String.fromCharCode(160)+"]+",Ca=RegExp("^"+Ea+"$");function Da(a){var b=1,c=R(a,"opacity");if(c)b=Number(c);if(a=Q(a))b*=Da(a);return b};var Fa=["dragstart","dragexit","mouseover","mouseout"];
+function T(a,b,c){var d=H(a),f=d?d.parentWindow||d.defaultView:window,e=new F;if(a.nodeType==1)if(a.getBoundingClientRect){var g=a.getBoundingClientRect();e.x=g.left;e.y=g.top}else{g=pa(a?new J(H(a)):E||(E=new J));var k,h=H(a);k=O(a,"position");var i=new F(0,0),t=(h?h.nodeType==9?h:H(h):document).documentElement;if(a!=t)if(a.getBoundingClientRect){k=a.getBoundingClientRect();h=pa(h?new J(H(h)):E||(E=new J));i.x=k.left+h.x;i.y=k.top+h.y}else if(h.getBoxObjectFor){k=h.getBoxObjectFor(a);h=h.getBoxObjectFor(t);
+i.x=k.screenX-h.screenX;i.y=k.screenY-h.screenY}else{var j=a;do{i.x+=j.offsetLeft;i.y+=j.offsetTop;if(j!=a){i.x+=j.clientLeft||0;i.y+=j.clientTop||0}if(C&&O(j,"position")=="fixed"){i.x+=h.body.scrollLeft;i.y+=h.body.scrollTop;break}j=j.offsetParent}while(j&&j!=a);if(C&&k=="absolute")i.y-=h.body.offsetTop;for(j=a;(j=ta(j))&&j!=h.body&&j!=t;){i.x-=j.scrollLeft;i.y-=j.scrollTop}}e.x=i.x-g.x;e.y=i.y-g.y}else{g=m(a.f)=="function";i=a;if(a.targetTouches)i=a.targetTouches[0];else if(g&&a.f().targetTouches)i=
+a.f().targetTouches[0];e.x=i.clientX;e.y=i.clientY}var n=c||{};c=(n.x||0)+e.x;e=(n.y||0)+e.y;g=n.button||0;i=n.bubble||true;k=null;if(A(Fa,b)>=0)k=n.related||null;h=!!n.alt;t=!!n.control;j=!!n.shift;n=!!n.meta;if(a.fireEvent&&d&&d.createEventObject){a=d.createEventObject();a.altKey=h;a.k=t;a.metaKey=n;a.shiftKey=j;a.clientX=c;a.clientY=e;a.button=g;a.relatedTarget=k}else{a=d.createEvent("MouseEvents");if(a.initMouseEvent)a.initMouseEvent(b,i,true,f,1,0,0,c,e,t,h,j,n,g,k);else{a.initEvent(b,i,true);
+a.shiftKey=j;a.metaKey=n;a.altKey=h;a.ctrlKey=t;a.button=g}}return a}function U(a,b,c){var d=c||{};c=d.keyCode||0;var f=d.charCode||0,e=!!d.alt,g=!!d.ctrl,k=!!d.shift;d=!!d.meta;a=H(a).createEvent("Events");a.initEvent(b,true,true);a.charCode=f;a.keyCode=c;a.altKey=e;a.ctrlKey=g;a.metaKey=d;a.shiftKey=k;return a}
+function Ga(a,b,c){var d=H(a),f=c||{};c=f.bubble!==false;var e=!!f.alt,g=!!f.control,k=!!f.shift;f=!!f.meta;if(a.fireEvent&&d&&d.createEventObject){a=d.createEventObject();a.altKey=e;a.l=g;a.metaKey=f;a.shiftKey=k}else{a=d.createEvent("HTMLEvents");a.initEvent(b,c,true);a.shiftKey=k;a.metaKey=f;a.altKey=e;a.ctrlKey=g}return a}var V={};V.click=T;V.keydown=U;V.keypress=U;V.keyup=U;V.mousedown=T;V.mousemove=T;V.mouseout=T;V.mouseover=T;V.mouseup=T;
+function Ha(a,b,c){c=(V[b]||Ga)(a,b,c);if(m(a.fireEvent)=="function"||q(a.fireEvent)){try{(H(a)?H(a).parentWindow||H(a).defaultView:window).event=c}catch(d){}a=a.fireEvent("on"+b,c)}else a=a.dispatchEvent(c);return a};function Ia(a){var b;if(P(a,"OPTION"))b=true;else if(P(a,"INPUT")){b=a.type.toLowerCase();b=b=="checkbox"||b=="radio"}else b=false;if(!b)throw new v(15,"Element is not selectable");b="selected";var c=a.type&&a.type.toLowerCase();if("checkbox"==c||"radio"==c)b="checked";b=ua[b]||b;a=a[b];a=a===undefined&&A(va,b)>=0?false:a;return!!a}function Ja(a){return P(a,"SELECT")}
+function Ka(a,b){if(!za(a))throw new v(12,"Element is not currently enabled and may not be manipulated");if(!S(a,true))throw new v(11,"Element is not currently visible and may not be manipulated");if(P(a,"INPUT")){var c=a.type.toLowerCase();if(c=="checkbox"||c=="radio"){if(a.checked!=b){if(a.type=="radio"&&!b)throw new v(12,"You may not deselect a radio button");if(b!=Ia(a)){a.checked=b;Ha(a,"change")}}}else throw new v(15,"You may not select an unselectable input element: "+a.type);}else if(P(a,
+"OPTION")){c=I(a,Ja);if(!c.multiple&&!b)throw new v(15,"You may not deselect an option within a select that does not support multiple selections.");if(b!=Ia(a)){a.selected=b;Ha(c,"change")}}else throw new v(15,"You may not select an unselectable element: "+a.tagName);};function W(a){switch(m(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return B(a,W);case "object":a=a;if("tagName"in a&&"nodeType"in a&&a.nodeType==1){var b={};b.ELEMENT=La(a);return b}if(o(a))return B(a,W);a=ba(a,function(c,d){return typeof d=="number"||p(d)});return u(a,W);default:return null}}
+function X(a,b){if(m(a)=="array")return B(a,function(c){return X(c,b)});else if(q(a))return"ELEMENT"in a?Ma(a.ELEMENT,b):u(a,function(c){return X(c,b)});return a}function Na(a){a=a||document;var b=a.$wdc_;if(!b){b=a.$wdc_={};b.i=aa()}return b}function La(a){var b=Na(a.ownerDocument),c=ca(b,function(d){return d==a});if(!c){c=":wdc:"+b.i++;b[c]=a}return c}
+function Ma(a,b){a=decodeURIComponent(a);var c=b||document,d=Na(c);if(!(a in d))throw new v(10,"Element does not exist in cache");var f=d[a];for(var e=f;e;){if(e==c.documentElement)return f;e=e.parentNode}delete d[a];throw new v(10,"Element is no longer attached to the DOM");};function Oa(a,b){var c=Ka,d=[a,b];try{if(p(c))c=new Function(c);var f=X(d),e=c.apply(null,f);W(e)}catch(g){}}var Y="_".split("."),Z=l;!(Y[0]in Z)&&Z.execScript&&Z.execScript("var "+Y[0]);for(var $;Y.length&&($=Y.shift());)if(!Y.length&&Oa!==undefined)Z[$]=Oa;else Z=Z[$]?Z[$]:Z[$]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/toggle_android.js b/core/res/res/raw/toggle_android.js
new file mode 100644
index 0000000..d4f4934
--- /dev/null
+++ b/core/res/res/raw/toggle_android.js
@@ -0,0 +1,29 @@
+function(){return function(){var l=this;
+function m(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(c=="[object Window]")return"object";if(c=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(c=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if(b==
+"function"&&typeof a.call=="undefined")return"object";return b}function aa(a){var b=m(a);return b=="array"||b=="object"&&typeof a.length=="number"}function o(a){return typeof a=="string"}function ba(a){a=m(a);return a=="object"||a=="array"||a=="function"}Math.floor(Math.random()*2147483648).toString(36);var ca=Date.now||function(){return+new Date};function p(a,b){function c(){}c.prototype=b.prototype;a.j=b.prototype;a.prototype=new c};function q(a){this.stack=Error().stack||"";if(a)this.message=String(a)}p(q,Error);q.prototype.name="CustomError";function da(a,b,c){var d={};for(var f in a)if(b.call(c,a[f],f,a))d[f]=a[f];return d}function ea(a,b,c){var d={};for(var f in a)d[f]=b.call(c,a[f],f,a);return d}function fa(a,b,c){for(var d in a)if(b.call(c,a[d],d,a))return d};function r(a,b){q.call(this,b);this.code=a;this.name=s[a]||s[13]}p(r,q);var s,ga={NoSuchElementError:7,NoSuchFrameError:8,UnknownCommandError:9,StaleElementReferenceError:10,ElementNotVisibleError:11,InvalidElementStateError:12,UnknownError:13,ElementNotSelectableError:15,XPathLookupError:19,NoSuchWindowError:23,InvalidCookieDomainError:24,UnableToSetCookieError:25,ModalDialogOpenedError:26,ModalDialogOpenError:27,ScriptTimeoutError:28},ha={};for(var ia in ga)ha[ga[ia]]=ia;s=ha;
+r.prototype.toString=function(){return"["+this.name+"] "+this.message};var ja=window;function ka(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a}var la={};function ma(a){return la[a]||(la[a]=String(a).replace(/\-([a-z])/g,function(b,c){return c.toUpperCase()}))};function u(a,b){b.unshift(a);q.call(this,ka.apply(null,b));b.shift();this.m=a}p(u,q);u.prototype.name="AssertionError";function v(a,b){if(!a){var c=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(b){d+=": "+b;var f=c}throw new u(""+d,f||[]);}return a};var w=Array.prototype,x=w.indexOf?function(a,b,c){v(a.length!=null);return w.indexOf.call(a,b,c)}:function(a,b,c){c=c==null?0:c<0?Math.max(0,a.length+c):c;if(o(a)){if(!o(b)||b.length!=1)return-1;return a.indexOf(b,c)}for(c=c;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},y=w.map?function(a,b,c){v(a.length!=null);return w.map.call(a,b,c)}:function(a,b,c){var d=a.length,f=Array(d),e=o(a)?a.split(""):a;for(var g=0;g<d;g++)if(g in e)f[g]=b.call(c,e[g],g,a);return f},na=w.some?function(a,b,c){v(a.length!=
+null);return w.some.call(a,b,c)}:function(a,b,c){var d=a.length,f=o(a)?a.split(""):a;for(var e=0;e<d;e++)if(e in f&&b.call(c,f[e],e,a))return true;return false};var z=true,oa="",A;if(z)A=/WebKit\/(\S+)/;if(A){var pa=A.exec(l.navigator?l.navigator.userAgent:null);oa=pa?pa[1]:""};var B;function C(a,b){this.x=a!==undefined?a:0;this.y=b!==undefined?b:0}C.prototype.toString=function(){return"("+this.x+", "+this.y+")"};function D(a,b){this.width=a;this.height=b}D.prototype.toString=function(){return"("+this.width+" x "+this.height+")"};D.prototype.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};function E(a){return a.nodeType==9?a:a.ownerDocument||a.document}function qa(a,b){var c=[];return ra(a,b,c,true)?c[0]:undefined}function ra(a,b,c,d){if(a!=null){var f=0;for(var e;e=a.childNodes[f];f++){if(b(e)){c.push(e);if(d)return true}if(ra(e,b,c,d))return true}}return false}function F(a,b,c,d){if(!c)a=a.parentNode;c=d==null;for(var f=0;a&&(c||f<=d);){if(b(a))return a;a=a.parentNode;f++}return null}function G(a){this.e=a||l.document||document}
+function sa(a){a=!z&&a.e.compatMode=="CSS1Compat"?a.e.documentElement:a.e.body;return new C(a.scrollLeft,a.scrollTop)};function ta(a,b){try{var c;if(typeof b.selectSingleNode!="undefined"){var d=E(b);typeof d.setProperty!="undefined"&&d.setProperty("SelectionLanguage","XPath");c=b.selectSingleNode(a)}else if(document.implementation.hasFeature("XPath","3.0")){d=E(b);var f=d.createNSResolver(d.documentElement);c=d.evaluate(a,b,f,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue}else c=null}catch(e){return null}if(!c)return null;if(c.nodeType!=1)throw Error("Returned node is not an element: "+a);return c};var H="StopIteration"in l?l.StopIteration:Error("StopIteration");function ua(){}ua.prototype.next=function(){throw H;};function I(a,b,c,d,f){this.a=!!b;a&&J(this,a,d);this.d=f!=undefined?f:this.c||0;if(this.a)this.d*=-1;this.h=!c}p(I,ua);I.prototype.b=null;I.prototype.c=0;I.prototype.g=false;function J(a,b,c,d){if(a.b=b)a.c=typeof c=="number"?c:a.b.nodeType!=1?0:a.a?-1:1;if(typeof d=="number")a.d=d}
+I.prototype.next=function(){var a;if(this.g){if(!this.b||this.h&&this.d==0)throw H;a=this.b;var b=this.a?-1:1;if(this.c==b){var c=this.a?a.lastChild:a.firstChild;c?J(this,c):J(this,a,b*-1)}else(c=this.a?a.previousSibling:a.nextSibling)?J(this,c):J(this,a.parentNode,b*-1);this.d+=this.c*(this.a?-1:1)}else this.g=true;a=this.b;if(!this.b)throw H;return a};
+I.prototype.splice=function(){var a=this.b,b=this.a?1:-1;if(this.c==b){this.c=b*-1;this.d+=this.c*(this.a?-1:1)}this.a=!this.a;I.prototype.next.call(this);this.a=!this.a;b=aa(arguments[0])?arguments[0]:arguments;for(var c=b.length-1;c>=0;c--)a.parentNode&&a.parentNode.insertBefore(b[c],a.nextSibling);a&&a.parentNode&&a.parentNode.removeChild(a)};function K(a,b,c,d){I.call(this,a,b,c,null,d)}p(K,I);K.prototype.next=function(){do K.j.next.call(this);while(this.c==-1);return this.b};function va(a,b){var c=E(a);if(c.defaultView&&c.defaultView.getComputedStyle)if(c=c.defaultView.getComputedStyle(a,null))return c[b]||c.getPropertyValue(b);return""}function L(a,b){return va(a,b)||(a.currentStyle?a.currentStyle[b]:null)||a.style[b]}
+function wa(a){var b=E(a),c=L(a,"position"),d=c=="fixed"||c=="absolute";for(a=a.parentNode;a&&a!=b;a=a.parentNode){c=L(a,"position");d=d&&c=="static"&&a!=b.documentElement&&a!=b.body;if(!d&&(a.scrollWidth>a.clientWidth||a.scrollHeight>a.clientHeight||c=="fixed"||c=="absolute"))return a}return null};function M(a,b){return!!a&&a.nodeType==1&&(!b||a.tagName.toUpperCase()==b)}
+var xa={"class":"className",readonly:"readOnly"},ya=["checked","disabled","draggable","hidden"],za=["async","autofocus","autoplay","checked","compact","complete","controls","declare","defaultchecked","defaultselected","defer","disabled","draggable","ended","formnovalidate","hidden","indeterminate","iscontenteditable","ismap","itemscope","loop","multiple","muted","nohref","noresize","noshade","novalidate","nowrap","open","paused","pubdate","readonly","required","reversed","scoped","seamless","seeking",
+"selected","spellcheck","truespeed","willvalidate"];function Aa(a,b){if(8==a.nodeType)return null;b=b.toLowerCase();if(b=="style"){var c=a.style.cssText.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").toLowerCase();return c.charAt(c.length-1)==";"?c:c+";"}c=a.getAttributeNode(b);if(!c)return null;if(x(za,b)>=0)return"true";return c.specified?c.value:null}var Ba=["BUTTON","INPUT","OPTGROUP","OPTION","SELECT","TEXTAREA"];
+function Ca(a){var b=a.tagName.toUpperCase();if(!(x(Ba,b)>=0))return true;if(Aa(a,"disabled"))return false;if(a.parentNode&&a.parentNode.nodeType==1&&"OPTGROUP"==b||"OPTION"==b)return Ca(a.parentNode);return true}function N(a){for(a=a.parentNode;a&&a.nodeType!=1&&a.nodeType!=9&&a.nodeType!=11;)a=a.parentNode;return M(a)?a:null}function O(a,b){b=ma(String(b));return va(a,b)||Da(a,b)}
+function Da(a,b){var c=(a.currentStyle||a.style)[b];if(c!="inherit")return c!==undefined?c:null;return(c=N(a))?Da(c,b):null}function Ea(a){if(m(a.getBBox)=="function")return a.getBBox();var b;if(L(a,"display")!="none")b=new D(a.offsetWidth,a.offsetHeight);else{b=a.style;var c=b.display,d=b.visibility,f=b.position;b.visibility="hidden";b.position="absolute";b.display="inline";var e;e=a.offsetWidth;a=a.offsetHeight;b.display=c;b.position=f;b.visibility=d;b=new D(e,a)}return b}
+function P(a,b){function c(e){if(O(e,"display")=="none")return false;e=N(e);return!e||c(e)}function d(e){var g=Ea(e);if(g.height>0&&g.width>0)return true;if(e.innerText||e.textContent)if(Fa.test(e.innerText||e.textContent))return true;return z&&na(e.childNodes,function(k){return M(k)&&d(k)})}if(!M(a))throw Error("Argument to isShown must be of type Element");if(M(a,"TITLE"))return(E(a)?E(a).parentWindow||E(a).defaultView:window)==ja;if(M(a,"OPTION")||M(a,"OPTGROUP")){var f=F(a,function(e){return M(e,
+"SELECT")});return!!f&&P(f)}if(M(a,"MAP")){if(!a.name)return false;f=E(a);f=f.evaluate?ta('/descendant::*[@usemap = "#'+a.name+'"]',f):qa(f,function(e){return M(e)&&Aa(e,"usemap")=="#"+a.name});return!!f&&P(f)}if(M(a,"AREA")){f=F(a,function(e){return M(e,"MAP")});return!!f&&P(f)}if(M(a,"INPUT")&&a.type.toLowerCase()=="hidden")return false;if(O(a,"visibility")=="hidden")return false;if(!c(a))return false;if(!b&&Ga(a)==0)return false;if(!d(a))return false;return true}
+var Ha="[\\s\\xa0"+String.fromCharCode(160)+"]+",Fa=RegExp("^"+Ha+"$");function Ga(a){var b=1,c=O(a,"opacity");if(c)b=Number(c);if(a=N(a))b*=Ga(a);return b};var Ia=["dragstart","dragexit","mouseover","mouseout"];
+function Q(a,b,c){var d=E(a),f=d?d.parentWindow||d.defaultView:window,e=new C;if(a.nodeType==1)if(a.getBoundingClientRect){var g=a.getBoundingClientRect();e.x=g.left;e.y=g.top}else{g=sa(a?new G(E(a)):B||(B=new G));var k,h=E(a);k=L(a,"position");var i=new C(0,0),t=(h?h.nodeType==9?h:E(h):document).documentElement;if(a!=t)if(a.getBoundingClientRect){k=a.getBoundingClientRect();h=sa(h?new G(E(h)):B||(B=new G));i.x=k.left+h.x;i.y=k.top+h.y}else if(h.getBoxObjectFor){k=h.getBoxObjectFor(a);h=h.getBoxObjectFor(t);
+i.x=k.screenX-h.screenX;i.y=k.screenY-h.screenY}else{var j=a;do{i.x+=j.offsetLeft;i.y+=j.offsetTop;if(j!=a){i.x+=j.clientLeft||0;i.y+=j.clientTop||0}if(z&&L(j,"position")=="fixed"){i.x+=h.body.scrollLeft;i.y+=h.body.scrollTop;break}j=j.offsetParent}while(j&&j!=a);if(z&&k=="absolute")i.y-=h.body.offsetTop;for(j=a;(j=wa(j))&&j!=h.body&&j!=t;){i.x-=j.scrollLeft;i.y-=j.scrollTop}}e.x=i.x-g.x;e.y=i.y-g.y}else{g=m(a.f)=="function";i=a;if(a.targetTouches)i=a.targetTouches[0];else if(g&&a.f().targetTouches)i=
+a.f().targetTouches[0];e.x=i.clientX;e.y=i.clientY}var n=c||{};c=(n.x||0)+e.x;e=(n.y||0)+e.y;g=n.button||0;i=n.bubble||true;k=null;if(x(Ia,b)>=0)k=n.related||null;h=!!n.alt;t=!!n.control;j=!!n.shift;n=!!n.meta;if(a.fireEvent&&d&&d.createEventObject){a=d.createEventObject();a.altKey=h;a.k=t;a.metaKey=n;a.shiftKey=j;a.clientX=c;a.clientY=e;a.button=g;a.relatedTarget=k}else{a=d.createEvent("MouseEvents");if(a.initMouseEvent)a.initMouseEvent(b,i,true,f,1,0,0,c,e,t,h,j,n,g,k);else{a.initEvent(b,i,true);
+a.shiftKey=j;a.metaKey=n;a.altKey=h;a.ctrlKey=t;a.button=g}}return a}function R(a,b,c){var d=c||{};c=d.keyCode||0;var f=d.charCode||0,e=!!d.alt,g=!!d.ctrl,k=!!d.shift;d=!!d.meta;a=E(a).createEvent("Events");a.initEvent(b,true,true);a.charCode=f;a.keyCode=c;a.altKey=e;a.ctrlKey=g;a.metaKey=d;a.shiftKey=k;return a}
+function Ja(a,b,c){var d=E(a),f=c||{};c=f.bubble!==false;var e=!!f.alt,g=!!f.control,k=!!f.shift;f=!!f.meta;if(a.fireEvent&&d&&d.createEventObject){a=d.createEventObject();a.altKey=e;a.l=g;a.metaKey=f;a.shiftKey=k}else{a=d.createEvent("HTMLEvents");a.initEvent(b,c,true);a.shiftKey=k;a.metaKey=f;a.altKey=e;a.ctrlKey=g}return a}var S={};S.click=Q;S.keydown=R;S.keypress=R;S.keyup=R;S.mousedown=Q;S.mousemove=Q;S.mouseout=Q;S.mouseover=Q;S.mouseup=Q;
+function Ka(a,b,c){c=(S[b]||Ja)(a,b,c);if(m(a.fireEvent)=="function"||ba(a.fireEvent)){try{(E(a)?E(a).parentWindow||E(a).defaultView:window).event=c}catch(d){}a=a.fireEvent("on"+b,c)}else a=a.dispatchEvent(c);return a};function T(a){var b;if(M(a,"OPTION"))b=true;else if(M(a,"INPUT")){b=a.type.toLowerCase();b=b=="checkbox"||b=="radio"}else b=false;if(!b)throw new r(15,"Element is not selectable");b="selected";var c=a.type&&a.type.toLowerCase();if("checkbox"==c||"radio"==c)b="checked";b=xa[b]||b;a=a[b];a=a===undefined&&x(ya,b)>=0?false:a;return!!a}function La(a){return M(a,"SELECT")}
+function Ma(a){if(M(a,"INPUT")&&"radio"==a.type)throw new r(12,"You may not toggle a radio button");var b=!T(a);if(!Ca(a))throw new r(12,"Element is not currently enabled and may not be manipulated");if(!P(a,true))throw new r(11,"Element is not currently visible and may not be manipulated");if(M(a,"INPUT")){var c=a.type.toLowerCase();if(c=="checkbox"||c=="radio"){if(a.checked!=b){if(a.type=="radio"&&!b)throw new r(12,"You may not deselect a radio button");if(b!=T(a)){a.checked=b;Ka(a,"change")}}}else throw new r(15,
+"You may not select an unselectable input element: "+a.type);}else if(M(a,"OPTION")){c=F(a,La);if(!c.multiple&&!b)throw new r(15,"You may not deselect an option within a select that does not support multiple selections.");if(b!=T(a)){a.selected=b;Ka(c,"change")}}else throw new r(15,"You may not select an unselectable element: "+a.tagName);return T(a)};function Na(){}
+function U(a,b,c){switch(typeof b){case "string":Oa(a,b,c);break;case "number":c.push(isFinite(b)&&!isNaN(b)?b:"null");break;case "boolean":c.push(b);break;case "undefined":c.push("null");break;case "object":if(b==null){c.push("null");break}if(m(b)=="array"){var d=b.length;c.push("[");var f="";for(var e=0;e<d;e++){c.push(f);U(a,b[e],c);f=","}c.push("]");break}c.push("{");d="";for(f in b)if(Object.prototype.hasOwnProperty.call(b,f)){e=b[f];if(typeof e!="function"){c.push(d);Oa(a,f,c);c.push(":");U(a,
+e,c);d=","}}c.push("}");break;case "function":break;default:throw Error("Unknown type: "+typeof b);}}var V={'"':'\\"',"\\":"\\\\","/":"\\/","\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\u000b":"\\u000b"},Pa=/\uffff/.test("\uffff")?/[\\\"\x00-\x1f\x7f-\uffff]/g:/[\\\"\x00-\x1f\x7f-\xff]/g;
+function Oa(a,b,c){c.push('"',b.replace(Pa,function(d){if(d in V)return V[d];var f=d.charCodeAt(0),e="\\u";if(f<16)e+="000";else if(f<256)e+="00";else if(f<4096)e+="0";return V[d]=e+f.toString(16)}),'"')};function W(a){switch(m(a)){case "string":case "number":case "boolean":return a;case "function":return a.toString();case "array":return y(a,W);case "object":a=a;if("tagName"in a&&"nodeType"in a&&a.nodeType==1){var b={};b.ELEMENT=Qa(a);return b}if(aa(a))return y(a,W);a=da(a,function(c,d){return typeof d=="number"||o(d)});return ea(a,W);default:return null}}
+function X(a,b){if(m(a)=="array")return y(a,function(c){return X(c,b)});else if(ba(a))return"ELEMENT"in a?Ra(a.ELEMENT,b):ea(a,function(c){return X(c,b)});return a}function Sa(a){a=a||document;var b=a.$wdc_;if(!b){b=a.$wdc_={};b.i=ca()}return b}function Qa(a){var b=Sa(a.ownerDocument),c=fa(b,function(d){return d==a});if(!c){c=":wdc:"+b.i++;b[c]=a}return c}
+function Ra(a,b){a=decodeURIComponent(a);var c=b||document,d=Sa(c);if(!(a in d))throw new r(10,"Element does not exist in cache");var f=d[a];for(var e=f;e;){if(e==c.documentElement)return f;e=e.parentNode}delete d[a];throw new r(10,"Element is no longer attached to the DOM");};function Ta(a){var b=Ma;a=[a];var c;try{if(o(b))b=new Function(b);var d=X(a),f=b.apply(null,d);c={status:0,value:W(f)}}catch(e){c={status:"code"in e?e.code:13,value:{message:e.message}}}b=[];U(new Na,c,b);return b.join("")}var Y="_".split("."),Z=l;!(Y[0]in Z)&&Z.execScript&&Z.execScript("var "+Y[0]);for(var $;Y.length&&($=Y.shift());)if(!Y.length&&Ta!==undefined)Z[$]=Ta;else Z=Z[$]?Z[$]:Z[$]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
diff --git a/core/res/res/raw/webdriver_readme.txt b/core/res/res/raw/webdriver_readme.txt
new file mode 100644
index 0000000..0ab6603
--- /dev/null
+++ b/core/res/res/raw/webdriver_readme.txt
@@ -0,0 +1,37 @@
+The JavaScript files *_android.js are used in frameworks/base/core/java/android/webkit/webdriver/
+. Those files contain closure compiled JavaScript from
+http://selenium.googlecode.com. They are under the Apache 2.0 licence:
+/** @license
+Copyright 2010 WebDriver committers
+Copyright 2010 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.
+*/
+
+The licence is not included in the compiled code to minimize the size
+of JavaScript injected into web pages.
+
+Those files can be generated by doing the following:
+$svn checkout http://selenium.googlecode.com/svn/trunk/ .
+$./go //javascript/webdriver-atoms/inject:<js_fragment_name>
+
+Where <js_fragment_name> should be replaced by the actual name of the fragment to
+generate. For example to generate is_selected_android.js, execute:
+$./go //javascript/webdriver-atoms/inject:is_selected
+
+The build file for those rules is under the following:
+http://code.google.com/p/selenium/source/browse/trunk/javascript/webdriver-atoms/inject/build.desc
+Every js_fragment rule generates a JavaScript file containing the corresponding
+JavaScript code snippet.
+
+The current version of the files was generated using revision 11823.
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 3731aaa..b0bd0f4 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"للسماح للتطبيق باسترداد معلومات حول المهام المُشغلة الحالية والحديثة. قد يسمح ذلك للتطبيقات الضارة باكتشاف معلومات خاصة حول التطبيقات الأخرى."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"إعادة ترتيب التطبيقات التي قيد التشغيل"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"للسماح لتطبيق ما بنقل المهام إلى المقدمة والخلفية. قد تفرض التطبيقات الضارة نفسها إلى المقدمة بدون تحكم منك."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"تمكين تصحيح أخطاء التطبيق"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"للسماح لتطبيق ما بتشغيل تصحيح الأخطاء لتطبيق آخر. قد تستخدم التطبيقات الضارة ذلك لإنهاء التطبيقات الأخرى."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"تغيير إعدادات واجهة المستخدم"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 0173f45..2c2035e 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Разрешава на приложението да извлича информация за задачите, изпълнявани понастоящем и неотдавна. Може да позволи на злонамерените приложения да открият поверителна информация за други приложения."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"пренареждане на изпълняваните приложения"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Разрешава на приложението да прехвърля задачи на преден и на заден план. Злонамерените приложения могат сами да се изведат на преден план без ваша намеса."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"активиране на отстраняването на грешки в приложения"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Разрешава на приложението да включва отстраняването на грешки в друго приложение. Злонамерените приложения могат да използват това, за да прекратят други приложения."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"промяна на настройките ви за потребителския интерфейс"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index e94e7e5..46b3342 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Permet a l\'aplicació recuperar informació sobre les tasques que s\'executen actualment i que s\'han executat recentment. Pot permetre a les aplicacions malicioses descobrir informació privada sobre altres aplicacions."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"canviar l\'ordre de les aplicacions en execució"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Permet a una aplicació desplaçar tasques al primer i al segon terme. Les aplicacions malicioses poden aparèixer en primer terme sense que ho controleu."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"activar la depuració d\'aplicacions"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Permet a una aplicació activar la depuració per a una altra aplicació. Les aplicacions malicioses poden utilitzar-ho per destruir altres aplicacions."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"canviar la configuració de la IU"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 0102536..a25640a 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Umožňuje aplikaci načíst informace o aktuálně a nedávno spuštěných úlohách. Toto nastavení může škodlivým aplikacím umožnit odhalení soukromých informací o jiných aplikacích."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"změna uspořádání spuštěných aplikací"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Umožňuje aplikaci přesouvat úlohy do popředí či pozadí. Škodlivé aplikace mohou vynutit své přesunutí do popředí bez vašeho přičinění."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"povolit ladění aplikací"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Umožňuje aplikaci povolit ladění jiné aplikace. Škodlivé aplikace mohou pomocí tohoto nastavení ukončit jiné aplikace."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"změna vašeho nastavení uživatelského rozhraní"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 93b59aa..1017d81 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Tillader, at et program henter oplysninger om nuværende og for nyligt kørende opgaver. Tillader, at eventuelt ondsindede programmer finder private oplysninger om andre programmer."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"omorganiser kørende programmer"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Tillader, at et program flytter opgaver til forgrunden og baggrunden. Ondsindede programmer kan tvinge dem selv til forgrunden uden din kontrol."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"aktiver programfejlretning"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Tillader, at et program slår fejlretning af andet program til. Ondsindede programmer kan bruge dette til at standse andre programmer."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"skift indstillinger for brugergrænsefladen"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index db3e9d9..d86fa07 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -151,7 +151,7 @@
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Flugmodus"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Flugmodus ist AN."</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Flugmodus ist AUS."</string>
-    <string name="status_bar_notification_info_overflow" msgid="5833510281787786290">"100 +"</string>
+    <string name="status_bar_notification_info_overflow" msgid="5833510281787786290">"Mehr als 100"</string>
     <string name="safeMode" msgid="2788228061547930246">"Abgesicherter Modus"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android-System"</string>
     <string name="permgrouplab_costMoney" msgid="5429808217861460401">"Kostenpflichtige Dienste"</string>
@@ -161,7 +161,7 @@
     <string name="permgrouplab_personalInfo" msgid="3519163141070533474">"Ihre persönlichen Informationen"</string>
     <string name="permgroupdesc_personalInfo" product="tablet" msgid="6975389054186265786">"Direkter Zugriff auf die Kontakte und den Kalender Ihres Tablets"</string>
     <string name="permgroupdesc_personalInfo" product="default" msgid="5488050357388806068">"Direkter Zugriff auf die Kontakte und den Kalender Ihres Telefons"</string>
-    <string name="permgrouplab_location" msgid="635149742436692049">"Ihren Standort"</string>
+    <string name="permgrouplab_location" msgid="635149742436692049">"Meinen Standort"</string>
     <string name="permgroupdesc_location" msgid="2430258821648348660">"Ihren physischen Standort überwachen"</string>
     <string name="permgrouplab_network" msgid="5808983377727109831">"Netzwerkkommunikation"</string>
     <string name="permgroupdesc_network" msgid="5035763698958415998">"Ermöglicht Anwendungen den Zugriff auf verschiedene Netzwerkfunktionen"</string>
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Ermöglicht der Anwendung, Informationen zu aktuellen und kürzlich ausführten Aufgaben abzurufen. Schädliche Anwendungen können so eventuell geheime Informationen zu anderen Anwendungen entdecken."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"Laufende Anwendungen neu ordnen"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Ermöglicht einer Anwendung, Aufgaben in den Vorder- und Hintergrund zu verschieben. Schädliche Anwendungen können so ohne Ihr Zutun eine Anzeige im Vordergrund erzwingen."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"Fehlerbeseitigung für Anwendung aktivieren"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Ermöglicht einer Anwendung, die Fehlerbeseitigung für eine andere Anwendung zu aktivieren. Schädliche Anwendungen können so andere Anwendungen löschen."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"UI-Einstellungen ändern"</string>
@@ -465,7 +469,7 @@
     <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6594393334785738252">"Ermöglicht der Anwendung Schreiben in USB-Speicher"</string>
     <string name="permdesc_sdcardWrite" product="default" msgid="6643963204976471878">"Ermöglicht einer Anwendung, auf die SD-Karte zu schreiben"</string>
     <string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"Intern. Mediensp. änd./löschen"</string>
-    <string name="permdesc_mediaStorageWrite" product="default" msgid="8232008512478316233">"Ermöglicht es einer Anwendung, den Inhalt des internen Medienspeichers zu ändern"</string>
+    <string name="permdesc_mediaStorageWrite" product="default" msgid="8232008512478316233">"Ermöglicht einer Anwendung, den Inhalt des internen Medienspeichers zu ändern"</string>
     <string name="permlab_cache_filesystem" msgid="5656487264819669824">"Zugriff auf das Cache-Dateisystem"</string>
     <string name="permdesc_cache_filesystem" msgid="1624734528435659906">"Gewährt einer Anwendung Lese- und Schreibzugriff auf das Cache-Dateisystem."</string>
     <string name="permlab_use_sip" msgid="5986952362795870502">"Internetanrufe tätigen/annehmen"</string>
@@ -966,7 +970,7 @@
     <string name="l2tp_ipsec_psk_vpn_description" msgid="3945043564008303239">"L2TP/IPSec-VPN mit vorinstalliertem Schlüssel"</string>
     <string name="l2tp_ipsec_crt_vpn_description" msgid="5382714073103653577">"L2TP/IPSec-VPN mit Zertifikat"</string>
     <string name="upload_file" msgid="2897957172366730416">"Datei auswählen"</string>
-    <string name="no_file_chosen" msgid="6363648562170759465">"Keine Datei ausgewählt"</string>
+    <string name="no_file_chosen" msgid="6363648562170759465">"Keine ausgewählt"</string>
     <string name="reset" msgid="2448168080964209908">"Zurücksetzen"</string>
     <string name="submit" msgid="1602335572089911941">"Senden"</string>
     <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Automodus aktiviert"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 571dc43..a5d2927 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Επιτρέπει σε μια εφαρμογή να ανακτήσει πληροφορίες σχετικά με τις τρέχουσες εκτελούμενες εργασίες και στις εργασίες που έχουν πρόσφατα εκτελεστεί. Ενδέχεται να δώσει τη δυνατότητα σε κακόβουλες εφαρμογές να ανακαλύψουν ιδιωτικές πληροφορίες σχετικά με άλλες εφαρμογές."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"αναδιάταξη εκτελούμενων εφαρμογών"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Επιτρέπει σε μια εφαρμογή τη μετακίνηση εργασιών στο προσκήνιο και στο φόντο. Κακόβουλες εφαρμογές μπορούν να προωθηθούν στο προσκήνιο χωρίς να μπορείτε να τις ελέγξετε."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"ενεργοποίηση εντοπισμού σφαλμάτων εφαρμογής"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Επιτρέπει σε μια εφαρμογή να ενεργοποιήσει τον εντοπισμό σφαλμάτων για μια άλλη εφαρμογή. Κακόβουλες εφαρμογές μπορούν να το χρησιμοποιήσουν για να τερματίσουν άλλες εφαρμογές."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"αλλαγή των ρυθμίσεων του UI"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 7015d48..f6ed325 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Allows application to retrieve information about currently and recently running tasks. May allow malicious applications to discover private information about other applications."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"reorder applications running"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Allows an application to move tasks to the foreground and background. Malicious applications can force themselves to the front without your control."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"enable application debugging"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Allows an application to turn on debugging for another application. Malicious applications can use this to kill other applications."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"change your UI settings"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 0052b18..820f5e2 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Admite que la aplicación recupere información sobre tareas en ejecución actuales y recientes. Puede permitir que las aplicaciones maliciosas descubran información privada sobre otras aplicaciones."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"reorganizar aplicaciones en ejecución"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Admite una aplicación que mueve tareas hacia el frente y el fondo. Las aplicaciones maliciosas pueden provocar su propio movimiento hacia el frente sin tu control."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"activar la depuración de la aplicación"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Admite una aplicación que activa la depuración en otra aplicación. Las aplicaciones maliciosas pueden utilizarlo para suprimir otras aplicaciones."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"cambiar tu configuración de la interfaz de usuario"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index bdca91f..2e187a5 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Permite que la aplicación recupere información sobre tareas que se están ejecutando en este momento o que se han ejecutado recientemente. Puede permitir que las aplicaciones malintencionadas vean información privada sobre otras aplicaciones."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"reorganizar aplicaciones en ejecución"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Permite que una aplicación mueva tareas a segundo plano y a primer plano. Las aplicaciones malintencionadas pueden aparecer en primer plano sin tu control."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"habilitar depuración de aplicación"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Permite que una aplicación active la depuración de otra aplicación. Las aplicaciones malintencionadas pueden utilizar este permiso para desactivar otras aplicaciones."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"cambiar la configuración de la interfaz de usuario"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 1f3ca7e..726bbab 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"به برنامه کاربردی اجازه می دهد اطلاعات مربوط به کارهای در حال اجرای فعلی و کارهای اخیر را بازیابی کند. ممکن است برنامه های مضر بتوانند اطلاعات خصوصی مربوط به شما را در ارتباط به سایر برنامه ها مشاهده کنند."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"ترتیب بندی مجدد برنامه های در حال اجرا"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"به یک برنامه کاربردی اجازه می دهد تا وظایف را به پیش زمینه و پس زمینه منتقل کند. برنامه های مضر می توانند بدون کنترل شما خودشان را به پیش زمینه منتقل کنند."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"فعال کردن رفع عیب برنامه"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"به یک برنامه کاربردی اجازه می دهد رفع عیب را برای یک برنامه دیگر فعال کند. برنامه های مضر می توانند از این ویژگی برای از بین بردن سایر برنامه ها استفاده کنند."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"تغییر تنظیمات UI"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index c9b6826..1c99fc6 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Antaa sovelluksen noutaa tietoja käynnissä olevista ja äskettäin käynnissä olleista tehtävistä. Haitalliset sovellukset saattavat saada selville yksityisiä tietoja muista sovelluksista."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"muuttaa käynnissä olevien sovelluksien järjestystä"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Antaa sovelluksen siirtää tehtäviä etualalle ja taustalle. Haitalliset sovellukset voivat tunkeutua etualalle väkisin."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"ota sovelluksen vianetsintä käyttöön"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Antaa sovelluksen käynnistää toisen sovelluksen vianetsinnän. Haitalliset sovellukset saattavat sulkea muita sovelluksia."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"muuta käyttöliittymäsi asetuksia"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index b38ae93..ab33b5c 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Permet à l\'application de récupérer des informations sur des tâches en cours d\'exécution ou récemment utilisées. Des applications malveillantes peuvent ainsi obtenir des informations d\'ordre privé concernant d\'autres applications."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"Réorganisation des applications en cours d\'exécution"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Permet à une application de placer des tâches au premier plan ou en arrière-plan. Des applications malveillantes peuvent se placer inopinément au premier plan sans votre autorisation."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"Activation du débogage de l\'application"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Permet à une application d\'activer le mode de débogage d\'une autre application. Des applications malveillantes peuvent utiliser cette fonctionnalité pour interrompre d\'autres applications de façon inopinée."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"Modification des paramètres de l\'IU"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index d8c64f0..6954364 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Aplikaciji omogućuje dohvaćanje podataka o trenutačno ili nedavno pokrenutim zadacima. Zlonamjernim aplikacijama može omogućiti otkrivanje privatnih podataka o drugim aplikacijama."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"promjena redoslijeda pokrenutih aplikacija"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Aplikaciji omogućuje premještanje zadataka u prvi plan ili u pozadinu. Zlonamjerne aplikacije mogu se prisilno postaviti u prednji plan bez vašeg znanja."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"omogućavanje uklanjanja programskih pogrešaka u aplikacijama"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Aplikaciji omogućuje uključivanje značajke uklanjanja programske pogreške za drugu aplikaciju. Zlonamjerne aplikacije to mogu koristiti za uklanjanje drugih aplikacija."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"promjena postavki korisničkog sučelja"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index ea4f81f..55b7cc2 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Lehetővé teszi az alkalmazás számára a jelenleg és a nemrég futó feladatok adatainak lekérését. A rosszindulatú alkalmazások privát adatokhoz juthatnak más alkalmazásokból."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"futó alkalmazások átrendezése"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Lehetővé teszi egy alkalmazás számára, hogy feladatokat helyezzen át az előtérből a háttérbe és fordítva. A rosszindulatú alkalmazások az előtérbe helyezhetik magukat az Ön engedélye nélkül."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"hibakeresés engedélyezése alkalmazásoknál"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Lehetővé teszi egy alkalmazás számára, hogy hibakeresést végezzen egy másik alkalmazáson. A rosszindulatú alkalmazások ezzel leállíthatnak más alkalmazásokat."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"a felhasználói felület beállításainak módosítása"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 4d5352a..50a1aad 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Mengizinkan aplikasi mengambil informasi tentang tugas yang sedang dan baru saja dijalankan. Aplikasi hasad dapat menemukan informasi bajakan tentang aplikasi lain."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"atur urutan aplikasi yang berjalan"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Mengizinkan aplikasi memindah tugas ke latar depan dan latar belakang. Aplikasi hasad dapat memaksa dirinya ke latar depan tanpa sepengetahuan Anda."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"aktifkan debugging aplikasi"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Mengizinkan aplikasi menghidupkan debug untuk aplikasi lain. Aplikasi hasad dapat menggunakan ini untuk menghentikan aplikasi penting lainnya."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"ubah setelan UI Anda"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 3196dbe..be7234e 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Consente all\'applicazione di recuperare informazioni sulle attività in esecuzione ed eseguite di recente. Le applicazioni dannose potrebbero essere in grado di scoprire informazioni riservate su altre applicazioni."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"riordinamento applicazioni in esecuz."</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Consente a un\'applicazione di spostare attività in primo e secondo piano. Le applicazioni dannose possono imporsi ponendosi automaticamente in primo piano."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"attivazione debug delle applicazioni"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Consente a un\'applicazione di attivare il debug per un\'altra applicazione. Le applicazioni dannose possono sfruttare questa possibilità per interrompere altre applicazioni."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"modifica impostazioni UI"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 07fab9e..e775182 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"מאפשר ליישום לאחזר מידע על משימות הפועלות כעת ושפעלו לאחרונה. עלול לאפשר ליישומים זדוניים לגלות מידע פרטי על יישומים אחרים."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"שנה את סדר היישומים הפועלים"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"מאפשר ליישום להעביר משימות לחזית ולרקע. יישומים זדוניים עלולים לאלץ את עצמם לחזית מבלי שתוכל לשלוט בהם."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"הפוך איתור באגים ביישום לפעיל"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"מאפשר ליישום להפעיל איתור באגים עבור יישום אחר. יישומים זדוניים יכולים להשתמש ביכולת זו כדי להשמיד יישומים אחרים."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"שנה את הגדרות ממשק המשתמש"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index c6a5f95..fb30252 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"現在実行中または最近実行したタスクに関する情報の取得をアプリケーションに許可します。悪意のあるアプリケーションが他のアプリケーションの非公開情報を取得する恐れがあります。"</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"実行中のアプリケーションの順序の変更"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"タスクをフォアグラウンドやバックグラウンドに移動することをアプリケーションに許可します。悪意のあるアプリケーションが優先されて、コントロールできなくなる恐れがあります。"</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"アプリケーションのデバッグを有効にする"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"別のアプリケーションをデバッグモードにすることをアプリケーションに許可します。悪意のあるアプリケーションが別のアプリケーションを終了させる恐れがあります。"</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"UI設定の変更"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 1b7051e..af83195 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"애플리케이션이 현재 실행 중이거나 최근에 실행된 작업에 대한 정보를 검색할 수 있도록 합니다. 이 경우 악성 애플리케이션이 다른 애플리케이션에 대한 개인 정보를 검색할 수 있습니다."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"실행 중인 애플리케이션 순서 재지정"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"애플리케이션이 작업을 포그라운드나 백그라운드로 이동할 수 있도록 합니다. 이 경우 악성 애플리케이션이 사용자의 조작 없이 앞으로 이동할 수 있습니다."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"애플리케이션 디버깅 사용"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"애플리케이션이 다른 애플리케이션에 대해 디버깅을 사용할 수 있도록 합니다. 이 경우 악성 애플리케이션이 다른 애플리케이션을 중지시킬 수 있습니다."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"UI 설정 변경"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 850a6c9..7d89830 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Leidžia programai nuskaityti informaciją apie dabar ir neseniai veikusias užduotis. Gali leisti kenkėjiškoms programoms atrasti privačią informaciją apie kitas programas."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"iš naujo užsakyti veikiančias programas"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Leidžia programai perkelti užduotis į aktyvųjį langą ir foną. Kenkėjiškos programos gali persikelti į priekį jums nieko nedarant."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"įgalinti programos derinimą"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Leidžia programai įjungti kitos programos derinimą. Kenkėjiškos programos tai gali naudoti, kad nutrauktų kitas programas."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"keisti UI nustatymus"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index a4936ac..ccc5fc1 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Ļauj lietojumprogrammai izgūt informāciju par pašlaik un nesen darbinātajiem uzdevumiem. Var atļaut lietojumprogrammām atklāt privātu informāciju par citām lietojumprogrammām."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"pārkārtot aktīvās lietojumprogrammas"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Ļauj lietojumprogrammai pārvietot uzdevumus priekšplānā vai fonā. Ļaunprātīgas lietojumprogrammas var tikt parādītas priekšplānā bez jūsu vadības."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"iespējot lietojumprogrammas atkļūdošanu"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Ļauj lietojumprogrammai ieslēgt citas lietojumprogrammas atkļūdošanu. Ļaunprātīgas lietojumprogrammas var to izmantot, lai pārtrauktu citu lietojumprogrammu darbību."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"mainīt lietotāja saskarnes iestatījumus"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 3b15f88..1c6d939 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Tillater applikasjonen å hente informasjon om aktive og nylig kjørte programmer. Kan tillate ondsinnede applikasjoner å oppdage privat informasjon om andre applikasjoner."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"omordne kjørende applikasjoner"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Tillater applikasjonen å flytte programmer til forgrunnen eller bakgrunnen. Ondsinnede applikasjoner kan tvinge seg selv til fronten."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"aktiver applikasjonsdebugging"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Lar applikasjonen skru på debugging for en annen applikasjon. Ondsinnede applikasjoner kan bruke dette til å drepe andre applikasjoner."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"endre innstillingene for brukergrensesnitt"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 5ca1e4d..3b612fa 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Hiermee kan een app informatie over huidige en recent uitgevoerde taken ophalen. Schadelijke apps kunnen op deze manier mogelijk privé-informatie over andere apps achterhalen."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"actieve toepassingen opnieuw indelen"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Hiermee kan een app taken naar de voor- en achtergrond verplaatsen. Schadelijke apps kunnen zichzelf op de voorgrond plaatsen zonder dat u hier iets aan kunt doen."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"foutopsporing in toepassingen inschakelen"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Hiermee kan een app de foutopsporing voor een andere app inschakelen. Schadelijke apps kunnen dit gebruiken om andere apps af te sluiten."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"uw UI-instellingen wijzigen"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 043faaf..d939ce6 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Umożliwia aplikacji pobieranie informacji na temat obecnie i ostatnio uruchomionych zadań. Może pozwolić szkodliwym aplikacjom na uzyskanie prywatnych informacji na temat innych aplikacji."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"zmienianie porządku uruchomionych aplikacji"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Pozwala aplikacji na przenoszenie zadań z tła na pierwszy plan. Szkodliwe aplikacje mogą wymusić działanie pierwszoplanowe bez kontroli użytkownika."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"włączenie debugowania aplikacji"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Pozwala aplikacji na włączenie debugowania innej aplikacji. Szkodliwe aplikacje mogą to wykorzystać do wyłączenia innych programów."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"zmienianie ustawień interfejsu użytkownika"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 1e2bd22..c49dc45 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Permite à aplicação obter informações sobre tarefas actualmente em execução e recentemente executadas. Pode permitir que aplicações maliciosas descubram informações privadas sobre outras aplicações."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"reordenar aplicações em execução"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Permite a uma aplicação mover tarefas do primeiro e do segundo planos.  Algumas aplicações maliciosas podem impor-se no primeiro plano sem o controlo do utilizador."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"activar depuração da aplicação"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Permite a uma aplicação activar a depuração para outra aplicação. Algumas aplicações maliciosas podem utilizar este item para eliminar outras aplicações."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"alterar definições da IU"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index fd3775a..32e3314 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Permite que o aplicativo recupere as informações sobre tarefas em execução no momento ou recentemente. Pode permitir que aplicativos maliciosos descubram informações particulares sobre outros aplicativos."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"reorganizar aplicativos em execução"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Permite que um aplicativo mova as tarefas para o primeiro e para o segundo planos. Aplicativos maliciosos podem se forçar à frente sem o seu controle."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"ativar depuração do aplicativo"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Permite que um aplicativo ative a depuração de outro aplicativo. Aplicativos maliciosos podem usar isso para encerrar outros aplicativos."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"alterar as suas configurações de UI"</string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index bc1e133..87796f7 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -208,6 +208,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Permetta a l\'applicaziun da recuperar infurmaziuns davart incumbensas che vegnan exequidas u èn gist vegnidas exequidas. Applicaziuns donnegiusas pon uschia obtegnair infurmaziuns privatas concernent autras applicaziuns."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"reorganisar las applicaziuns che vegnan exequidas"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Permetta ad ina applicaziun da spustar las incumbensas en il fund davant u en il fund davos. Applicaziuns donnegiusas pon sa mussar en il fund davant senza Vossa autorisaziun."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"activar il debugging da l\'applicaziun"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Permetta ad ina applicaziun dad activar il debugging per autras applicaziuns. Applicaziuns donnegiusas pon uschia serrar autras applicaziuns."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"modifitgar ils parameters da la UI"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index b388128..e07374e 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Permite aplicaţiei să regăsească informaţii despre activităţile rulate curent şi recent. Poate permite aplicaţiilor rău-intenţionate să descopere informaţii confidenţiale despre alte aplicaţii."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"reordonare aplicaţii aflate în derulare"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Permite unei aplicaţii să mute activităţile în prim-plan şi în fundal. Aplicaţiile rău-intenţionate ar putea să apară forţat în prim-plan, fără ca dvs. să puteţi controla acest lucru."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"activare depanare aplicaţie"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Permite unei aplicaţii să activeze depanarea pentru o altă aplicaţie. Aplicaţiile rău-intenţionate ar putea să utilizeze această permisiune pentru a închide alte aplicaţii."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"modificare setări UI"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 8e8f87f..7227a34 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Позволяет приложению получать сведения о последних и текущих задачах. Вредоносные приложения могут получить доступ к конфиденциальной информации о других приложениях."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"изменять порядок запущенных приложений"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Позволяет приложению переключать режим выполнения задачи с активного на фоновый. Вредоносные приложения могут установить для себя активный режим без уведомления."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"запускать отладку приложения"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Позволяет приложению запускать процесс отладки другого приложения. Вредоносные приложения могут использовать эту возможность для остановки других приложений."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"изменять настройки пользовательского интерфейса"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 8a37fd1..9a58c36 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Umožňuje aplikácii načítať informácie o aktuálne a nedávno spustených úlohách. Toto nastavenie môže škodlivým aplikáciám umožniť odhaliť súkromné informácie o iných aplikáciách."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"zmena usporiadania spustených aplikácií"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Umožňuje aplikácii presúvať úlohy do popredia alebo pozadia. Škodlivé aplikácie môžu vynútiť svoje presunutia do popredia bez vášho pričinenia."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"povoliť ladenie aplikácií"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Umožňuje aplikácii povoliť ladenie inej aplikácie. Škodlivé aplikácie môžu pomocou tohto nastavenia ukončiť iné aplikácie."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"zmeny vašich nastavení používateľského rozhrania"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 6ee4ac0..6c2830b 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Programu dovoljuje pridobivanje informacij o trenutnih in nedavno izvajajočih se opravilih. Zlonamerni programi lahko odkrijejo zasebne podatke o drugih programih."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"preurejanje programov, ki se izvajajo"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Programu dovoljuje premikanje opravil v ospredje in ozadje. Zlonamerni programi se lahko brez vašega nadzora vsilijo v ospredje."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"omogočanje iskanja in odpravljanja napak v programu"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Programu dovoljuje vklop funkcije iskanja in odpravljanja napak za drug program. Zlonamerni programi lahko to uporabijo za zapiranje drugih programov."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"spreminjanje nastavitev UV"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 6faf9da..f67979b 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Омогућава да апликација преузима информације о тренутно и недавно покренутим задацима. На тај начин злонамерне апликације могу да стекну увид у приватне информације о другим апликацијама."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"промена редоследа покретања апликација"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Омогућава да апликација премешта задатке у први план и позадину. Злонамерне апликације могу на тај начин да принудно пређу у први план, при чему ви нећете имати контролу над тим."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"омогућавање отклањања грешака у апликацији"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Омогућава да апликација укључи отклањање грешака за другу апликацију. Злонамерне апликације могу то да злоупотребе и искористе за онемогућавање других апликација."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"промена подешавања корисничког интерфејса"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index fcc1fdd..0413918 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Tillåter att program hämtar information om uppgifter som körs och har körts. Skadliga program kan upptäcka privat information om andra program."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"byt ordning på appar som körs"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Tillåter att ett program flyttar uppgifter till förgrunden eller bakgrunden. Skadliga program kan tvinga sig till förgrunden utan att du kan styra det."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"aktivera felsökning av appar"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Tillåter att ett program aktiverar felsökning för ett annat program. Skadliga program kan använda detta för att avsluta andra program."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"ändra dina gränssnittsinställningar"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index fae3853..aab60b5 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"อนุญาตให้แอปพลิเคชันเรียกดูข้อมูลเกี่ยวกับงานที่ทำเมื่อไม่นานมานี้และที่กำลังทำอยู่ วิธีนี้อาจทำให้แอปพลิเคชันที่เป็นอันตรายพบข้อมูลที่เป็นความลับเกี่ยวกับแอปพลิเคชันอื่นได้"</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"จัดลำดับแอปพลิเคชันที่ทำงานอยู่ใหม่"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"อนุญาตให้แอปพลิเคชันย้ายงานไปที่ด้านหน้าและพื้นหลัง แอปพลิเคชันที่เป็นอันตรายสามารถบังคับตัวเองให้อยู่ด้านหน้าได้โดยไม่ต้องให้คุณควบคุม"</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"เปิดใช้งานการแก้ไขข้อบกพร่องของแอปพลิเคชัน"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"อนุญาตให้แอปพลิเคชันเปิดการแก้ไขข้อบกพร่องสำหรับแอปพลิเคชันอื่น แอปพลิเคชันที่เป็นอันตรายอาจใช้วิธีนี้จบการทำงานแอปพลิเคชันอื่น"</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"เปลี่ยนการตั้งค่า UI ของคุณ"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 5d4a916..10c3438 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Pinapayagan ang application na ibalik ang impormasyon tungkol sa mga kasalukuyan at kamakailang tumatakbong gawain. Maaaring payagan ang mga nakakahamak na application na tuklasin ang pribadong impormasyon tungkol sa ibang mga application."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"muling pagsunud-sunurin ang mga tumatakbong application"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Pinapayagan ang isang application na ilipat ang mga gawain sa foreground at background. Mapupuwersa ng mga nakakahamak na application ang mga sarili nito sa harapan nang wala ng iyong kontrol."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"paganahin ang debugging ng application"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Pinapayagan ang isang application na i-on ang debugging para sa isa pang application. Magagamit ito ng mga nakakahamak na application upang alisin ang ibang mga application."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"baguhin ang iyong mga setting ng UI"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index ad4eee7..20ddef3 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Uygulamaların şu anda ve yakın geçmişte çalışmakta olan işlemler hakkında bilgi almasına izin verir. Kötü amaçlı uygulamaların diğer uygulamalar ile ilgili gizli bilgileri keşfetmesine izin verebilir."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"çalışan uygulamaları yeniden sırala"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Uygulamaların görevleri ön plana ve arka plana taşımasına izin verir. Kötü amaçlı uygulamalar kendilerini sizin denetiminiz dışında zorla ön plana çıkarabilir."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"uygulama hata ayıklamayı etkinleştir"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Bir uygulamanın başka bir uygulama için hata ayıklamayı çalıştırmasına izin verir. Kötü amaçlı uygulamalar bu işlevi başka uygulamaları kapatmak için kullanabilir."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"kullanıcı arayüzü ayarlarınızı değiştirin"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 60cea7b..b723004 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Дозволяє програмі отримувати інформацію про теперішні й останні завдання. Може дозволити шкідливим програмам дізнаватися приватну інформацію про інші програми."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"змін. порядок запущених програм"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Дозволяє програмі робити завдання активними та фоновими. Шкідливі програми можуть примусово ставати активними без контролю з вашого боку."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"увімк. налагодження програми"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Дозволяє програмі вмикати налагодження для іншої програми. Шкідливі програми можуть використовувати це для заверш. роботи ін. програм."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"змін. налашт. інтерф. кор."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 8070767..f200fdd 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"Cho phép ứng dụng truy xuất thông tin về các công việc hiện đang chạy. Có thể cho phép các ứng dụng độc hại phát hiện thông tin riêng tư về các ứng dụng khác."</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"sắp xếp lại các ứng dụng đang chạy"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"Cho phép ứng dụng di chuyển công việc lên trên nền và dưới nền. Các ứng dụng độc hại có thể tự hiện lên trước mà không cần sự kiểm soát của bạn."</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"cho phép gỡ lỗi ứng dụng"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"Cho phép ứng dụng bật gỡ lỗi cho ứng dụng khác. Các ứng dụng độc hại có thể sử dụng quyền này đề loại bỏ các ứng dụng khác."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"thay đổi cài đặt giao diện người dùng của bạn"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index fce5fca..124bd8e 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"允许应用程序检索有关当前和最近运行的任务的信息。恶意应用程序可借此发现有关其他应用程序的保密信息。"</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"对正在运行的应用程序重新排序"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"允许应用程序将任务移至前端和后台。恶意应用程序可借此强行进入前端,而不受您的控制。"</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"启用应用程序调试"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"允许应用程序启动对其他应用程序的调试。恶意应用程序可借此终止其他应用程序。"</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"更改用户界面设置"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index f87c2f6..33825c7 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -204,6 +204,10 @@
     <string name="permdesc_getTasks" msgid="7048711358713443341">"允許應用程式取得最近執行任務的資訊。請注意:惡意程式可能利用此功能找出其他應用程式的隱私資訊。"</string>
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"重新安排執行中的應用程式"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"允許應用程式將工作移至前端或背景作業。請注意:惡意程式可能使用此功能自行把自己拉到前端。"</string>
+    <!-- no translation found for permlab_removeTasks (4802740047161700683) -->
+    <skip />
+    <!-- no translation found for permdesc_removeTasks (2000332928514575461) -->
+    <skip />
     <string name="permlab_setDebugApp" msgid="4339730312925176742">"啟用應用程式偵錯"</string>
     <string name="permdesc_setDebugApp" msgid="5584310661711990702">"允許應用程式為其他程式開啟偵錯功能。請注意:惡意程式可利用此功能終止其他應用程式。"</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"變更介面設定"</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 80beaa5..9b04f78 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1169,6 +1169,10 @@
              component specific values). -->
         <attr name="enabled" />
         <attr name="exported" />
+        <!-- If set to true, this service with be automatically stopped
+             when the user remove a task rooted in an activity owned by
+             the application.  The default is false. -->
+        <attr name="stopWithTask" format="boolean" />
     </declare-styleable>
     
     <!-- The <code>receiver</code> tag declares an
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index d5c374d..778d934 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1656,6 +1656,7 @@
   <public type="attr" name="state_hovered" />
   <public type="attr" name="state_drag_can_accept" />
   <public type="attr" name="state_drag_hovered" />
+  <public type="attr" name="stopWithTask" />
 
   <public type="style" name="Theme.Holo.Light.NoActionBar" />
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8ef9a3b..bc419ec 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -506,6 +506,13 @@
         tasks to the foreground and background. Malicious applications can force
         themselves to the front without your control.</string>
 
+    <!-- Title of an application permission, allowing an application to remove/kill tasks -->
+    <string name="permlab_removeTasks">stop running applications</string>
+    <!-- Description of an application permission, allowing an application to remove/kill tasks -->
+    <string name="permdesc_removeTasks">Allows an application to remove
+        tasks and kill their applications. Malicious applications can disrupt
+        the behavior of other applications.</string>
+
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_setDebugApp">enable application debugging</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
index 3667c7b..d22356d 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
@@ -182,6 +182,7 @@
                 }
                 config.proxySettings = ProxySettings.NONE;
                 networks.add(config);
+                mLinkProperties = null;
             }
         }
 
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
index e138200..adf1883c8 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
@@ -242,10 +242,10 @@
 
         initializeNetworkStates();
 
-        if (mWifiManager.isWifiEnabled()) {
-            log("Clear Wifi before we start the test.");
-            removeConfiguredNetworksAndDisableWifi();
-        }
+        mWifiManager.setWifiEnabled(true);
+        log("Clear Wifi before we start the test.");
+        sleep(SHORT_TIMEOUT);
+        removeConfiguredNetworksAndDisableWifi();
         mWifiRegexs = mCM.getTetherableWifiRegexs();
      }
 
@@ -633,13 +633,13 @@
      * Disconnect from the current AP and remove configured networks.
      */
     public boolean disconnectAP() {
-        if (mWifiManager.isWifiEnabled()) {
-            // remove saved networks
-            List<WifiConfiguration> wifiConfigList = mWifiManager.getConfiguredNetworks();
-            for (WifiConfiguration wifiConfig: wifiConfigList) {
-                log("remove wifi configuration: " + wifiConfig.toString());
-                mWifiManager.forgetNetwork(wifiConfig.networkId);
-            }
+        // remove saved networks
+        List<WifiConfiguration> wifiConfigList = mWifiManager.getConfiguredNetworks();
+        log("size of wifiConfigList: " + wifiConfigList.size());
+        for (WifiConfiguration wifiConfig: wifiConfigList) {
+            log("remove wifi configuration: " + wifiConfig.networkId);
+            int netId = wifiConfig.networkId;
+            mWifiManager.forgetNetwork(netId);
         }
         return true;
     }
@@ -655,20 +655,23 @@
      * Remove configured networks and disable wifi
      */
     public boolean removeConfiguredNetworksAndDisableWifi() {
-            if (!disconnectAP()) {
-                return false;
-            }
-            // Disable Wifi
-            if (!mWifiManager.setWifiEnabled(false)) {
-                return false;
-            }
-            // Wait for the actions to be completed
-            try {
-                Thread.sleep(SHORT_TIMEOUT);
-            } catch (InterruptedException e) {}
+        if (!disconnectAP()) {
+           return false;
+        }
+        sleep(SHORT_TIMEOUT);
+        if (!mWifiManager.setWifiEnabled(false)) {
+            return false;
+        }
+        sleep(SHORT_TIMEOUT);
         return true;
     }
 
+    private void sleep(long sleeptime) {
+        try {
+            Thread.sleep(sleeptime);
+        } catch (InterruptedException e) {}
+    }
+
     /**
      * Set airplane mode
      */
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
index fe79e6c..22b1759 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
@@ -72,10 +72,8 @@
     @Override
     public void setUp() throws Exception {
         super.setUp();
-        log("before we launch the test activity, we preserve all the configured networks.");
         mRunner = ((ConnectivityManagerTestRunner)getInstrumentation());
         mWifiManager = (WifiManager) mRunner.getContext().getSystemService(Context.WIFI_SERVICE);
-        enabledNetworks = getEnabledNetworks(mWifiManager.getConfiguredNetworks());
 
         mAct = getActivity();
         mWifiManager.asyncConnect(mAct, new WifiServiceHandler());
@@ -123,42 +121,9 @@
     public void tearDown() throws Exception {
         log("tearDown()");
         mAct.removeConfiguredNetworksAndDisableWifi();
-        reEnableNetworks(enabledNetworks);
         super.tearDown();
     }
 
-    private Set<WifiConfiguration> getEnabledNetworks(List<WifiConfiguration> configuredNetworks) {
-        Set<WifiConfiguration> networks = new HashSet<WifiConfiguration>();
-        for (WifiConfiguration wifiConfig : configuredNetworks) {
-            if (wifiConfig.status == Status.ENABLED || wifiConfig.status == Status.CURRENT) {
-                networks.add(wifiConfig);
-                log("remembering enabled network " + wifiConfig.SSID +
-                        " status is " + wifiConfig.status);
-            }
-        }
-        return networks;
-    }
-
-    private void reEnableNetworks(Set<WifiConfiguration> enabledNetworks) {
-        if (!mWifiManager.isWifiEnabled()) {
-            log("reEnableNetworks: enable Wifi");
-            mWifiManager.setWifiEnabled(true);
-            sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT,
-                    "interruped while waiting for wifi to be enabled");
-        }
-
-        for (WifiConfiguration config : enabledNetworks) {
-            if (DEBUG) {
-                log("recover wifi configuration: " + config.toString());
-            }
-            config.SSID = "\"" + config.SSID + "\"";
-            config.networkId = -1;
-            mWifiManager.connectNetwork(config);
-            sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT,
-                    "interruped while connecting to " + config.SSID);
-        }
-    }
-
     /**
      * Connect to the provided Wi-Fi network
      * @param config is the network configuration
diff --git a/core/tests/coretests/src/com/android/internal/util/HierarchicalStateMachineTest.java b/core/tests/coretests/src/com/android/internal/util/StateMachineTest.java
similarity index 84%
rename from core/tests/coretests/src/com/android/internal/util/HierarchicalStateMachineTest.java
rename to core/tests/coretests/src/com/android/internal/util/StateMachineTest.java
index 36666c4..ab6b2b6 100644
--- a/core/tests/coretests/src/com/android/internal/util/HierarchicalStateMachineTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/StateMachineTest.java
@@ -22,9 +22,9 @@
 import android.os.Message;
 import android.os.SystemClock;
 
-import com.android.internal.util.HierarchicalState;
-import com.android.internal.util.HierarchicalStateMachine;
-import com.android.internal.util.ProcessedMessages;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+import com.android.internal.util.StateMachine.ProcessedMessageInfo;
 
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -33,9 +33,9 @@
 import junit.framework.TestCase;
 
 /**
- * Test for HierarchicalStateMachine.
+ * Test for StateMachine.
  */
-public class HierarchicalStateMachineTest extends TestCase {
+public class StateMachineTest extends TestCase {
     private static final int TEST_CMD_1 = 1;
     private static final int TEST_CMD_2 = 2;
     private static final int TEST_CMD_3 = 3;
@@ -45,12 +45,12 @@
 
     private static final boolean DBG = true;
     private static final boolean WAIT_FOR_DEBUGGER = false;
-    private static final String TAG = "HierarchicalStateMachineTest";
+    private static final String TAG = "StateMachineTest";
 
     /**
      * Tests that we can quit the state machine.
      */
-    class StateMachineQuitTest extends HierarchicalStateMachine {
+    class StateMachineQuitTest extends StateMachine {
         private int mQuitCount = 0;
 
         StateMachineQuitTest(String name) {
@@ -65,8 +65,9 @@
             setInitialState(mS1);
         }
 
-        class S1 extends HierarchicalState {
-            @Override protected boolean processMessage(Message message) {
+        class S1 extends State {
+            @Override
+            public boolean processMessage(Message message) {
                 if (isQuit(message)) {
                     mQuitCount += 1;
                     if (mQuitCount > 2) {
@@ -125,22 +126,22 @@
 
         assertTrue(smQuitTest.getProcessedMessagesCount() == 9);
 
-        ProcessedMessages.Info pmi;
+        ProcessedMessageInfo pmi;
 
         // The first two message didn't quit and were handled by mS1
-        pmi = smQuitTest.getProcessedMessage(6);
-        assertEquals(HierarchicalStateMachine.HSM_QUIT_CMD, pmi.getWhat());
+        pmi = smQuitTest.getProcessedMessageInfo(6);
+        assertEquals(StateMachine.SM_QUIT_CMD, pmi.getWhat());
         assertEquals(smQuitTest.mS1, pmi.getState());
         assertEquals(smQuitTest.mS1, pmi.getOriginalState());
 
-        pmi = smQuitTest.getProcessedMessage(7);
-        assertEquals(HierarchicalStateMachine.HSM_QUIT_CMD, pmi.getWhat());
+        pmi = smQuitTest.getProcessedMessageInfo(7);
+        assertEquals(StateMachine.SM_QUIT_CMD, pmi.getWhat());
         assertEquals(smQuitTest.mS1, pmi.getState());
         assertEquals(smQuitTest.mS1, pmi.getOriginalState());
 
         // The last message was never handled so the states are null
-        pmi = smQuitTest.getProcessedMessage(8);
-        assertEquals(HierarchicalStateMachine.HSM_QUIT_CMD, pmi.getWhat());
+        pmi = smQuitTest.getProcessedMessageInfo(8);
+        assertEquals(StateMachine.SM_QUIT_CMD, pmi.getWhat());
         assertEquals(null, pmi.getState());
         assertEquals(null, pmi.getOriginalState());
 
@@ -150,7 +151,7 @@
     /**
      * Test enter/exit can use transitionTo
      */
-    class StateMachineEnterExitTransitionToTest extends HierarchicalStateMachine {
+    class StateMachineEnterExitTransitionToTest extends StateMachine {
         StateMachineEnterExitTransitionToTest(String name) {
             super(name);
             mThisSm = this;
@@ -166,34 +167,38 @@
             setInitialState(mS1);
         }
 
-        class S1 extends HierarchicalState {
-            @Override protected void enter() {
+        class S1 extends State {
+            @Override
+            public void enter() {
                 // Test that message is HSM_INIT_CMD
-                assertEquals(HSM_INIT_CMD, getCurrentMessage().what);
+                assertEquals(SM_INIT_CMD, getCurrentMessage().what);
 
                 // Test that a transition in enter and the initial state works
                 mS1EnterCount += 1;
                 transitionTo(mS2);
                 Log.d(TAG, "S1.enter");
             }
-            @Override protected void exit() {
+            @Override
+            public void exit() {
                 // Test that message is HSM_INIT_CMD
-                assertEquals(HSM_INIT_CMD, getCurrentMessage().what);
+                assertEquals(SM_INIT_CMD, getCurrentMessage().what);
 
                 mS1ExitCount += 1;
                 Log.d(TAG, "S1.exit");
             }
         }
 
-        class S2 extends HierarchicalState {
-            @Override protected void enter() {
+        class S2 extends State {
+            @Override
+            public void enter() {
                 // Test that message is HSM_INIT_CMD
-                assertEquals(HSM_INIT_CMD, getCurrentMessage().what);
+                assertEquals(SM_INIT_CMD, getCurrentMessage().what);
 
                 mS2EnterCount += 1;
                 Log.d(TAG, "S2.enter");
             }
-            @Override protected void exit() {
+            @Override
+            public void exit() {
                 // Test that message is TEST_CMD_1
                 assertEquals(TEST_CMD_1, getCurrentMessage().what);
 
@@ -202,7 +207,8 @@
                 transitionTo(mS4);
                 Log.d(TAG, "S2.exit");
             }
-            @Override protected boolean processMessage(Message message) {
+            @Override
+            public boolean processMessage(Message message) {
                 // Start a transition to S3 but it will be
                 // changed to a transition to S4 in exit
                 transitionTo(mS3);
@@ -211,28 +217,32 @@
             }
         }
 
-        class S3 extends HierarchicalState {
-            @Override protected void enter() {
+        class S3 extends State {
+            @Override
+            public void enter() {
                 // Test that we can do halting in an enter/exit
                 transitionToHaltingState();
                 mS3EnterCount += 1;
                 Log.d(TAG, "S3.enter");
             }
-            @Override protected void exit() {
+            @Override
+            public void exit() {
                 mS3ExitCount += 1;
                 Log.d(TAG, "S3.exit");
             }
         }
 
 
-        class S4 extends HierarchicalState {
-            @Override protected void enter() {
+        class S4 extends State {
+            @Override
+            public void enter() {
                 // Test that we can do halting in an enter/exit
                 transitionToHaltingState();
                 mS4EnterCount += 1;
                 Log.d(TAG, "S4.enter");
             }
-            @Override protected void exit() {
+            @Override
+            public void exit() {
                 mS4ExitCount += 1;
                 Log.d(TAG, "S4.exit");
             }
@@ -285,10 +295,10 @@
 
         assertTrue(smEnterExitTranstionToTest.getProcessedMessagesCount() == 1);
 
-        ProcessedMessages.Info pmi;
+        ProcessedMessageInfo pmi;
 
         // Message should be handled by mS2.
-        pmi = smEnterExitTranstionToTest.getProcessedMessage(0);
+        pmi = smEnterExitTranstionToTest.getProcessedMessageInfo(0);
         assertEquals(TEST_CMD_1, pmi.getWhat());
         assertEquals(smEnterExitTranstionToTest.mS2, pmi.getState());
         assertEquals(smEnterExitTranstionToTest.mS2, pmi.getOriginalState());
@@ -310,7 +320,7 @@
     /**
      * Tests that ProcessedMessage works as a circular buffer.
      */
-    class StateMachine0 extends HierarchicalStateMachine {
+    class StateMachine0 extends StateMachine {
         StateMachine0(String name) {
             super(name);
             mThisSm = this;
@@ -324,8 +334,9 @@
             setInitialState(mS1);
         }
 
-        class S1 extends HierarchicalState {
-            @Override protected boolean processMessage(Message message) {
+        class S1 extends State {
+            @Override
+            public boolean processMessage(Message message) {
                 if (message.what == TEST_CMD_6) {
                     transitionToHaltingState();
                 }
@@ -369,18 +380,18 @@
         assertTrue(sm0.getProcessedMessagesCount() == 6);
         assertTrue(sm0.getProcessedMessagesSize() == 3);
 
-        ProcessedMessages.Info pmi;
-        pmi = sm0.getProcessedMessage(0);
+        ProcessedMessageInfo pmi;
+        pmi = sm0.getProcessedMessageInfo(0);
         assertEquals(TEST_CMD_4, pmi.getWhat());
         assertEquals(sm0.mS1, pmi.getState());
         assertEquals(sm0.mS1, pmi.getOriginalState());
 
-        pmi = sm0.getProcessedMessage(1);
+        pmi = sm0.getProcessedMessageInfo(1);
         assertEquals(TEST_CMD_5, pmi.getWhat());
         assertEquals(sm0.mS1, pmi.getState());
         assertEquals(sm0.mS1, pmi.getOriginalState());
 
-        pmi = sm0.getProcessedMessage(2);
+        pmi = sm0.getProcessedMessageInfo(2);
         assertEquals(TEST_CMD_6, pmi.getWhat());
         assertEquals(sm0.mS1, pmi.getState());
         assertEquals(sm0.mS1, pmi.getOriginalState());
@@ -394,7 +405,7 @@
      * in state mS1. With the first message it transitions to
      * itself which causes it to be exited and reentered.
      */
-    class StateMachine1 extends HierarchicalStateMachine {
+    class StateMachine1 extends StateMachine {
         StateMachine1(String name) {
             super(name);
             mThisSm = this;
@@ -408,12 +419,17 @@
             if (DBG) Log.d(TAG, "StateMachine1: ctor X");
         }
 
-        class S1 extends HierarchicalState {
-            @Override protected void enter() {
+        class S1 extends State {
+            @Override
+            public void enter() {
                 mEnterCount++;
             }
-
-            @Override protected boolean processMessage(Message message) {
+            @Override
+            public void exit() {
+                mExitCount++;
+            }
+            @Override
+            public boolean processMessage(Message message) {
                 if (message.what == TEST_CMD_1) {
                     assertEquals(1, mEnterCount);
                     assertEquals(0, mExitCount);
@@ -425,10 +441,6 @@
                 }
                 return HANDLED;
             }
-
-            @Override protected void exit() {
-                mExitCount++;
-            }
         }
 
         @Override
@@ -469,13 +481,13 @@
 
         assertTrue(sm1.getProcessedMessagesSize() == 2);
 
-        ProcessedMessages.Info pmi;
-        pmi = sm1.getProcessedMessage(0);
+        ProcessedMessageInfo pmi;
+        pmi = sm1.getProcessedMessageInfo(0);
         assertEquals(TEST_CMD_1, pmi.getWhat());
         assertEquals(sm1.mS1, pmi.getState());
         assertEquals(sm1.mS1, pmi.getOriginalState());
 
-        pmi = sm1.getProcessedMessage(1);
+        pmi = sm1.getProcessedMessageInfo(1);
         assertEquals(TEST_CMD_2, pmi.getWhat());
         assertEquals(sm1.mS1, pmi.getState());
         assertEquals(sm1.mS1, pmi.getOriginalState());
@@ -493,7 +505,7 @@
      * mS2 then receives both of the deferred messages first TEST_CMD_1 and
      * then TEST_CMD_2.
      */
-    class StateMachine2 extends HierarchicalStateMachine {
+    class StateMachine2 extends StateMachine {
         StateMachine2(String name) {
             super(name);
             mThisSm = this;
@@ -508,26 +520,28 @@
             if (DBG) Log.d(TAG, "StateMachine2: ctor X");
         }
 
-        class S1 extends HierarchicalState {
-            @Override protected void enter() {
+        class S1 extends State {
+            @Override
+            public void enter() {
                 mDidEnter = true;
             }
-
-            @Override protected boolean processMessage(Message message) {
+            @Override
+            public void exit() {
+                mDidExit = true;
+            }
+            @Override
+            public boolean processMessage(Message message) {
                 deferMessage(message);
                 if (message.what == TEST_CMD_2) {
                     transitionTo(mS2);
                 }
                 return HANDLED;
             }
-
-            @Override protected void exit() {
-                mDidExit = true;
-            }
         }
 
-        class S2 extends HierarchicalState {
-            @Override protected boolean processMessage(Message message) {
+        class S2 extends State {
+            @Override
+            public boolean processMessage(Message message) {
                 if (message.what == TEST_CMD_2) {
                     transitionToHaltingState();
                 }
@@ -571,20 +585,20 @@
 
         assertTrue(sm2.getProcessedMessagesSize() == 4);
 
-        ProcessedMessages.Info pmi;
-        pmi = sm2.getProcessedMessage(0);
+        ProcessedMessageInfo pmi;
+        pmi = sm2.getProcessedMessageInfo(0);
         assertEquals(TEST_CMD_1, pmi.getWhat());
         assertEquals(sm2.mS1, pmi.getState());
 
-        pmi = sm2.getProcessedMessage(1);
+        pmi = sm2.getProcessedMessageInfo(1);
         assertEquals(TEST_CMD_2, pmi.getWhat());
         assertEquals(sm2.mS1, pmi.getState());
 
-        pmi = sm2.getProcessedMessage(2);
+        pmi = sm2.getProcessedMessageInfo(2);
         assertEquals(TEST_CMD_1, pmi.getWhat());
         assertEquals(sm2.mS2, pmi.getState());
 
-        pmi = sm2.getProcessedMessage(3);
+        pmi = sm2.getProcessedMessageInfo(3);
         assertEquals(TEST_CMD_2, pmi.getWhat());
         assertEquals(sm2.mS2, pmi.getState());
 
@@ -598,7 +612,7 @@
      * Test that unhandled messages in a child are handled by the parent.
      * When TEST_CMD_2 is received.
      */
-    class StateMachine3 extends HierarchicalStateMachine {
+    class StateMachine3 extends StateMachine {
         StateMachine3(String name) {
             super(name);
             mThisSm = this;
@@ -615,8 +629,9 @@
             if (DBG) Log.d(TAG, "StateMachine3: ctor X");
         }
 
-        class ParentState extends HierarchicalState {
-            @Override protected boolean processMessage(Message message) {
+        class ParentState extends State {
+            @Override
+            public boolean processMessage(Message message) {
                 if (message.what == TEST_CMD_2) {
                     transitionToHaltingState();
                 }
@@ -624,8 +639,9 @@
             }
         }
 
-        class ChildState extends HierarchicalState {
-            @Override protected boolean processMessage(Message message) {
+        class ChildState extends State {
+            @Override
+            public boolean processMessage(Message message) {
                 return NOT_HANDLED;
             }
         }
@@ -663,13 +679,13 @@
 
         assertTrue(sm3.getProcessedMessagesSize() == 2);
 
-        ProcessedMessages.Info pmi;
-        pmi = sm3.getProcessedMessage(0);
+        ProcessedMessageInfo pmi;
+        pmi = sm3.getProcessedMessageInfo(0);
         assertEquals(TEST_CMD_1, pmi.getWhat());
         assertEquals(sm3.mParentState, pmi.getState());
         assertEquals(sm3.mChildState, pmi.getOriginalState());
 
-        pmi = sm3.getProcessedMessage(1);
+        pmi = sm3.getProcessedMessageInfo(1);
         assertEquals(TEST_CMD_2, pmi.getWhat());
         assertEquals(sm3.mParentState, pmi.getState());
         assertEquals(sm3.mChildState, pmi.getOriginalState());
@@ -682,7 +698,7 @@
      * with transition from child 1 to child 2 and child 2
      * lets the parent handle the messages.
      */
-    class StateMachine4 extends HierarchicalStateMachine {
+    class StateMachine4 extends StateMachine {
         StateMachine4(String name) {
             super(name);
             mThisSm = this;
@@ -700,8 +716,9 @@
             if (DBG) Log.d(TAG, "StateMachine4: ctor X");
         }
 
-        class ParentState extends HierarchicalState {
-            @Override protected boolean processMessage(Message message) {
+        class ParentState extends State {
+            @Override
+            public boolean processMessage(Message message) {
                 if (message.what == TEST_CMD_2) {
                     transitionToHaltingState();
                 }
@@ -709,15 +726,17 @@
             }
         }
 
-        class ChildState1 extends HierarchicalState {
-            @Override protected boolean processMessage(Message message) {
+        class ChildState1 extends State {
+            @Override
+            public boolean processMessage(Message message) {
                 transitionTo(mChildState2);
                 return HANDLED;
             }
         }
 
-        class ChildState2 extends HierarchicalState {
-            @Override protected boolean processMessage(Message message) {
+        class ChildState2 extends State {
+            @Override
+            public boolean processMessage(Message message) {
                 return NOT_HANDLED;
             }
         }
@@ -757,13 +776,13 @@
 
         assertTrue(sm4.getProcessedMessagesSize() == 2);
 
-        ProcessedMessages.Info pmi;
-        pmi = sm4.getProcessedMessage(0);
+        ProcessedMessageInfo pmi;
+        pmi = sm4.getProcessedMessageInfo(0);
         assertEquals(TEST_CMD_1, pmi.getWhat());
         assertEquals(sm4.mChildState1, pmi.getState());
         assertEquals(sm4.mChildState1, pmi.getOriginalState());
 
-        pmi = sm4.getProcessedMessage(1);
+        pmi = sm4.getProcessedMessageInfo(1);
         assertEquals(TEST_CMD_2, pmi.getWhat());
         assertEquals(sm4.mParentState, pmi.getState());
         assertEquals(sm4.mChildState2, pmi.getOriginalState());
@@ -775,7 +794,7 @@
      * Test transition from one child to another of a "complex"
      * hierarchy with two parents and multiple children.
      */
-    class StateMachine5 extends HierarchicalStateMachine {
+    class StateMachine5 extends StateMachine {
         StateMachine5(String name) {
             super(name);
             mThisSm = this;
@@ -797,23 +816,32 @@
             if (DBG) Log.d(TAG, "StateMachine5: ctor X");
         }
 
-        class ParentState1 extends HierarchicalState {
-            @Override protected void enter() {
+        class ParentState1 extends State {
+            @Override
+            public void enter() {
                 mParentState1EnterCount += 1;
             }
-            @Override protected boolean processMessage(Message message) {
-                return HANDLED;
-            }
-            @Override protected void exit() {
+            @Override
+            public void exit() {
                 mParentState1ExitCount += 1;
             }
+            @Override
+            public boolean processMessage(Message message) {
+                return HANDLED;
+            }
         }
 
-        class ChildState1 extends HierarchicalState {
-            @Override protected void enter() {
+        class ChildState1 extends State {
+            @Override
+            public void enter() {
                 mChildState1EnterCount += 1;
             }
-            @Override protected boolean processMessage(Message message) {
+            @Override
+            public void exit() {
+                mChildState1ExitCount += 1;
+            }
+            @Override
+            public boolean processMessage(Message message) {
                 assertEquals(1, mParentState1EnterCount);
                 assertEquals(0, mParentState1ExitCount);
                 assertEquals(1, mChildState1EnterCount);
@@ -832,16 +860,19 @@
                 transitionTo(mChildState2);
                 return HANDLED;
             }
-            @Override protected void exit() {
-                mChildState1ExitCount += 1;
-            }
         }
 
-        class ChildState2 extends HierarchicalState {
-            @Override protected void enter() {
+        class ChildState2 extends State {
+            @Override
+            public void enter() {
                 mChildState2EnterCount += 1;
             }
-            @Override protected boolean processMessage(Message message) {
+            @Override
+            public void exit() {
+                mChildState2ExitCount += 1;
+            }
+            @Override
+            public boolean processMessage(Message message) {
                 assertEquals(1, mParentState1EnterCount);
                 assertEquals(0, mParentState1ExitCount);
                 assertEquals(1, mChildState1EnterCount);
@@ -860,16 +891,19 @@
                 transitionTo(mChildState5);
                 return HANDLED;
             }
-            @Override protected void exit() {
-                mChildState2ExitCount += 1;
-            }
         }
 
-        class ParentState2 extends HierarchicalState {
-            @Override protected void enter() {
+        class ParentState2 extends State {
+            @Override
+            public void enter() {
                 mParentState2EnterCount += 1;
             }
-            @Override protected boolean processMessage(Message message) {
+            @Override
+            public void exit() {
+                mParentState2ExitCount += 1;
+            }
+            @Override
+            public boolean processMessage(Message message) {
                 assertEquals(1, mParentState1EnterCount);
                 assertEquals(1, mParentState1ExitCount);
                 assertEquals(1, mChildState1EnterCount);
@@ -888,16 +922,19 @@
                 transitionToHaltingState();
                 return HANDLED;
             }
-            @Override protected void exit() {
-                mParentState2ExitCount += 1;
-            }
         }
 
-        class ChildState3 extends HierarchicalState {
-            @Override protected void enter() {
+        class ChildState3 extends State {
+            @Override
+            public void enter() {
                 mChildState3EnterCount += 1;
             }
-            @Override protected boolean processMessage(Message message) {
+            @Override
+            public void exit() {
+                mChildState3ExitCount += 1;
+            }
+            @Override
+            public boolean processMessage(Message message) {
                 assertEquals(1, mParentState1EnterCount);
                 assertEquals(1, mParentState1ExitCount);
                 assertEquals(1, mChildState1EnterCount);
@@ -916,16 +953,19 @@
                 transitionTo(mChildState4);
                 return HANDLED;
             }
-            @Override protected void exit() {
-                mChildState3ExitCount += 1;
-            }
         }
 
-        class ChildState4 extends HierarchicalState {
-            @Override protected void enter() {
+        class ChildState4 extends State {
+            @Override
+            public void enter() {
                 mChildState4EnterCount += 1;
             }
-            @Override protected boolean processMessage(Message message) {
+            @Override
+            public void exit() {
+                mChildState4ExitCount += 1;
+            }
+            @Override
+            public boolean processMessage(Message message) {
                 assertEquals(1, mParentState1EnterCount);
                 assertEquals(1, mParentState1ExitCount);
                 assertEquals(1, mChildState1EnterCount);
@@ -944,16 +984,19 @@
                 transitionTo(mParentState2);
                 return HANDLED;
             }
-            @Override protected void exit() {
-                mChildState4ExitCount += 1;
-            }
         }
 
-        class ChildState5 extends HierarchicalState {
-            @Override protected void enter() {
+        class ChildState5 extends State {
+            @Override
+            public void enter() {
                 mChildState5EnterCount += 1;
             }
-            @Override protected boolean processMessage(Message message) {
+            @Override
+            public void exit() {
+                mChildState5ExitCount += 1;
+            }
+            @Override
+            public boolean processMessage(Message message) {
                 assertEquals(1, mParentState1EnterCount);
                 assertEquals(1, mParentState1ExitCount);
                 assertEquals(1, mChildState1EnterCount);
@@ -972,9 +1015,6 @@
                 transitionTo(mChildState3);
                 return HANDLED;
             }
-            @Override protected void exit() {
-                mChildState5ExitCount += 1;
-            }
         }
 
         @Override
@@ -1050,33 +1090,33 @@
         assertEquals(1, sm5.mChildState5EnterCount);
         assertEquals(1, sm5.mChildState5ExitCount);
 
-        ProcessedMessages.Info pmi;
-        pmi = sm5.getProcessedMessage(0);
+        ProcessedMessageInfo pmi;
+        pmi = sm5.getProcessedMessageInfo(0);
         assertEquals(TEST_CMD_1, pmi.getWhat());
         assertEquals(sm5.mChildState1, pmi.getState());
         assertEquals(sm5.mChildState1, pmi.getOriginalState());
 
-        pmi = sm5.getProcessedMessage(1);
+        pmi = sm5.getProcessedMessageInfo(1);
         assertEquals(TEST_CMD_2, pmi.getWhat());
         assertEquals(sm5.mChildState2, pmi.getState());
         assertEquals(sm5.mChildState2, pmi.getOriginalState());
 
-        pmi = sm5.getProcessedMessage(2);
+        pmi = sm5.getProcessedMessageInfo(2);
         assertEquals(TEST_CMD_3, pmi.getWhat());
         assertEquals(sm5.mChildState5, pmi.getState());
         assertEquals(sm5.mChildState5, pmi.getOriginalState());
 
-        pmi = sm5.getProcessedMessage(3);
+        pmi = sm5.getProcessedMessageInfo(3);
         assertEquals(TEST_CMD_4, pmi.getWhat());
         assertEquals(sm5.mChildState3, pmi.getState());
         assertEquals(sm5.mChildState3, pmi.getOriginalState());
 
-        pmi = sm5.getProcessedMessage(4);
+        pmi = sm5.getProcessedMessageInfo(4);
         assertEquals(TEST_CMD_5, pmi.getWhat());
         assertEquals(sm5.mChildState4, pmi.getState());
         assertEquals(sm5.mChildState4, pmi.getOriginalState());
 
-        pmi = sm5.getProcessedMessage(5);
+        pmi = sm5.getProcessedMessageInfo(5);
         assertEquals(TEST_CMD_6, pmi.getWhat());
         assertEquals(sm5.mParentState2, pmi.getState());
         assertEquals(sm5.mParentState2, pmi.getOriginalState());
@@ -1089,7 +1129,7 @@
      * after construction and before any other messages arrive and that
      * sendMessageDelayed works.
      */
-    class StateMachine6 extends HierarchicalStateMachine {
+    class StateMachine6 extends StateMachine {
         StateMachine6(String name) {
             super(name);
             mThisSm = this;
@@ -1103,13 +1143,13 @@
             if (DBG) Log.d(TAG, "StateMachine6: ctor X");
         }
 
-        class S1 extends HierarchicalState {
-
-            @Override protected void enter() {
+        class S1 extends State {
+            @Override
+            public void enter() {
                 sendMessage(TEST_CMD_1);
             }
-
-            @Override protected boolean processMessage(Message message) {
+            @Override
+            public boolean processMessage(Message message) {
                 if (message.what == TEST_CMD_1) {
                     mArrivalTimeMsg1 = SystemClock.elapsedRealtime();
                 } else if (message.what == TEST_CMD_2) {
@@ -1118,9 +1158,6 @@
                 }
                 return HANDLED;
             }
-
-            @Override protected void exit() {
-            }
         }
 
         @Override
@@ -1178,7 +1215,7 @@
      * Test that enter is invoked immediately after exit. This validates
      * that enter can be used to send a watch dog message for its state.
      */
-    class StateMachine7 extends HierarchicalStateMachine {
+    class StateMachine7 extends StateMachine {
         private final int SM7_DELAY_TIME = 250;
 
         StateMachine7(String name) {
@@ -1195,24 +1232,26 @@
             if (DBG) Log.d(TAG, "StateMachine7: ctor X");
         }
 
-        class S1 extends HierarchicalState {
-            @Override protected boolean processMessage(Message message) {
+        class S1 extends State {
+            @Override
+            public void exit() {
+                sendMessage(TEST_CMD_2);
+            }
+            @Override
+            public boolean processMessage(Message message) {
                 transitionTo(mS2);
                 return HANDLED;
             }
-            @Override protected void exit() {
-                sendMessage(TEST_CMD_2);
-            }
         }
 
-        class S2 extends HierarchicalState {
-
-            @Override protected void enter() {
+        class S2 extends State {
+            @Override
+            public void enter() {
                 // Send a delayed message as a watch dog
                 sendMessageDelayed(TEST_CMD_3, SM7_DELAY_TIME);
             }
-
-            @Override protected boolean processMessage(Message message) {
+            @Override
+            public boolean processMessage(Message message) {
                 if (message.what == TEST_CMD_2) {
                     mMsgCount += 1;
                     mArrivalTimeMsg2 = SystemClock.elapsedRealtime();
@@ -1226,9 +1265,6 @@
                 }
                 return HANDLED;
             }
-
-            @Override protected void exit() {
-            }
         }
 
         @Override
@@ -1286,7 +1322,7 @@
     /**
      * Test unhandledMessage.
      */
-    class StateMachineUnhandledMessage extends HierarchicalStateMachine {
+    class StateMachineUnhandledMessage extends StateMachine {
         StateMachineUnhandledMessage(String name) {
             super(name);
             mThisSm = this;
@@ -1298,13 +1334,14 @@
             // Set the initial state
             setInitialState(mS1);
         }
-
-        @Override protected void unhandledMessage(Message message) {
+        @Override
+        public void unhandledMessage(Message message) {
             mUnhandledMessageCount += 1;
         }
 
-        class S1 extends HierarchicalState {
-            @Override protected boolean processMessage(Message message) {
+        class S1 extends State {
+            @Override
+            public boolean processMessage(Message message) {
                 if (message.what == TEST_CMD_2) {
                     transitionToHaltingState();
                 }
@@ -1359,7 +1396,7 @@
      * will be used to notify testStateMachineSharedThread that the test is
      * complete.
      */
-    class StateMachineSharedThread extends HierarchicalStateMachine {
+    class StateMachineSharedThread extends StateMachine {
         StateMachineSharedThread(String name, Looper looper, int maxCount) {
             super(name, looper);
             mMaxCount = maxCount;
@@ -1372,8 +1409,9 @@
             setInitialState(mS1);
         }
 
-        class S1 extends HierarchicalState {
-            @Override protected boolean processMessage(Message message) {
+        class S1 extends State {
+            @Override
+            public boolean processMessage(Message message) {
                 if (message.what == TEST_CMD_4) {
                     transitionToHaltingState();
                 }
@@ -1434,7 +1472,7 @@
         for (StateMachineSharedThread sm : sms) {
             assertTrue(sm.getProcessedMessagesCount() == 4);
             for (int i = 0; i < sm.getProcessedMessagesCount(); i++) {
-                ProcessedMessages.Info pmi = sm.getProcessedMessage(i);
+                ProcessedMessageInfo pmi = sm.getProcessedMessageInfo(i);
                 assertEquals(i+1, pmi.getWhat());
                 assertEquals(sm.mS1, pmi.getState());
                 assertEquals(sm.mS1, pmi.getOriginalState());
@@ -1464,37 +1502,37 @@
         }
 
         assertEquals(7, sm.getProcessedMessagesCount());
-        ProcessedMessages.Info pmi = sm.getProcessedMessage(0);
+        ProcessedMessageInfo pmi = sm.getProcessedMessageInfo(0);
         assertEquals(Hsm1.CMD_1, pmi.getWhat());
         assertEquals(sm.mS1, pmi.getState());
         assertEquals(sm.mS1, pmi.getOriginalState());
 
-        pmi = sm.getProcessedMessage(1);
+        pmi = sm.getProcessedMessageInfo(1);
         assertEquals(Hsm1.CMD_2, pmi.getWhat());
         assertEquals(sm.mP1, pmi.getState());
         assertEquals(sm.mS1, pmi.getOriginalState());
 
-        pmi = sm.getProcessedMessage(2);
+        pmi = sm.getProcessedMessageInfo(2);
         assertEquals(Hsm1.CMD_2, pmi.getWhat());
         assertEquals(sm.mS2, pmi.getState());
         assertEquals(sm.mS2, pmi.getOriginalState());
 
-        pmi = sm.getProcessedMessage(3);
+        pmi = sm.getProcessedMessageInfo(3);
         assertEquals(Hsm1.CMD_3, pmi.getWhat());
         assertEquals(sm.mS2, pmi.getState());
         assertEquals(sm.mS2, pmi.getOriginalState());
 
-        pmi = sm.getProcessedMessage(4);
+        pmi = sm.getProcessedMessageInfo(4);
         assertEquals(Hsm1.CMD_3, pmi.getWhat());
         assertEquals(sm.mP2, pmi.getState());
         assertEquals(sm.mP2, pmi.getOriginalState());
 
-        pmi = sm.getProcessedMessage(5);
+        pmi = sm.getProcessedMessageInfo(5);
         assertEquals(Hsm1.CMD_4, pmi.getWhat());
         assertEquals(sm.mP2, pmi.getState());
         assertEquals(sm.mP2, pmi.getOriginalState());
 
-        pmi = sm.getProcessedMessage(6);
+        pmi = sm.getProcessedMessageInfo(6);
         assertEquals(Hsm1.CMD_5, pmi.getWhat());
         assertEquals(sm.mP2, pmi.getState());
         assertEquals(sm.mP2, pmi.getOriginalState());
@@ -1503,7 +1541,7 @@
     }
 }
 
-class Hsm1 extends HierarchicalStateMachine {
+class Hsm1 extends StateMachine {
     private static final String TAG = "hsm1";
 
     public static final int CMD_1 = 1;
@@ -1535,11 +1573,17 @@
         Log.d(TAG, "ctor X");
     }
 
-    class P1 extends HierarchicalState {
-        @Override protected void enter() {
+    class P1 extends State {
+        @Override
+        public void enter() {
             Log.d(TAG, "P1.enter");
         }
-        @Override protected boolean processMessage(Message message) {
+        @Override
+        public void exit() {
+            Log.d(TAG, "P1.exit");
+        }
+        @Override
+        public boolean processMessage(Message message) {
             boolean retVal;
             Log.d(TAG, "P1.processMessage what=" + message.what);
             switch(message.what) {
@@ -1557,16 +1601,19 @@
             }
             return retVal;
         }
-        @Override protected void exit() {
-            Log.d(TAG, "P1.exit");
-        }
     }
 
-    class S1 extends HierarchicalState {
-        @Override protected void enter() {
+    class S1 extends State {
+        @Override
+        public void enter() {
             Log.d(TAG, "S1.enter");
         }
-        @Override protected boolean processMessage(Message message) {
+        @Override
+        public void exit() {
+            Log.d(TAG, "S1.exit");
+        }
+        @Override
+        public boolean processMessage(Message message) {
             Log.d(TAG, "S1.processMessage what=" + message.what);
             if (message.what == CMD_1) {
                 // Transition to ourself to show that enter/exit is called
@@ -1577,16 +1624,19 @@
                 return NOT_HANDLED;
             }
         }
-        @Override protected void exit() {
-            Log.d(TAG, "S1.exit");
-        }
     }
 
-    class S2 extends HierarchicalState {
-        @Override protected void enter() {
+    class S2 extends State {
+        @Override
+        public void enter() {
             Log.d(TAG, "S2.enter");
         }
-        @Override protected boolean processMessage(Message message) {
+        @Override
+        public void exit() {
+            Log.d(TAG, "S2.exit");
+        }
+        @Override
+        public boolean processMessage(Message message) {
             boolean retVal;
             Log.d(TAG, "S2.processMessage what=" + message.what);
             switch(message.what) {
@@ -1605,17 +1655,20 @@
             }
             return retVal;
         }
-        @Override protected void exit() {
-            Log.d(TAG, "S2.exit");
-        }
     }
 
-    class P2 extends HierarchicalState {
-        @Override protected void enter() {
+    class P2 extends State {
+        @Override
+        public void enter() {
             Log.d(TAG, "P2.enter");
             sendMessage(CMD_5);
         }
-        @Override protected boolean processMessage(Message message) {
+        @Override
+        public void exit() {
+            Log.d(TAG, "P2.exit");
+        }
+        @Override
+        public boolean processMessage(Message message) {
             Log.d(TAG, "P2.processMessage what=" + message.what);
             switch(message.what) {
             case(CMD_3):
@@ -1628,9 +1681,6 @@
             }
             return HANDLED;
         }
-        @Override protected void exit() {
-            Log.d(TAG, "P2.exit");
-        }
     }
 
     @Override
diff --git a/core/tests/overlaytests/Android.mk b/core/tests/overlaytests/Android.mk
new file mode 100644
index 0000000..bf69442
--- /dev/null
+++ b/core/tests/overlaytests/Android.mk
@@ -0,0 +1,4 @@
+# Dummy makefile to halt recursive directory traversal.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
diff --git a/core/tests/overlaytests/OverlayTest/Android.mk b/core/tests/overlaytests/OverlayTest/Android.mk
new file mode 100644
index 0000000..f7f67f6
--- /dev/null
+++ b/core/tests/overlaytests/OverlayTest/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_PACKAGE_NAME := OverlayTest
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/OverlayTest/AndroidManifest.xml b/core/tests/overlaytests/OverlayTest/AndroidManifest.xml
new file mode 100644
index 0000000..9edba12
--- /dev/null
+++ b/core/tests/overlaytests/OverlayTest/AndroidManifest.xml
@@ -0,0 +1,10 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.overlaytest">
+    <uses-permission android:name="android.permission.RUN_INSTRUMENTATION"/>
+    <application>
+        <uses-library android:name="android.test.runner"/>
+    </application>
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+        android:targetPackage="com.android.overlaytest"
+        android:label="Runtime resource overlay tests"/>
+</manifest>
diff --git a/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java
new file mode 100644
index 0000000..85b49ce
--- /dev/null
+++ b/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java
@@ -0,0 +1,118 @@
+package com.android.overlaytest;
+
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.test.AndroidTestCase;
+import java.io.InputStream;
+import java.util.Locale;
+
+public abstract class OverlayBaseTest extends AndroidTestCase {
+    private Resources mResources;
+    protected boolean mWithOverlay; // will be set by subclasses
+
+    protected void setUp() {
+        mResources = getContext().getResources();
+    }
+
+    private int calculateRawResourceChecksum(int resId) throws Throwable {
+        InputStream input = null;
+        try {
+            input = mResources.openRawResource(resId);
+            int ch, checksum = 0;
+            while ((ch = input.read()) != -1) {
+                checksum = (checksum + ch) % 0xffddbb00;
+            }
+            return checksum;
+        } finally {
+            input.close();
+        }
+    }
+
+    private void setLocale(String code) {
+        Locale locale = new Locale(code);
+        Locale.setDefault(locale);
+        Configuration config = new Configuration();
+        config.locale = locale;
+        mResources.updateConfiguration(config, mResources.getDisplayMetrics());
+    }
+
+    private void assertResource(int resId, boolean ewo, boolean ew) throws Throwable {
+        boolean expected = mWithOverlay ? ew : ewo;
+        boolean actual = mResources.getBoolean(resId);
+        assertEquals(expected, actual);
+    }
+
+    private void assertResource(int resId, String ewo, String ew) throws Throwable {
+        String expected = mWithOverlay ? ew : ewo;
+        String actual = mResources.getString(resId);
+        assertEquals(expected, actual);
+    }
+
+    private void assertResource(int resId, int[] ewo, int[] ew) throws Throwable {
+        int[] expected = mWithOverlay ? ew : ewo;
+        int[] actual = mResources.getIntArray(resId);
+        assertEquals("length:", expected.length, actual.length);
+        for (int i = 0; i < actual.length; ++i) {
+            assertEquals("index " + i + ":", actual[i], expected[i]);
+        }
+    }
+
+    public void testBooleanOverlay() throws Throwable {
+        // config_automatic_brightness_available has overlay (default config)
+        final int resId = com.android.internal.R.bool.config_automatic_brightness_available;
+        assertResource(resId, false, true);
+    }
+
+    public void testBoolean() throws Throwable {
+        // config_bypass_keyguard_if_slider_open has no overlay
+        final int resId = com.android.internal.R.bool.config_bypass_keyguard_if_slider_open;
+        assertResource(resId, true, true);
+    }
+
+    public void testStringOverlay() throws Throwable {
+        // phoneTypeCar has an overlay (default config), which shouldn't shadow
+        // the Swedish translation
+        final int resId = com.android.internal.R.string.phoneTypeCar;
+        setLocale("sv_SE");
+        assertResource(resId, "Bil", "Bil");
+    }
+
+    public void testStringSwedishOverlay() throws Throwable {
+        // phoneTypeWork has overlay (no default config, only for lang=sv)
+        final int resId = com.android.internal.R.string.phoneTypeWork;
+        setLocale("en_US");
+        assertResource(resId, "Work", "Work");
+        setLocale("sv_SE");
+        assertResource(resId, "Arbete", "Jobb");
+    }
+
+    public void testString() throws Throwable {
+        // phoneTypeHome has no overlay
+        final int resId = com.android.internal.R.string.phoneTypeHome;
+        setLocale("en_US");
+        assertResource(resId, "Home", "Home");
+        setLocale("sv_SE");
+        assertResource(resId, "Hem", "Hem");
+    }
+
+    public void testIntegerArrayOverlay() throws Throwable {
+        // config_scrollBarrierVibePattern has overlay (default config)
+        final int resId = com.android.internal.R.array.config_scrollBarrierVibePattern;
+        assertResource(resId, new int[]{0, 15, 10, 10}, new int[]{100, 200, 300});
+    }
+
+    public void testIntegerArray() throws Throwable {
+        // config_virtualKeyVibePattern has no overlay
+        final int resId = com.android.internal.R.array.config_virtualKeyVibePattern;
+        final int[] expected = {0, 10, 20, 30};
+        assertResource(resId, expected, expected);
+    }
+
+    public void testAsset() throws Throwable {
+        // drawable/default_background.jpg has overlay (default config)
+        final int resId = com.android.internal.R.drawable.default_wallpaper;
+        int actual = calculateRawResourceChecksum(resId);
+        int expected = mWithOverlay ? 0x000051da : 0x0014ebce;
+        assertEquals(expected, actual);
+    }
+}
diff --git a/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/WithOverlayTest.java
new file mode 100644
index 0000000..1292d03
--- /dev/null
+++ b/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/WithOverlayTest.java
@@ -0,0 +1,7 @@
+package com.android.overlaytest;
+
+public class WithOverlayTest extends OverlayBaseTest {
+    public WithOverlayTest() {
+        mWithOverlay = true;
+    }
+}
diff --git a/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/WithoutOverlayTest.java
new file mode 100644
index 0000000..630ff8f
--- /dev/null
+++ b/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/WithoutOverlayTest.java
@@ -0,0 +1,7 @@
+package com.android.overlaytest;
+
+public class WithoutOverlayTest extends OverlayBaseTest {
+    public WithoutOverlayTest() {
+        mWithOverlay = false;
+    }
+}
diff --git a/core/tests/overlaytests/OverlayTestOverlay/Android.mk b/core/tests/overlaytests/OverlayTestOverlay/Android.mk
new file mode 100644
index 0000000..cf32c9f
--- /dev/null
+++ b/core/tests/overlaytests/OverlayTestOverlay/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := com.android.overlaytest.overlay
+
+LOCAL_AAPT_FLAGS := -o
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/OverlayTestOverlay/AndroidManifest.xml b/core/tests/overlaytests/OverlayTestOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..bcbb0d1
--- /dev/null
+++ b/core/tests/overlaytests/OverlayTestOverlay/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.overlaytest.overlay"
+        android:versionCode="1"
+        android:versionName="1.0">
+    <overlay-package android:name="android"/>
+</manifest>
diff --git a/core/tests/overlaytests/OverlayTestOverlay/res/drawable/default_wallpaper.jpg b/core/tests/overlaytests/OverlayTestOverlay/res/drawable/default_wallpaper.jpg
new file mode 100644
index 0000000..0d944d0
--- /dev/null
+++ b/core/tests/overlaytests/OverlayTestOverlay/res/drawable/default_wallpaper.jpg
Binary files differ
diff --git a/core/tests/overlaytests/OverlayTestOverlay/res/values-sv/config.xml b/core/tests/overlaytests/OverlayTestOverlay/res/values-sv/config.xml
new file mode 100644
index 0000000..bc52367
--- /dev/null
+++ b/core/tests/overlaytests/OverlayTestOverlay/res/values-sv/config.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="phoneTypeWork">Jobb</string>
+</resources>
diff --git a/core/tests/overlaytests/OverlayTestOverlay/res/values/config.xml b/core/tests/overlaytests/OverlayTestOverlay/res/values/config.xml
new file mode 100644
index 0000000..794f475
--- /dev/null
+++ b/core/tests/overlaytests/OverlayTestOverlay/res/values/config.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <bool name="config_automatic_brightness_available">true</bool>
+    <string name="phoneTypeCar">Automobile</string>
+    <integer-array name="config_scrollBarrierVibePattern">
+        <item>100</item>
+        <item>200</item>
+        <item>300</item>
+    </integer-array>
+    <!-- The following integer does not exist in the original package. Idmap
+         generation should therefore ignore it. -->
+    <integer name="integer_not_in_original_package">0</integer>
+</resources>
diff --git a/core/tests/overlaytests/README b/core/tests/overlaytests/README
new file mode 100644
index 0000000..4b3e6f2
--- /dev/null
+++ b/core/tests/overlaytests/README
@@ -0,0 +1,15 @@
+Unit tests for runtime resource overlay
+=======================================
+
+As of this writing, runtime resource overlay is only triggered for
+/system/framework/framework-res.apk. Because of this, installation of
+overlay packages require the Android platform be rebooted. However, the
+regular unit tests (triggered via development/testrunner/runtest.py)
+cannot handle reboots. As a workaround, this directory contains a shell
+script which will trigger the tests in a non-standard way.
+
+Once runtime resource overlay may be applied to applications, the tests
+in this directory should be moved to core/tests/coretests. Also, by
+applying runtime resource overlay to a dedicated test application, the
+test cases would not need to assume default values for non-overlaid
+resources.
diff --git a/core/tests/overlaytests/runtests.sh b/core/tests/overlaytests/runtests.sh
new file mode 100755
index 0000000..0ad9efb
--- /dev/null
+++ b/core/tests/overlaytests/runtests.sh
@@ -0,0 +1,89 @@
+#!/bin/bash
+
+adb="adb"
+if [[ $# -gt 0 ]]; then
+	adb="adb $*" # for setting -e, -d or -s <serial>
+fi
+
+function atexit()
+{
+	local retval=$?
+
+	if [[ $retval -eq 0 ]]; then
+		rm $log
+	else
+		echo "There were errors, please check log at $log"
+	fi
+}
+
+log=$(mktemp)
+trap "atexit" EXIT
+failures=0
+
+function compile_module()
+{
+	local android_mk="$1"
+
+	echo "Compiling .${android_mk:${#PWD}}"
+	ONE_SHOT_MAKEFILE="$android_mk" make -C "../../../../../" files | tee -a $log
+	if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
+		exit 1
+	fi
+}
+
+function wait_for_boot_completed()
+{
+	echo "Rebooting device"
+	$adb wait-for-device logcat -c
+	$adb wait-for-device logcat | grep -m 1 -e 'PowerManagerService.*bootCompleted' >/dev/null
+}
+
+function disable_overlay()
+{
+	echo "Disabling overlay"
+	$adb shell rm /vendor/overlay/framework/framework-res.apk
+	$adb shell rm /data/resource-cache/vendor@overlay@framework@framework-res.apk@idmap
+}
+
+function enable_overlay()
+{
+	echo "Enabling overlay"
+	$adb shell ln -s /data/app/com.android.overlaytest.overlay.apk /vendor/overlay/framework/framework-res.apk
+}
+
+function instrument()
+{
+	local class="$1"
+
+	echo "Instrumenting $class"
+	$adb shell am instrument -w -e class $class com.android.overlaytest/android.test.InstrumentationTestRunner | tee -a $log
+}
+
+function sync()
+{
+	echo "Syncing to device"
+	$adb remount | tee -a $log
+	$adb sync data | tee -a $log
+}
+
+# build and sync
+compile_module "$PWD/OverlayTest/Android.mk"
+compile_module "$PWD/OverlayTestOverlay/Android.mk"
+sync
+
+# instrument test (without overlay)
+$adb shell stop
+disable_overlay
+$adb shell start
+wait_for_boot_completed
+instrument "com.android.overlaytest.WithoutOverlayTest"
+
+# instrument test (with overlay)
+$adb shell stop
+enable_overlay
+$adb shell start
+wait_for_boot_completed
+instrument "com.android.overlaytest.WithOverlayTest"
+
+# cleanup
+exit $(grep -c -e '^FAILURES' $log)
diff --git a/docs/html/guide/developing/tools/logcat.jd b/docs/html/guide/developing/tools/logcat.jd
index d4ee68a..546e3ea 100644
--- a/docs/html/guide/developing/tools/logcat.jd
+++ b/docs/html/guide/developing/tools/logcat.jd
@@ -2,7 +2,6 @@
 parent.title=Tools
 parent.link=index.html
 @jd:body
-<div></div>
 
   <p>The Android logging system provides a mechanism for collecting and viewing system debug
   output. Logs from various applications and portions of the system are collected in a series of
@@ -23,7 +22,7 @@
 
   <p>You can run <code>logcat</code> as an adb command or directly in a shell prompt
   of your emulator or connected device. To view log output using adb, navigate to your SDK
-  <code>platform-tools/</code> directory and execute:/p>
+  <code>platform-tools/</code> directory and execute:</p>
   <pre>
 $ adb logcat
 </pre>
diff --git a/docs/html/guide/market/billing/billing_admin.jd b/docs/html/guide/market/billing/billing_admin.jd
index 723113d..939bbaa 100755
--- a/docs/html/guide/market/billing/billing_admin.jd
+++ b/docs/html/guide/market/billing/billing_admin.jd
@@ -97,7 +97,7 @@
 
 <img src="{@docRoot}images/billing_list_form.png" height="840" id="figure3" />
 <p class="img-caption">
-  f<strong>Figure 3.</strong> The Create New In-app Product page lets you add items to an
+  <strong>Figure 3.</strong> The Create New In-app Product page lets you add items to an
   application's product list.
 </p>
 
diff --git a/docs/html/guide/market/billing/billing_integrate.jd b/docs/html/guide/market/billing/billing_integrate.jd
index f57ebe3..59344ba 100755
--- a/docs/html/guide/market/billing/billing_integrate.jd
+++ b/docs/html/guide/market/billing/billing_integrate.jd
@@ -163,11 +163,11 @@
 <p>When the download is complete, the Android SDK and AVD Manager saves the component into the
 following directory:</p>
 
-<p><code>&lt;sdk&gt;/google-market_billing/</code></p>
+<p><code>&lt;sdk&gt;/extras/google/market_billing/</code></p>
 
 <p>If you want to see an end-to-end demonstration of in-app billing before you integrate in-app
 billing into your own application, you can build and run the sample application. Building and
-running the sample application involves three tasks:<p>
+running the sample application involves three tasks:</p>
 
 <ul>
   <li>Configuring and building the sample application.</li>
@@ -501,7 +501,7 @@
 
 <p>To use this helper method, you pass in a <code>String</code> that corresponds to one of the five
 types of billing requests. The method returns a Bundle that has the three required keys defined. The
-following sections show you how to use this helper method when you send a billing request.<p>
+following sections show you how to use this helper method when you send a billing request.</p>
 
 <p class="caution"><strong>Important</strong>: You must make all in-app billing requests from your
 application's main thread.</p>
diff --git a/docs/html/guide/practices/design/accessibility.jd b/docs/html/guide/practices/design/accessibility.jd
index a2b314e..a66a974 100644
--- a/docs/html/guide/practices/design/accessibility.jd
+++ b/docs/html/guide/practices/design/accessibility.jd
@@ -60,8 +60,9 @@
 <li>Make all of your user interface controls accessible with a trackball or directional
 controller (d-pad).</li>
 <li>Label your {@link android.widget.ImageButton}, {@link android.widget.EditText}, and other input
-widgets using the <a href="{@docRoot}reference/android/view/View#attr_android:contentDescription"
->{@code android:contentDescription}</a> attribute.</li>
+widgets using the <a
+href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">{@code
+android:contentDescription}</a> attribute.</li>
 </ul>
 
 
@@ -100,8 +101,9 @@
 </ul>
 
 <p>When working with a view that is not focusable by default, you can make it focusable from the XML
-layout file by setting the <a href="{@docRoot}reference/android/view/View#attr_android:focusable"
->{@code android:focusable}</a> attribute to {@code "true"}.</p>
+layout file by setting the <a
+href="{@docRoot}reference/android/view/View.html#attr_android:focusable">{@code
+android:focusable}</a> attribute to {@code "true"}.</p>
 
 
 
@@ -181,13 +183,13 @@
 application, these visual cues are often useless.</p>
 
 <p>To provide textual information about these widgets (as an alternative to the visual cues), you
-should use the <a href="{@docRoot}reference/android/view/View#attr_android:contentDescription"
+should use the <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription"
 >{@code android:contentDescription}</a> attribute. The text you provide in this attribute
 is not visible on the screen, but if a user has enabled accessibility speech tools then the
 description in this attribute is read aloud to the user.</p>
 
 <p>You should set the <a
-href="{@docRoot}reference/android/view/View#attr_android:contentDescription" >{@code
+href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription" >{@code
 android:contentDescription}</a> attribute on every {@link android.widget.ImageButton}, {@link
 android.widget.EditText}, {@link android.widget.CheckBox}, and on any other input widgets that might
 benefit users with extra information.</p>
diff --git a/docs/html/guide/practices/design/jni.jd b/docs/html/guide/practices/design/jni.jd
new file mode 100644
index 0000000..3e9ddc4
--- /dev/null
+++ b/docs/html/guide/practices/design/jni.jd
@@ -0,0 +1,715 @@
+page.title=JNI Tips
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+<h2>In this document</h2>
+<ol>
+  <li><a href="#what">What is JNI?</a></li>
+  <li><a href="#JavaVM_and_JNIEnv">JavaVM and JNIEnv</a></li>
+  <li><a href="#threads">Threads</a></li>
+  <li><a href="#jclass_jmethodID_and_jfieldID">jclass, jmethodID, and jfieldID</a></li>
+  <li><a href="#local_and_global_references">Local and Global References</a></li>
+  <li><a href="#UTF_8_and_UTF_16_strings">UTF-8 and UTF-16 Strings</a></li>
+  <li><a href="#arrays">Primitive Arrays</a></li>
+  <li><a href="#region_calls">Region Calls</a></li>
+  <li><a href="#exceptions">Exceptions</a></li>
+  <li><a href="#extended_checking">Extended Checking</a> </li>
+  <li><a href="#native_libraries">Native Libraries</a></li>
+  <li><a href="#64_bit">64-bit Considerations</a></li>
+  <li><a href="#unsupported">Unsupported Features</a></li>
+  <li><a href="#faq_ULE">FAQ: UnsatisfiedLinkError</a></li>
+  <li><a href="#faq_FindClass">FAQ: FindClass didn't find my class</a></li>
+  <li><a href="#faq_sharing">FAQ: Sharing raw data with native code</a></li>
+</ol>
+
+</div>
+</div>
+
+<a name="what_is_jni" id="what_is_jni"></a>
+<h2>What is JNI?</h2>
+
+<p>JNI is the Java Native Interface.  It defines a way for code written in the
+Java programming language to interact with native
+code, e.g. functions written in C/C++.  It's VM-neutral, has support for loading code from
+dynamic shared libraries, and while cumbersome at times is reasonably efficient.</p>
+
+<p>You really should read through the
+<a href="http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/jniTOC.html">JNI spec for J2SE 6</a>
+to get a sense for how JNI works and what features are available.  Some
+aspects of the interface aren't immediately obvious on
+first reading, so you may find the next few sections handy.
+The more detailed <i>JNI Programmer's Guide and Specification</i> can be found
+<a href="http://java.sun.com/docs/books/jni/html/jniTOC.html">here</a>.</p>
+
+
+<a name="JavaVM_and_JNIEnv" id="JavaVM_and_JNIEnv"></a>
+<h2>JavaVM and JNIEnv</h2>
+
+<p>JNI defines two key data structures, "JavaVM" and "JNIEnv".  Both of these are essentially
+pointers to pointers to function tables.  (In the C++ version, they're classes with a
+pointer to a function table and a member function for each JNI function that indirects through
+the table.)  The JavaVM provides the "invocation interface" functions,
+which allow you to create and destroy the VM.  In theory you can have multiple VMs per process,
+but Android's VM only allows one.</p>
+
+<p>The JNIEnv provides most of the JNI functions.  Your native functions all receive a JNIEnv as
+the first argument.</p>
+
+<p>On some VMs, the JNIEnv is used for thread-local storage.  For this reason, <strong>you cannot share a JNIEnv between threads</strong>.
+If a piece of code has no other way to get its JNIEnv, you should share
+the JavaVM, and use JavaVM-&gt;GetEnv to discover the thread's JNIEnv. (Assuming it has one; see <code>AttachCurrentThread</code> below.)</p>
+
+<p>The C declarations of JNIEnv and JavaVM are different from the C++
+declarations.  "jni.h" provides different typedefs
+depending on whether it's included into ".c" or ".cpp".  For this reason it's a bad idea to
+include JNIEnv arguments in header files included by both languages.  (Put another way: if your
+header file requires "#ifdef __cplusplus", you may have to do some extra work if anything in
+that header refers to JNIEnv.)</p>
+
+<a name="threads" id="threads"></a>
+<h2>Threads</h2>
+
+<p>All VM threads are Linux threads, scheduled by the kernel.  They're usually
+started using Java language features (notably <code>Thread.start()</code>),
+but they can also be created elsewhere and then attached to the VM.  For
+example, a thread started with <code>pthread_create</code> can be attached
+with the JNI <code>AttachCurrentThread</code> or
+<code>AttachCurrentThreadAsDaemon</code> functions.  Until a thread is
+attached to the VM, it has no JNIEnv, and
+<strong>cannot make JNI calls</strong>.</p>
+
+<p>Attaching a natively-created thread causes the VM to allocate and initialize
+a <code>Thread</code> object, add it to the "main" <code>ThreadGroup</code>,
+and add the thread to the set that is visible to the debugger.  Calling
+<code>AttachCurrentThread</code> on an already-attached thread is a no-op.</p>
+
+<p>The Dalvik VM does not suspend threads executing native code.  If
+garbage collection is in progress, or the debugger has issued a suspend
+request, the VM will pause the thread the next time it makes a JNI call.</p>
+
+<p>Threads attached through JNI <strong>must call
+<code>DetachCurrentThread</code> before they exit</strong>.
+If coding this directly is awkward, in Android &gt;= 2.0 ("Eclair") you
+can use <code>pthread_key_create</code> to define a destructor
+function that will be called before the thread exits, and
+call <code>DetachCurrentThread</code> from there.  (Use that
+key with <code>pthread_setspecific</code> to store the JNIEnv in
+thread-local-storage; that way it'll be passed into your destructor as
+the argument.)</p>
+
+
+<a name="jclass_jmethodID_and_jfieldID" id="jclass_jmethodID_and_jfieldID"></a>
+<h2>jclass, jmethodID, and jfieldID</h2>
+
+<p>If you want to access an object's field from native code, you would do the following:</p>
+
+<ul>
+<li> Get the class object reference for the class with <code>FindClass</code></li>
+<li> Get the field ID for the field with <code>GetFieldID</code></li>
+<li> Get the contents of the field with something appropriate, e.g.
+<code>GetIntField</code></li>
+</ul>
+
+<p>Similarly, to call a method, you'd first get a class object reference and then a method ID.  The IDs are often just
+pointers to internal VM data structures.  Looking them up may require several string
+comparisons, but once you have them the actual call to get the field or invoke the method
+is very quick.</p>
+
+<p>If performance is important, it's useful to look the values up once and cache the results
+in your native code.  Because we are limiting ourselves to one VM per process, it's reasonable
+to store this data in a static local structure.</p>
+
+<p>The class references, field IDs, and method IDs are guaranteed valid until the class is unloaded.  Classes
+are only unloaded if all classes associated with a ClassLoader can be garbage collected,
+which is rare but will not be impossible in our system.  Note however that
+the <code>jclass</code>
+is a class reference and <strong>must be protected</strong> with a call
+to <code>NewGlobalRef</code> (see the next section).</p>
+
+<p>If you would like to cache the IDs when a class is loaded, and automatically re-cache them
+if the class is ever unloaded and reloaded, the correct way to initialize
+the IDs is to add a piece of code that looks like this to the appropriate class:</p>
+
+<pre>    /*
+     * We use a class initializer to allow the native code to cache some
+     * field offsets.
+     */
+
+    /*
+     * A native function that looks up and caches interesting
+     * class/field/method IDs for this class.  Returns false on failure.
+     */
+    native private static boolean nativeClassInit();
+
+    /*
+     * Invoke the native initializer when the class is loaded.
+     */
+    static {
+        if (!nativeClassInit())
+            throw new RuntimeException("native init failed");
+    }</pre>
+
+<p>Create a nativeClassInit method in your C/C++ code that performs the ID lookups.  The code
+will be executed once, when the class is initialized.  If the class is ever unloaded and
+then reloaded, it will be executed again.  (See the implementation of java.io.FileDescriptor
+for an example in our source tree.)</p>
+
+<a name="local_and_global_references" id="local_and_global_references"></a>
+<h2>Local and Global References</h2>
+
+<p>Every object that JNI returns is a "local reference".  This means that it's valid for the
+duration of the current native method in the current thread.
+<strong>Even if the object itself continues to live on after the native method returns, the reference is not valid.</strong>
+This applies to all sub-classes of <code>jobject</code>, including
+<code>jclass</code>, <code>jstring</code>, and <code>jarray</code>.
+(Dalvik VM will warn you about most reference mis-uses when extended JNI
+checks are enabled.)</p>
+
+<p>If you want to hold on to a reference for a longer period, you must use
+a "global" reference.  The <code>NewGlobalRef</code> function takes the
+local reference as an argument and returns a global one.
+The global reference is guaranteed to be valid until you call
+<code>DeleteGlobalRef</code>.</p>
+
+<p>This pattern is commonly used when caching copies of class objects obtained
+from <code>FindClass</code>, e.g.:</p>
+<pre>jclass* localClass = env-&gt;FindClass("MyClass");
+jclass* globalClass = (jclass*) env-&gt;NewGlobalRef(localClass);</pre>
+
+<p>All JNI methods accept both local and global references as arguments.
+It's possible for references to the same object to have different values;
+for example, the return values from consecutive calls to
+<code>NewGlobalRef</code> on the same object may be different.
+<strong>To see if two references refer to the same object,
+you must use the <code>IsSameObject</code> function.</strong>  Never compare
+references with "==" in native code.</p>
+
+<p>One consequence of this is that you
+<strong>must not assume object references are constant or unique</strong>
+in native code.  The 32-bit value representing an object may be different
+from one invocation of a method to the next, and it's possible that two
+different objects could have the same 32-bit value on consecutive calls.  Do
+not use <code>jobject</code> values as keys.</p>
+
+<p>Programmers are required to "not excessively allocate" local references.  In practical terms this means
+that if you're creating large numbers of local references, perhaps while running through an array of
+Objects, you should free them manually with
+<code>DeleteLocalRef</code> instead of letting JNI do it for you.  The
+VM is only required to reserve slots for
+16 local references, so if you need more than that you should either delete as you go or use
+<code>EnsureLocalCapacity</code> to reserve more.</p>
+
+<p>Note: method and field IDs are just 32-bit identifiers, not object
+references, and should not be passed to <code>NewGlobalRef</code>.  The raw data
+pointers returned by functions like <code>GetStringUTFChars</code>
+and <code>GetByteArrayElements</code> are also not objects.</p>
+
+<p>One unusual case deserves separate mention.  If you attach a native
+thread to the VM with AttachCurrentThread, the code you are running will
+never "return" to the VM until the thread detaches from the VM.  Any local
+references you create will have to be deleted manually unless you're going
+to detach the thread soon.</p>
+
+<a name="UTF_8_and_UTF_16_strings" id="UTF_8_and_UTF_16_strings"></a>
+<h2>UTF-8 and UTF-16 Strings</h2>
+
+<p>The Java programming language uses UTF-16.  For convenience, JNI provides methods that work with "modified UTF-8" encoding
+as well.  (Some VMs use the modified UTF-8 internally to store strings; ours do not.)  The
+modified encoding only supports the 8- and 16-bit forms, and stores ASCII NUL values in a 16-bit encoding.
+The nice thing about it is that you can count on having C-style zero-terminated strings,
+suitable for use with standard libc string functions.  The down side is that you cannot pass
+arbitrary UTF-8 data into the VM and expect it to work correctly.</p>
+
+<p>It's usually best to operate with UTF-16 strings.  With our current VMs, the
+<code>GetStringChars</code> method
+does not require a copy, whereas <code>GetStringUTFChars</code> requires a malloc and a UTF conversion.  Note that
+<strong>UTF-16 strings are not zero-terminated</strong>, and \u0000 is allowed,
+so you need to hang on to the string length as well as
+the string pointer.</p>
+
+<p><strong>Don't forget to Release the strings you Get</strong>.  The
+string functions return <code>jchar*</code> or <code>jbyte*</code>, which
+are C-style pointers to primitive data rather than local references.  They
+are guaranteed valid until Release is called, which means they are not
+released when the native method returns.</p>
+
+<p><strong>Data passed to NewStringUTF must be in "modified" UTF-8 format</strong>.  A
+common mistake is reading character data from a file or network stream
+and handing it to <code>NewStringUTF</code> without filtering it.
+Unless you know the data is 7-bit ASCII, you need to strip out high-ASCII
+characters or convert them to proper "modified" UTF-8 form.  If you don't,
+the UTF-16 conversion will likely not be what you expect.  The extended
+JNI checks will scan strings and warn you about invalid data, but they
+won't catch everything.</p>
+
+<a name="arrays" id="arrays"></a>
+<h2>Primitive Arrays</h2>
+
+<p>JNI provides functions for accessing the contents of array objects.
+While arrays of objects must be accessed one entry at a time, arrays of
+primitives can be read and written directly as if they were declared in C.</p>
+
+<p>To make the interface as efficient as possible without constraining
+the VM implementation,
+the <code>Get&lt;PrimitiveType&gt;ArrayElements</code> family of calls
+allows the VM to either return a pointer to the actual elements, or
+allocate some memory and make a copy.  Either way, the raw pointer returned
+is guaranteed to be valid until the corresponding <code>Release</code> call
+is issued (which implies that, if the data wasn't copied, the array object
+will be pinned down and can't be relocated as part of compacting the heap).
+<strong>You must Release every array you Get.</strong>  Also, if the Get
+call fails, you must ensure that your code doesn't try to Release a NULL
+pointer later.</p>
+
+<p>You can determine whether or not the data was copied by passing in a
+non-NULL pointer for the <code>isCopy</code> argument.  This is rarely
+useful.</p>
+
+<p>The <code>Release</code> call takes a <code>mode</code> argument that can
+have one of three values.  The actions performed by the VM depend upon
+whether it returned a pointer to the actual data or a copy of it:</p>
+
+<ul>
+    <li><code>0</code>
+    <ul>
+        <li>Actual: the array object is un-pinned.
+        <li>Copy: data is copied back.  The buffer with the copy is freed.
+    </ul>
+    <li><code>JNI_COMMIT</code>
+    <ul>
+        <li>Actual: does nothing.
+        <li>Copy: data is copied back.  The buffer with the copy
+        <strong>is not freed</strong>.
+    </ul>
+    <li><code>JNI_ABORT</code>
+    <ul>
+        <li>Actual: the array object is un-pinned.  Earlier
+        writes are <strong>not</strong> aborted.
+        <li>Copy: the buffer with the copy is freed; any changes to it are lost.
+    </ul>
+</ul>
+
+<p>One reason for checking the <code>isCopy</code> flag is to know if
+you need to call <code>Release</code> with <code>JNI_COMMIT</code>
+after making changes to an array &mdash; if you're alternating between making
+changes and executing code that uses the contents of the array, you may be
+able to
+skip the no-op commit.  Another possible reason for checking the flag is for
+efficient handling of <code>JNI_ABORT</code>.  For example, you might want
+to get an array, modify it in place, pass pieces to other functions, and
+then discard the changes.  If you know that JNI is making a new copy for
+you, there's no need to create another "editable" copy.  If JNI is passing
+you the original, then you do need to make your own copy.</p>
+
+<p>Some have asserted that you can skip the <code>Release</code> call if
+<code>*isCopy</code> is false.  This is not the case.  If no copy buffer was
+allocated, then the original memory must be pinned down and can't be moved by
+the garbage collector.</p>
+
+<p>Also note that the <code>JNI_COMMIT</code> flag does NOT release the array,
+and you will need to call <code>Release</code> again with a different flag
+eventually.</p>
+
+
+<a name="region_calls" id="region_calls"></a>
+<h2>Region Calls</h2>
+
+<p>There is an alternative to calls like <code>Get&lt;Type&gt;ArrayElements</code>
+and <code>GetStringChars</code> that may be very helpful when all you want
+to do is copy data in or out.  Consider the following:</p>
+
+<pre>
+    jbyte* data = env->GetByteArrayElements(array, NULL);
+    if (data != NULL) {
+        memcpy(buffer, data, len);
+        env->ReleaseByteArrayElements(array, data, JNI_ABORT);
+    }</pre>
+
+<p>This grabs the array, copies the first <code>len</code> byte
+elements out of it, and then releases the array.  Depending upon the VM
+policies the <code>Get</code> call will either pin or copy the array contents.
+We copy the data (for perhaps a second time), then call Release; in this case
+we use <code>JNI_ABORT</code> so there's no chance of a third copy.</p>
+
+<p>We can accomplish the same thing with this:</p>
+<pre>
+    env->GetByteArrayRegion(array, 0, len, buffer);</pre>
+
+<p>This has several advantages:</p>
+<ul>
+    <li>Requires one JNI call instead of 2, reducing overhead.
+    <li>Doesn't require pinning or extra data copies.
+    <li>Reduces the risk of programmer error &mdash; no risk of forgetting
+    to call <code>Release</code> after something fails.
+</ul>
+
+<p>Similarly, you can use the <code>Set&lt;Type&gt;ArrayRegion</code> call
+to copy data into an array, and <code>GetStringRegion</code> or
+<code>GetStringUTFRegion</code> to copy characters out of a
+<code>String</code>.
+
+
+<a name="exceptions" id="exceptions"></a>
+<h2>Exception</h2>
+
+<p><strong>You may not call most JNI functions while an exception is pending.</strong>
+Your code is expected to notice the exception (via the function's return value,
+<code>ExceptionCheck()</code>, or <code>ExceptionOccurred()</code>) and return,
+or clear the exception and handle it.</p>
+
+<p>The only JNI functions that you are allowed to call while an exception is
+pending are:</p>
+<ul>
+    <li>DeleteGlobalRef
+    <li>DeleteLocalRef
+    <li>DeleteWeakGlobalRef
+    <li>ExceptionCheck
+    <li>ExceptionClear
+    <li>ExceptionDescribe
+    <li>ExceptionOccurred
+    <li>MonitorExit
+    <li>PopLocalFrame
+    <li>PushLocalFrame
+    <li>Release&lt;PrimitiveType&gt;ArrayElements
+    <li>ReleasePrimitiveArrayCritical
+    <li>ReleaseStringChars
+    <li>ReleaseStringCritical
+    <li>ReleaseStringUTFChars
+</ul>
+
+<p>Many JNI calls can throw an exception, but often provide a simpler way
+of checking for failure.  For example, if <code>NewString</code> returns
+a non-NULL value, you don't need to check for an exception.  However, if
+you call a method (using a function like <code>CallObjectMethod</code>),
+you must always check for an exception, because the return value is not
+going to be valid if an exception was thrown.</p>
+
+<p>Note that exceptions thrown by interpreted code do not "leap over" native code,
+and C++ exceptions thrown by native code are not handled by Dalvik.
+The JNI <code>Throw</code> and <code>ThrowNew</code> instructions just
+set an exception pointer in the current thread.  Upon returning to the VM from
+native code, the exception will be noted and handled appropriately.</p>
+
+<p>Native code can "catch" an exception by calling <code>ExceptionCheck</code> or
+<code>ExceptionOccurred</code>, and clear it with
+<code>ExceptionClear</code>.  As usual,
+discarding exceptions without handling them can lead to problems.</p>
+
+<p>There are no built-in functions for manipulating the Throwable object
+itself, so if you want to (say) get the exception string you will need to
+find the Throwable class, look up the method ID for
+<code>getMessage "()Ljava/lang/String;"</code>, invoke it, and if the result
+is non-NULL use <code>GetStringUTFChars</code> to get something you can
+hand to printf or a LOG macro.</p>
+
+
+<a name="extended_checking" id="extended_checking"></a>
+<h2>Extended Checking</h2>
+
+<p>JNI does very little error checking.  Calling <code>SetIntField</code>
+on an Object field will succeed, even if the field is marked
+<code>private</code> and <code>final</code>.  The
+goal is to minimize the overhead on the assumption that, if you've written it in native code,
+you probably did it for performance reasons.</p>
+
+<p>In Dalvik, you can enable additional checks by setting the
+"<code>-Xcheck:jni</code>" flag.  If the flag is set, the VM directs
+the JavaVM and JNIEnv pointers to a different table of functions.
+These functions perform an extended series of checks before calling the
+standard implementation.</p>
+
+<p>The additional tests include:</p>
+
+<ul>
+<li> Check for null pointers where not allowed.</li>
+<li> Verify argument type correctness (jclass is a class object,
+jfieldID points to field data, jstring is a java.lang.String).</li>
+<li> Field type correctness, e.g. don't store a HashMap in a String field.</li>
+<li> Ensure jmethodID is appropriate when making a static or virtual
+method call.</li>
+<li> Check to see if an exception is pending on calls where pending exceptions are not legal.</li>
+<li> Check for calls to inappropriate functions between Critical get/release calls.</li>
+<li> Check that JNIEnv structs aren't being shared between threads.</li>
+<li> Make sure local references aren't used outside their allowed lifespan.</li>
+<li> UTF-8 strings contain only valid "modified UTF-8" data.</li>
+</ul>
+
+<p>Accessibility of methods and fields (i.e. public vs. private) is not
+checked.</p>
+
+<p>For a description of how to enable CheckJNI for Android apps, see
+<a href="embedded-vm-control.html">Controlling the Embedded VM</a>.
+It's currently enabled by default in the Android emulator and on
+"engineering" device builds.</p>
+
+<p>JNI checks can be modified with the <code>-Xjniopts</code> command-line
+flag.  Currently supported values include:</p>
+
+<dl>
+<dt>forcecopy
+<dd>When set, any function that can return a copy of the original data
+(array of primitive values, UTF-16 chars) will always do so.  The buffers
+are over-allocated and surrounded with a guard pattern to help identify
+code writing outside the buffer, and the contents are erased before the
+storage is freed to trip up code that uses the data after calling Release.
+This will have a noticeable performance impact on some applications.
+<dt>warnonly
+<dd>By default, JNI "warnings" cause the VM to abort.  With this flag
+it continues on.
+</dl>
+
+
+<a name="native_libraries" id="native_libraries"></a>
+<h2>Native Libraries</h2>
+
+<p>You can load native code from shared libraries with the standard
+<code>System.loadLibrary()</code> call.  The
+preferred way to get at your native code is:</p>
+
+<ul>
+<li> Call <code>System.loadLibrary()</code> from a static class
+initializer.  (See the earlier example, where one is used to call
+<code>nativeClassInit()</code>.)  The argument is the "undecorated"
+library name, e.g. to load "libfubar.so" you would pass in "fubar".</li>
+<li> Provide a native function: <code><strong>jint JNI_OnLoad(JavaVM* vm, void* reserved)</strong></code></li>
+<li>In <code>JNI_OnLoad</code>, register all of your native methods.  You
+should declare
+the methods "static" so the names don't take up space in the symbol table
+on the device.</li>
+</ul>
+
+<p>The <code>JNI_OnLoad</code> function should look something like this if
+written in C:</p>
+<pre>jint JNI_OnLoad(JavaVM* vm, void* reserved)
+{
+    JNIEnv* env;
+    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK)
+        return -1;
+
+    /* get class with (*env)->FindClass */
+    /* register methods with (*env)->RegisterNatives */
+
+    return JNI_VERSION_1_6;
+}</pre>
+
+<p>You can also call <code>System.load()</code> with the full path name of the
+shared library.  For Android apps, you may find it useful to get the full
+path to the application's private data storage area from the context object.</p>
+
+<p>This is the recommended approach, but not the only approach.  The VM does
+not require explicit registration, nor that you provide a
+<code>JNI_OnLoad</code> function.
+You can instead use "discovery" of native methods that are named in a
+specific way (see <a href="http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/design.html#wp615">
+    the JNI spec</a> for details), though this is less desirable.
+It requires more space in the shared object symbol table,
+loading is slower because it requires string searches through all of the
+loaded shared libraries, and if a method signature is wrong you won't know
+about it until the first time the method is actually used.</p>
+
+<p>One other note about <code>JNI_OnLoad</code>: any <code>FindClass</code>
+calls you make from there will happen in the context of the class loader
+that was used to load the shared library.  Normally <code>FindClass</code>
+uses the loader associated with the method at the top of the interpreted
+stack, or if there isn't one (because the thread was just attached to
+the VM) it uses the "system" class loader.  This makes
+<code>JNI_OnLoad</code> a convenient place to look up and cache class
+object references.</p>
+
+
+<a name="64_bit" id="64_bit"></a>
+<h2>64-bit Considerations</h2>
+
+<p>Android is currently expected to run on 32-bit platforms.  In theory it
+could be built for a 64-bit system, but that is not a goal at this time.
+For the most part this isn't something that you will need to worry about
+when interacting with native code,
+but it becomes significant if you plan to store pointers to native
+structures in integer fields in an object.  To support architectures
+that use 64-bit pointers, <strong>you need to stash your native pointers in a
+<code>long</code> field rather than an <code>int</code></strong>.
+
+
+<a name="unsupported" id="unsupported"></a>
+<h2>Unsupported Features</h2>
+
+<p>All JNI 1.6 features are supported, with the following exceptions:</p>
+<ul>
+    <li><code>DefineClass</code> is not implemented.  Dalvik does not use
+    Java bytecodes or class files, so passing in binary class data
+    doesn't work.  Translation facilities may be added in a future
+    version of the VM.</li>
+    <li>"Weak global" references are implemented, but may only be passed
+    to <code>NewLocalRef</code>, <code>NewGlobalRef</code>, and
+    <code>DeleteWeakGlobalRef</code>.  (The spec strongly encourages
+    programmers to create hard references to weak globals before doing
+    anything with them, so this should not be at all limiting.)</li>
+    <li><code>GetObjectRefType</code> (new in JNI 1.6) is implemented but not fully
+    functional &mdash; it can't always tell the difference between "local" and
+    "global" references.</li>
+</ul>
+
+<p>For backward compatibility, you may need to be aware of:</p>
+<ul>
+    <li>Until Android 2.0 ("Eclair"), the '$' character was not properly
+    converted to "_00024" during searches for method names.  Working
+    around this requires using explicit registration or moving the
+    native methods out of inner classes.
+    <li>Until Android 2.0 ("Eclair"), it was not possible to use a <code>pthread_key_create</code>
+    destructor function to avoid the VM's "thread must be detached before
+    exit" check.  (The VM also uses a pthread key destructor function,
+    so it'd be a race to see which gets called first.)
+    <li>"Weak global" references were not implemented until Android 2.2 ("Froyo").
+    Older VMs will vigorously reject attempts to use them.  You can use
+    the Android platform version constants to test for support.
+</ul>
+
+
+<a name="faq_ULE" id="faq_ULE"></a>
+<h2>FAQ: UnsatisfiedLinkError</h2>
+
+<p>When working on native code it's not uncommon to see a failure like this:</p>
+<pre>java.lang.UnsatisfiedLinkError: Library foo not found</pre>
+
+<p>In some cases it means what it says &mdash; the library wasn't found.  In
+other cases the library exists but couldn't be opened by dlopen(), and
+the details of the failure can be found in the exception's detail message.</p>
+
+<p>Common reasons why you might encounter "library not found" exceptions:</p>
+<ul>
+    <li>The library doesn't exist or isn't accessible to the app.  Use
+    <code>adb shell ls -l &lt;path&gt;</code> to check its presence
+    and permissions.
+    <li>The library wasn't built with the NDK.  This can result in
+    dependencies on functions or libraries that don't exist on the device.
+</ul>
+
+<p>Another class of <code>UnsatisfiedLinkError</code> failures looks like:</p>
+<pre>java.lang.UnsatisfiedLinkError: myfunc
+        at Foo.myfunc(Native Method)
+        at Foo.main(Foo.java:10)</pre>
+
+<p>In logcat, you'll see:</p>
+<pre>W/dalvikvm(  880): No implementation found for native LFoo;.myfunc ()V</pre>
+
+<p>This means that the VM tried to find a matching method but was unsuccessful.
+Some common reasons for this are:</p>
+<ul>
+    <li>The library isn't getting loaded.  Check the logcat output for
+    messages about library loading.
+    <li>The method isn't being found due to a name or signature mismatch.  This
+    is commonly caused by:
+    <ul>
+        <li>For lazy method lookup, failing to declare C++ functions
+        with <code>extern C</code>.  You can use <code>arm-eabi-nm</code>
+        to see the symbols as they appear in the library; if they look
+        mangled (e.g. <code>_Z15Java_Foo_myfuncP7_JNIEnvP7_jclass</code>
+        rather than <code>Java_Foo_myfunc</code>) then you need to
+        adjust the declaration.
+        <li>For explicit registration, minor errors when entering the
+        method signature.  Make sure that what you're passing to the
+        registration call matches the signature in the log file.
+        Remember that 'B' is <code>byte</code> and 'Z' is <code>boolean</code>.
+        Class name components in signatures start with 'L', end with ';',
+        use '/' to separate package/class names, and use '$' to separate
+        inner-class names
+        (e.g. <code>Ljava/util/Map$Entry;</code>).
+    </ul>
+</ul>
+
+<p>Using <code>javah</code> to automatically generate JNI headers may help
+avoid some problems.
+
+
+<a name="faq_FindClass" id="faq_FindClass"></a>
+<h2>FAQ: FindClass didn't find my class</h2>
+
+<p>Make sure that the class name string has the correct format.  JNI class
+names start with the package name and are separated with slashes,
+e.g. <code>java/lang/String</code>.  If you're looking up an array class,
+you need to start with the appropriate number of square brackets and
+must also wrap the class with 'L' and ';', so a one-dimensional array of
+<code>String</code> would be <code>[Ljava/lang/String;</code>.</p>
+
+<p>If the class name looks right, you could be running into a class loader
+issue.  <code>FindClass</code> wants to start the class search in the
+class loader associated with your code.  It examines the VM call stack,
+which will look something like:
+<pre>    Foo.myfunc(Native Method)
+    Foo.main(Foo.java:10)
+    dalvik.system.NativeStart.main(Native Method)</pre>
+
+<p>The topmost method is <code>Foo.myfunc</code>.  <code>FindClass</code>
+finds the <code>ClassLoader</code> object associated with the <code>Foo</code>
+class and uses that.</p>
+
+<p>This usually does what you want.  You can get into trouble if you
+create a thread outside the VM (perhaps by calling <code>pthread_create</code>
+and then attaching it to the VM with <code>AttachCurrentThread</code>).
+Now the stack trace looks like this:</p>
+<pre>    dalvik.system.NativeStart.run(Native Method)</pre>
+
+<p>The topmost method is <code>NativeStart.run</code>, which isn't part of
+your application.  If you call <code>FindClass</code> from this thread, the
+VM will start in the "system" class loader instead of the one associated
+with your application, so attempts to find app-specific classes will fail.</p>
+
+<p>There are a few ways to work around this:</p>
+<ul>
+    <li>Do your <code>FindClass</code> lookups once, in
+    <code>JNI_OnLoad</code>, and cache the class references for later
+    use.  Any <code>FindClass</code> calls made as part of executing
+    <code>JNI_OnLoad</code> will use the class loader associated with
+    the function that called <code>System.loadLibrary</code> (this is a
+    special rule, provided to make library initialization more convenient).
+    If your app code is loading the library, <code>FindClass</code>
+    will use the correct class loader.
+    <li>Pass an instance of the class into the functions that need
+    it, e.g. declare your native method to take a Class argument and
+    then pass <code>Foo.class</code> in.
+    <li>Cache a reference to the <code>ClassLoader</code> object somewhere
+    handy, and issue <code>loadClass</code> calls directly.  This requires
+    some effort.
+</ul>
+
+
+<a name="faq_sharing" id="faq_sharing"></a>
+<h2>FAQ: Sharing raw data with native code</h2>
+
+<p>You may find yourself in a situation where you need to access a large
+buffer of raw data from code written in Java and C/C++.  Common examples
+include manipulation of bitmaps or sound samples.  There are two
+basic approaches.</p>
+
+<p>You can store the data in a <code>byte[]</code>.  This allows very fast
+access from code written in Java.  On the native side, however, you're
+not guaranteed to be able to access the data without having to copy it.  In
+some implementations, <code>GetByteArrayElements</code> and
+<code>GetPrimitiveArrayCritical</code> will return actual pointers to the
+raw data in the managed heap, but in others it will allocate a buffer
+on the native heap and copy the data over.</p>
+
+<p>The alternative is to store the data in a direct byte buffer.  These
+can be created with <code>java.nio.ByteBuffer.allocateDirect</code>, or
+the JNI <code>NewDirectByteBuffer</code> function.  Unlike regular
+byte buffers, the storage is not allocated on the managed heap, and can
+always be accessed directly from native code (get the address
+with <code>GetDirectBufferAddress</code>).  Depending on how direct
+byte buffer access is implemented in the VM, accessing the data from code
+written in Java can be very slow.</p>
+
+<p>The choice of which to use depends on two factors:</p>
+<ol>
+    <li>Will most of the data accesses happen from code written in Java
+    or in C/C++?
+    <li>If the data is eventually being passed to a system API, what form
+    must it be in?  (For example, if the data is eventually passed to a
+    function that takes a byte[], doing processing in a direct
+    <code>ByteBuffer</code> might be unwise.)
+</ol>
+
+<p>If there's no clear winner, use a direct byte buffer.  Support for them
+is built directly into JNI, and access to them from code written in
+Java can be made faster with VM improvements.</p>
diff --git a/docs/html/guide/practices/design/performance.jd b/docs/html/guide/practices/design/performance.jd
index fe69d7d..c41f971 100644
--- a/docs/html/guide/practices/design/performance.jd
+++ b/docs/html/guide/practices/design/performance.jd
@@ -375,6 +375,9 @@
 <p>Native code is primarily useful when you have an existing native codebase
 that you want to port to Android, not for "speeding up" parts of a Java app.</p>
 
+<p>If you do need to use native code, you should read our
+<a href="{@docRoot}guide/practices/design/jni.html">JNI Tips</a>.</p>
+
 <p>(See also <em>Effective Java</em> item 54.)</p>
 
 <a name="closing_notes" id="closing_notes"></a>
diff --git a/docs/html/guide/publishing/licensing.jd b/docs/html/guide/publishing/licensing.jd
index 4184ecb..a9b182e 100644
--- a/docs/html/guide/publishing/licensing.jd
+++ b/docs/html/guide/publishing/licensing.jd
@@ -368,7 +368,7 @@
 
 <ol>
 <li><a href="#download-sdk">Downloading the latest SDK</a>, if you haven't already done so </li>
-<li><a href="#runtime-setup">Setting up the runtime environment</a> for development</a></li>
+<li><a href="#runtime-setup">Setting up the runtime environment</a> for development</li>
 <li><a href="#download-lvl">Downloading the Market Licensing component</a> into your SDK </li>
 <li><a href="#lvl-setup">Setting up the Licensing Verification Library</a></li>
 <li><a href="#add-library">Including the LVL library project in your application</a></li>
@@ -582,9 +582,9 @@
 <p>When the download is complete, the Android SDK and AVD Manager installs both
 the LVL library project and the example application into these directories: </p>
 
-<p style="margin-left:2em"><code>&lt;<em>sdk</em>&gt;/market_licensing/library/</code>
+<p style="margin-left:2em"><code>&lt;<em>sdk</em>&gt;/extras/google/market_licensing/library/</code>
 &nbsp;&nbsp;(the LVL library project)<br />
-<code>&lt;<em>sdk</em>&gt;/market_licensing/sample/</code>&nbsp;&nbsp;(the example
+<code>&lt;<em>sdk</em>&gt;/extras/google/market_licensing/sample/</code>&nbsp;&nbsp;(the example
 application)</p>
 
 <p>If you aren't familiar with how to download components into your SDK, see the
@@ -1530,7 +1530,7 @@
 licensing is published (or will be published). </li>
 <li>In the account home page, locate the "Edit profile" link and click it. </li>
 <li>In the Edit Profile page, locate the "Licensing" pane, shown below. Your
-public key for licensing is given in the "Public key" text box. </p>
+public key for licensing is given in the "Public key" text box. </li>
 </ol>
 
 <p>To add the public key to your application, simply copy/paste the key string
diff --git a/docs/html/guide/topics/fundamentals/tasks-and-back-stack.jd b/docs/html/guide/topics/fundamentals/tasks-and-back-stack.jd
index bd542bd..f22e5b2 100644
--- a/docs/html/guide/topics/fundamentals/tasks-and-back-stack.jd
+++ b/docs/html/guide/topics/fundamentals/tasks-and-back-stack.jd
@@ -11,7 +11,7 @@
   <li>A task contains a collection of activities in the order in which the user interacts with
 them</li>
   <li>Tasks can move to the background and retain the state of each activity in order for the user
-to perform other tasks without loosing their work</li>
+to perform other tasks without losing their work</li>
 </ul>
 
 <h2>In this document</h2>
@@ -181,7 +181,7 @@
 system still
 knows that the activity has a place in the back stack, but when the activity is brought to the
 top of the stack the system must recreate it (rather than resume it). In order to
-avoid loosing the user's work, you should proactively retain it by implementing the {@link
+avoid losing the user's work, you should proactively retain it by implementing the {@link
 android.app.Activity#onSaveInstanceState onSaveInstanceState()} callback
 methods in your activity.</p>
 
@@ -201,7 +201,7 @@
 started (instead of being placed within the current task); or, when you start an activity, you want
 to bring forward an existing instance of it (instead of creating a new
 instance on top of the back stack); or, you want your back stack to be cleared of all
-activitiesstart an activity except for the root activity when the user leaves the task.</p>
+activities except for the root activity when the user leaves the task.</p>
 
 <p>You can do these things and more, with attributes in the
 <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code
@@ -566,4 +566,4 @@
 respond, continue with the <b><a
 href="{@docRoot}guide/topics/intents/intents-filters.html">Intents and Intent
 Filters</a></b> document.</p>
--->
\ No newline at end of file
+-->
diff --git a/docs/html/guide/webapps/targeting.jd b/docs/html/guide/webapps/targeting.jd
index bdc2d5e..46f769c 100644
--- a/docs/html/guide/webapps/targeting.jd
+++ b/docs/html/guide/webapps/targeting.jd
@@ -368,14 +368,14 @@
 }
 
 &#64;media screen and (-webkit-device-pixel-ratio: 1.5) {
-    // CSS for high-density screens
+    /* CSS for high-density screens */
     #header {
         background:url(high-density-image.png);
     }
 }
 
 &#64;media screen and (-webkit-device-pixel-ratio: 0.75) {
-    // CSS for low-density screens
+    /* CSS for low-density screens */
     #header {
         background:url(low-density-image.png);
     }
@@ -426,7 +426,7 @@
 <pre>
 if (window.devicePixelRatio == 1.5) {
   alert("This is a high-density screen");
-} else if (window.devicePixelRation == 0.75) {
+} else if (window.devicePixelRatio == 0.75) {
   alert("This is a low-density screen");
 }
 </pre>
diff --git a/docs/html/sdk/download.jd b/docs/html/sdk/download.jd
index 81b4ff6..44fe5e4 100644
--- a/docs/html/sdk/download.jd
+++ b/docs/html/sdk/download.jd
@@ -1,4 +1,93 @@
-sdk.redirect=true
+page.title=Download an Archived Android SDK
+hide_license_footer=true
 
 @jd:body
 
+<script type="text/javascript">
+  function verify() {
+    document.getElementById('download-button').disabled =
+!document.getElementById('checkbox').checked;
+  }
+  function submit() {
+    var location = window.location.href;
+    if (location.indexOf('?v=') != -1) {
+      var filename = location.substring(location.indexOf('=')+1,location.length);
+      if (document.getElementById('checkbox').checked) {
+        document.location = "http://dl.google.com/android/" + filename;
+      }
+      document.getElementById('click-download').setAttribute("href", "http://dl.google.com/android/"
++ filename);
+      $("#terms-form").hide(500);
+      $("#next-steps").show(500);
+      document.getElementById('checkbox').disabled=true;
+      document.getElementById('download-button').disabled=true;
+    } else {
+      alert("You have not selected an SDK version. Please return to the SDK Archives page");
+    }
+  }
+</script>
+
+<div id="terms-form">
+    <p>Please carefully review the Android SDK License Agreement before downloading the SDK.
+The License Agreement constitutes a contract between you and Google with respect to your use of the
+SDK.</p>
+    <p class="note"><strong>Note:</strong> You must agree to this license agreement in order to
+download one of the archived SDKs, because these SDK packages contain Google software (whereas, the
+<a href="http://developer.android.com/sdk/index.html">current SDK</a> packages do not require a
+license agreement, because they contain only the open sourced SDK tools).</p>
+
+  <iframe id="terms" style="border:1px solid #888;margin:0 0 1em;height:400px;width:95%;"
+src="terms_body.html">
+  </iframe>
+
+  <p>
+    <input type="checkbox" id="checkbox" onclick="verify()" />
+    <label for="checkbox">I agree to the terms of the Android SDK License Agreement.</label>
+  </p>
+  <p>
+    <input type="submit" value="Download" id="download-button" disabled="disabled"
+onclick="submit()" />
+  </p>
+  <p>
+  <script language="javascript">
+    var loc = window.location.href;
+    if (loc.indexOf('?v=') != -1) {
+      var filename = loc.substring(loc.indexOf('=')+1,loc.length);
+      document.write("File: " + filename);
+    }
+  </script>
+  </p>
+</div><!-- end terms-form -->
+
+<noscript>
+  <p><strong>Please enable Javascript in your browser in order to agree to the terms and download
+the SDK.</strong></p>
+</noscript>
+
+<div class="special" id="next-steps" style="display:none">
+  <p>Your download should be underway. If not, <a id="click-download">click here to start the
+download</a>.</p>
+  <p>Beware that you've just downloaded a very old version of the Android SDK, which is not
+recommended. We no longer maintain documentation about how to install these archived SDKs nor
+support the tools contained within.</p>
+  <p>We recommend that you instead download the latest <a
+href="http://developer.android.com/sdk/index.html">Android SDK starter package</a>, which includes
+the latest SDK tools and allows you to develop against any version of the Android platform, back to
+Android 1.1.</p>
+</div>
+
+<script type="text/javascript">
+  var loc = window.location.href;
+  var filename = loc.substring(loc.indexOf('=')+1,loc.length);
+  version = filename.substring(filename.indexOf('.')-1,filename.lastIndexOf('.'));
+  $(".addVersionPath").each(function(i) {
+    var oldHref = $(this).attr("href");
+    $(this).attr({href: "/sdk/" + version + "/" + oldHref});
+  });
+</script>
+
+
+
+
+
+
diff --git a/docs/html/sdk/older_releases.jd b/docs/html/sdk/older_releases.jd
index 77f7e43..870ff04 100644
--- a/docs/html/sdk/older_releases.jd
+++ b/docs/html/sdk/older_releases.jd
@@ -47,7 +47,7 @@
     <td>Windows</td>
     <td>
   <a
-href="http://dl.google.com/android/archives/android-sdk-windows-1.6_r1.zip">android-sdk-
+href="{@docRoot}sdk/download.html?v=archives/android-sdk-windows-1.6_r1.zip">android-sdk-
 windows-1 .6_r1.zip</a>
     </td>
     <td>260529085 bytes</td>
@@ -57,7 +57,7 @@
     <td>Mac OS X (intel)</td>
     <td>
   <a
-href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.6_r1.zip">android-sdk-
+href="{@docRoot}sdk/download.html?v=archives/android-sdk-mac_x86-1.6_r1.zip">android-sdk-
 mac_x86-1 .6_r1.zip</a>
     </td>
     <td>247412515 bytes</td>
@@ -67,7 +67,7 @@
     <td>Linux (i386)</td>
     <td>
   <a
-href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.6_r1.tgz">android-
+href="{@docRoot}sdk/download.html?v=archives/android-sdk-linux_x86-1.6_r1.tgz">android-
 sdk- linux_x86-1.6_r1.tgz</a>
     </td>
     <td>238224860 bytes</td>
@@ -92,7 +92,7 @@
     <td>Windows</td>
     <td>
   <a
-href="http://dl.google.com/android/archives/android-sdk-windows-1.5_r3.zip">android-sdk-
+href="{@docRoot}sdk/download.html?v=archives/android-sdk-windows-1.5_r3.zip">android-sdk-
 windows-1 .5_r3.zip</a>
     </td>
     <td>191477853 bytes</td>
@@ -102,7 +102,7 @@
     <td>Mac OS X (intel)</td>
     <td>
   <a
-href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.5_r3.zip">android-sdk-
+href="{@docRoot}sdk/download.html?v=archives/android-sdk-mac_x86-1.5_r3.zip">android-sdk-
 mac_x86-1 .5_r3.zip</a>
     </td>
     <td>183024673 bytes</td>
@@ -112,7 +112,7 @@
     <td>Linux (i386)</td>
     <td>
   <a
-href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.5_r3.zip">android-
+href="{@docRoot}sdk/download.html?v=archives/android-sdk-linux_x86-1.5_r3.zip">android-
 sdk- linux_x86-1.5_r3.zip</a>
     </td>
     <td>178117561 bytes</td>
@@ -137,7 +137,7 @@
     <td>Windows</td>
     <td>
   <a
-href="http://dl.google.com/android/archives/android-sdk-windows-1.1_r1.zip">android-sdk-
+href="{@docRoot}sdk/download.html?v=archives/android-sdk-windows-1.1_r1.zip">android-sdk-
 windows-1
 .1_r1.zip</a>
     </td>
@@ -148,7 +148,7 @@
     <td>Mac OS X (intel)</td>
     <td>
   <a
-href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.1_r1.zip">android-sdk-
+href="{@docRoot}sdk/download.html?v=archives/android-sdk-mac_x86-1.1_r1.zip">android-sdk-
 mac_x86-1
 .1_r1.zip</a>
     </td>
@@ -159,7 +159,7 @@
     <td>Linux (i386)</td>
     <td>
   <a
-href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.1_r1.zip">android-
+href="{@docRoot}sdk/download.html?v=archives/android-sdk-linux_x86-1.1_r1.zip">android-
 sdk-
 linux_x86-1.1_r1.zip</a>
     </td>
@@ -185,7 +185,7 @@
     <td>Windows</td>
     <td>
   <a
-href="http://dl.google.com/android/archives/android-sdk-windows-1.0_r2.zip">android-sdk-
+href="{@docRoot}sdk/download.html?v=archives/android-sdk-windows-1.0_r2.zip">android-sdk-
 windows-1
 .0_r2.zip</a>
     </td>
@@ -196,7 +196,7 @@
     <td>Mac OS X (intel)</td>
     <td>
   <a
-href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.0_r2.zip">android-sdk-
+href="{@docRoot}sdk/download.html?v=archives/android-sdk-mac_x86-1.0_r2.zip">android-sdk-
 mac_x86-1
 .0_r2.zip</a>
     </td>
@@ -207,7 +207,7 @@
     <td>Linux (i386)</td>
     <td>
   <a
-href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.0_r2.zip">android-
+href="{@docRoot}sdk/download.html?v=archives/android-sdk-linux_x86-1.0_r2.zip">android-
 sdk-
 linux_x86-1.0_r2.zip</a>
     </td>
@@ -241,7 +241,7 @@
     <td>Windows</td>
     <td>
   <a
-href="http://dl.google.com/android/archives/android-sdk-windows-1.5_r2.zip">android-sdk-
+href="{@docRoot}sdk/download.html?v=archives/android-sdk-windows-1.5_r2.zip">android-sdk-
 windows-1 .5_r2.zip</a>
     </td>
     <td>178346828 bytes</td>
@@ -251,7 +251,7 @@
     <td>Mac OS X (intel)</td>
     <td>
   <a
-href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.5_r2.zip">android-sdk-
+href="{@docRoot}sdk/download.html?v=archives/android-sdk-mac_x86-1.5_r2.zip">android-sdk-
 mac_x86-1 .5_r2.zip</a>
     </td>
     <td>169945128 bytes</td>
@@ -261,7 +261,7 @@
     <td>Linux (i386)</td>
     <td>
   <a
-href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.5_r2.zip">android-
+href="{@docRoot}sdk/download.html?v=archives/android-sdk-linux_x86-1.5_r2.zip">android-
 sdk- linux_x86-1.5_r2.zip</a>
     </td>
     <td>165035130 bytes</td>
@@ -286,7 +286,7 @@
     <td>Windows</td>
     <td>
   <a
-href="http://dl.google.com/android/archives/android-sdk-windows-1.5_r1.zip">android-sdk-
+href="{@docRoot}sdk/download.html?v=archives/android-sdk-windows-1.5_r1.zip">android-sdk-
 windows-1 .5_r1.zip</a>
     </td>
     <td>176263368 bytes</td>
@@ -296,7 +296,7 @@
     <td>Mac OS X (intel)</td>
     <td>
   <a
-href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.5_r1.zip">android-sdk-
+href="{@docRoot}sdk/download.html?v=archives/android-sdk-mac_x86-1.5_r1.zip">android-sdk-
 mac_x86-1 .5_r1.zip</a>
     </td>
     <td>167848675 bytes</td>
@@ -306,7 +306,7 @@
     <td>Linux (i386)</td>
     <td>
   <a
-href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.5_r1.zip">android-
+href="{@docRoot}sdk/download.html?v=archives/android-sdk-linux_x86-1.5_r1.zip">android-
 sdk- linux_x86-1.5_r1.zip</a>
     </td>
     <td>162938845 bytes</td>
@@ -331,7 +331,7 @@
     <td>Windows</td>
     <td>
   <a
-href="http://dl.google.com/android/archives/android-sdk-windows-1.0_r1.zip">android-sdk-
+href="{@docRoot}sdk/download.html?v=archives/android-sdk-windows-1.0_r1.zip">android-sdk-
 windows-1 .0_r1.zip</a>
     </td>
     <td>89.7 MB bytes</td>
@@ -341,7 +341,7 @@
     <td>Mac OS X (intel)</td>
     <td>
   <a
-href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.0_r1.zip">android-sdk-
+href="{@docRoot}sdk/download.html?v=archives/android-sdk-mac_x86-1.0_r1.zip">android-sdk-
 mac_x86-1 .0_r1.zip</a>
     </td>
     <td>87.5 MB bytes</td>
@@ -351,7 +351,7 @@
     <td>Linux (i386)</td>
     <td>
   <a
-href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.0_r1.zip">android-
+href="{@docRoot}sdk/download.html?v=archives/android-sdk-linux_x86-1.0_r1.zip">android-
 sdk- linux_x86-1.0_r1.zip</a>
     </td>
     <td>87.8 MB bytes</td>
diff --git a/docs/html/sdk/terms_body.html b/docs/html/sdk/terms_body.html
new file mode 100644
index 0000000..8c55b37
--- /dev/null
+++ b/docs/html/sdk/terms_body.html
@@ -0,0 +1,336 @@
+
+ 
+<p>This is the Android Software Development Kit License Agreement.</p> 
+ 
+<h2> 
+	1. Introduction
+</h2> 
+<p> 
+	1.1 The Android Software Development Kit (referred to in this License Agreement as the "SDK"
+and specifically including the Android system files, packaged APIs, and Google APIs add-ons) is
+licensed to you subject to the terms of this License Agreement. This License Agreement forms a
+legally binding contract between you and Google in relation to your use of the SDK.
+ 
+</p> 
+<p> 
+	1.2 "Google" means Google Inc., a Delaware corporation with principal place of business at
+1600 Amphitheatre Parkway, Mountain View, CA 94043, United States.
+</p> 
+<h2> 
+	2. Accepting this License Agreement
+</h2> 
+<p> 
+	2.1 In order to use the SDK, you must first agree to this License Agreement. You may not use
+the SDK if you do not accept this License Agreement.
+</p> 
+<p> 
+	2.2 You can accept this License Agreement by:
+</p> 
+<p> 
+	(A) clicking to accept or agree to this License Agreement, where this option is made
+available to you; or
+</p> 
+<p> 
+	(B) by actually using the SDK. In this case, you agree that use of the SDK constitutes
+acceptance of the Licensing Agreement from that point onwards.
+</p> 
+<p> 
+	2.3 You may not use the SDK and may not accept the Licensing Agreement if you are a person
+barred from receiving the SDK under the laws of the United States or other countries including the
+country in which you are resident or from which you use the SDK.
+</p> 
+<p> 
+	2.4 If you are agreeing to be bound by this License Agreement on behalf of your employer or
+other entity, you represent and warrant that you have full legal authority to bind your employer or
+such entity to this License Agreement. If you do not have the requisite authority, you may not
+accept the Licensing Agreement or use the SDK on behalf of your employer or other entity.
+</p> 
+<h2> 
+	3. SDK License from Google
+</h2> 
+<p> 
+	3.1 Subject to the terms of this License Agreement, Google grants you a limited, worldwide,
+royalty-free, non- assignable and non-exclusive license to use the SDK solely to develop
+applications to run on the Android platform.
+</p> 
+<p> 
+	3.2 You agree that Google or third parties own all legal right, title and interest in and to
+the SDK, including any Intellectual Property Rights that subsist in the SDK. "Intellectual Property
+Rights" means any and all rights under patent law, copyright law, trade secret law, trademark law,
+and any and all other proprietary rights. Google reserves all rights not expressly granted to you. 
+ 
+</p> 
+<p> 
+	3.3 Except to the extent required by applicable third party licenses, you may not copy
+(except for backup purposes), modify, adapt, redistribute, decompile, reverse engineer, disassemble,
+or create derivative works of the SDK or any part of the SDK. Except to the extent required by
+applicable third party licenses, you may not load any part of the SDK onto a mobile handset or any
+other hardware device except a personal computer, combine any part of the SDK with other software,
+or distribute any software or device incorporating a part of the SDK. 
+</p> 
+<p> 
+	3.4 Use, reproduction and distribution of components of the SDK licensed under an open
+source software license are governed solely by the terms of that open source software license and
+not this License Agreement.
+</p> 
+<p> 
+	3.5 You agree that the form and nature of the SDK that Google provides may change without
+prior notice to you and that future versions of the SDK may be incompatible with applications
+developed on previous versions of the SDK. You agree that Google may stop (permanently or
+temporarily) providing the SDK (or any features within the SDK) to you or to users generally at
+Google's sole discretion, without prior notice to you.
+</p> 
+<p> 
+	3.6 Nothing in this License Agreement gives you a right to use any of Google's trade names,
+trademarks, service marks, logos, domain names, or other distinctive brand features.
+</p> 
+<p> 
+	3.7 You agree that you will not remove, obscure, or alter any proprietary rights notices
+(including copyright and trademark notices) that may be affixed to or contained within the SDK.
+</p> 
+<h2> 
+	4. Use of the SDK by You
+</h2> 
+<p> 
+	4.1 Google agrees that it obtains no right, title or interest from you (or your licensors)
+under this License Agreement in or to any software applications that you develop using the SDK,
+including any intellectual property rights that subsist in those applications. 
+</p> 
+<p> 
+	4.2 You agree to use the SDK and write applications only for purposes that are permitted by
+(a) this License Agreement and (b) any applicable law, regulation or generally accepted practices or
+guidelines in the relevant jurisdictions (including any laws regarding the export of data or
+software to and from the United States or other relevant countries).
+</p> 
+<p> 
+	4.3 You agree that if you use the SDK to develop applications for general public users, you
+will protect the privacy and legal rights of those users. If the users provide you with user names,
+passwords, or other login information or personal information, your must make the users aware that
+the information will be available to your application, and you must provide legally adequate privacy
+notice and protection for those users. If your application stores personal or sensitive information
+provided by users, it must do so securely. If the user provides your application with Google Account
+information, your application may only use that information to access the user's Google Account
+when, and for the limited purposes for which, the user has given you permission to do so.
+</p> 
+<p> 
+	4.4 You agree that you will not engage in any activity with the SDK, including the
+development or distribution of an application, that interferes with, disrupts, damages, or accesses
+in an unauthorized manner the servers, networks, or other properties or services of any third party
+including, but not limited to, Google or any mobile communications carrier.
+</p> 
+<p> 
+	4.5 You agree that you are solely responsible for (and that Google has no responsibility to
+you or to any third party for) any data, content, or resources that you create, transmit or display
+through the Android platform and/or applications for the Android platform, and for the consequences
+of your actions (including any loss or damage which Google may suffer) by doing so.
+</p> 
+<p> 
+	4.6 You agree that you are solely responsible for (and that Google has no responsibility to
+you or to any third party for) any breach of your obligations under this License Agreement, any
+applicable third party contract or Terms of Service, or any applicable law or regulation, and for
+the consequences (including any loss or damage which Google or any third party may suffer) of any
+such breach.
+</p> 
+<h2> 
+	5. Your Developer Credentials
+</h2> 
+<p> 
+	5.1 You agree that you are responsible for maintaining the confidentiality of any developer
+credentials that may be issued to you by Google or which you may choose yourself and that you will
+be solely responsible for all applications that are developed under your developer credentials.
+</p> 
+<h2> 
+	6. Privacy and Information
+</h2> 
+<p> 
+	6.1 In order to continually innovate and improve the SDK, Google may collect certain usage
+statistics from the software including but not limited to a unique identifier, associated IP
+address, version number of the software, and information on which tools and/or services in the SDK
+are being used and how they are being used. Before any of this information is collected, the SDK
+will notify you and seek your consent. If you withhold consent, the information will not be
+collected.
+</p> 
+<p> 
+	6.2 The data collected is examined in the aggregate to improve the SDK and is maintained in
+accordance with Google's Privacy Policy.
+</p> 
+<h2> 
+	7. Third Party Applications for the Android Platform
+</h2> 
+<p> 
+	7.1 If you use the SDK to run applications developed by a third party or that access data,
+content or resources provided by a third party, you agree that Google is not responsible for those
+applications, data, content, or resources. You understand that all data, content or resources which
+you may access through such third party applications are the sole responsibility of the person from
+which they originated and that Google is not liable for any loss or damage that you may experience
+as a result of the use or access of any of those third party applications, data, content, or
+resources.
+</p> 
+<p> 
+	7.2 You should be aware the data, content, and resources presented to you through such a
+third party application may be protected by intellectual property rights which are owned by the
+providers (or by other persons or companies on their behalf). You may not modify, rent, lease, loan,
+sell, distribute or create derivative works based on these data, content, or resources (either in
+whole or in part) unless you have been specifically given permission to do so by the relevant
+owners.
+</p> 
+<p> 
+	7.3 You acknowledge that your use of such third party applications, data, content, or
+resources may be subject to separate terms between you and the relevant third party. In that case,
+this License Agreement does not affect your legal relationship with these third parties.
+</p> 
+<h2> 
+	8. Using Android APIs
+</h2> 
+<p> 
+	8.1 Google Data APIs
+</p> 
+<p> 
+	8.1.1 If you use any API to retrieve data from Google, you acknowledge that the data may be
+protected by intellectual property rights which are owned by Google or those parties that provide
+the data (or by other persons or companies on their behalf). Your use of any such API may be subject
+to additional Terms of Service. You may not modify, rent, lease, loan, sell, distribute or create
+derivative works based on this data (either in whole or in part) unless allowed by the relevant
+Terms of Service.
+</p> 
+<p> 
+	8.1.2 If you use any API to retrieve a user's data from Google, you acknowledge and agree
+that you shall retrieve data only with the user's explicit consent and only when, and for the
+limited purposes for which, the user has given you permission to do so. 
+ 
+</p> 
+<h2> 
+	9. Terminating this License Agreement
+</h2> 
+<p> 
+	9.1 This License Agreement will continue to apply until terminated by either you or Google
+as set out below.
+</p> 
+<p> 
+	9.2 If you want to terminate this License Agreement, you may do so by ceasing your use of
+the SDK and any relevant developer credentials.
+</p> 
+<p> 
+	9.3 Google may at any time, terminate this License Agreement with you if:
+</p> 
+<p> 
+	(A) you have breached any provision of this License Agreement; or
+</p> 
+<p> 
+	(B) Google is required to do so by law; or
+</p> 
+<p> 
+	(C) the partner with whom Google offered certain parts of SDK (such as APIs) to you has
+terminated its relationship with Google or ceased to offer certain parts of the SDK to you; or
+</p> 
+<p> 
+	(D) Google decides to no longer providing the SDK or certain parts of the SDK to users in
+the country in which you are resident or from which you use the service, or the provision of the SDK
+or certain SDK services to you by Google is, in Google's sole discretion, no longer commercially
+viable.
+</p> 
+<p> 
+	9.4 When this License Agreement comes to an end, all of the legal rights, obligations and
+liabilities that you and Google have benefited from, been subject to (or which have accrued over
+time whilst this License Agreement has been in force) or which are expressed to continue
+indefinitely, shall be unaffected by this cessation, and the provisions of paragraph 14.7 shall
+continue to apply to such rights, obligations and liabilities indefinitely.
+</p> 
+<h2> 
+	10. DISCLAIMER OF WARRANTIES
+</h2> 
+<p> 
+	10.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT YOUR USE OF THE SDK IS AT YOUR SOLE RISK AND
+THAT THE SDK IS PROVIDED "AS IS" AND "AS AVAILABLE" WITHOUT WARRANTY OF ANY KIND FROM GOOGLE.
+</p> 
+<p> 
+	10.2 YOUR USE OF THE SDK AND ANY MATERIAL DOWNLOADED OR OTHERWISE OBTAINED THROUGH THE USE
+OF THE SDK IS AT YOUR OWN DISCRETION AND RISK AND YOU ARE SOLELY RESPONSIBLE FOR ANY DAMAGE TO YOUR
+COMPUTER SYSTEM OR OTHER DEVICE OR LOSS OF DATA THAT RESULTS FROM SUCH USE.
+</p> 
+<p> 
+	10.3 GOOGLE FURTHER EXPRESSLY DISCLAIMS ALL WARRANTIES AND CONDITIONS OF ANY KIND, WHETHER
+EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES AND CONDITIONS OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+</p> 
+<h2> 
+	11. LIMITATION OF LIABILITY
+</h2> 
+<p> 
+	11.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT GOOGLE, ITS SUBSIDIARIES AND AFFILIATES, AND
+ITS LICENSORS SHALL NOT BE LIABLE TO YOU UNDER ANY THEORY OF LIABILITY FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL CONSEQUENTIAL OR EXEMPLARY DAMAGES THAT MAY BE INCURRED BY YOU, INCLUDING ANY
+LOSS OF DATA, WHETHER OR NOT GOOGLE OR ITS REPRESENTATIVES HAVE BEEN ADVISED OF OR SHOULD HAVE BEEN
+AWARE OF THE POSSIBILITY OF ANY SUCH LOSSES ARISING.
+</p> 
+<h2> 
+	12. Indemnification
+</h2> 
+<p> 
+	12.1 To the maximum extent permitted by law, you agree to defend, indemnify and hold
+harmless Google, its affiliates and their respective directors, officers, employees and agents from
+and against any and all claims, actions, suits or proceedings, as well as any and all losses,
+liabilities, damages, costs and expenses (including reasonable attorneys fees) arising out of or
+accruing from (a) your use of the SDK, (b) any application you develop on the SDK that infringes any
+copyright, trademark, trade secret, trade dress, patent or other intellectual property right of any
+person or defames any person or violates their rights of publicity or privacy, and (c) any
+non-compliance by you with this License Agreement.
+</p> 
+<h2> 
+	13. Changes to the License Agreement
+</h2> 
+<p> 
+	13.1 Google may make changes to the License Agreement as it distributes new versions of the
+SDK. When these changes are made, Google will make a new version of the License Agreement available
+on the website where the SDK is made available.
+</p> 
+<h2> 
+	14. General Legal Terms
+</h2> 
+<p> 
+	14.1 This License Agreement constitute the whole legal agreement between you and Google and
+govern your use of the SDK (excluding any services which Google may provide to you under a separate
+written agreement), and completely replace any prior agreements between you and Google in relation
+to the SDK.
+</p> 
+<p> 
+	14.2 You agree that if Google does not exercise or enforce any legal right or remedy which
+is contained in this License Agreement (or which Google has the benefit of under any applicable
+law), this will not be taken to be a formal waiver of Google's rights and that those rights or
+remedies will still be available to Google.
+</p> 
+<p> 
+	14.3 If any court of law, having the jurisdiction to decide on this matter, rules that any
+provision of this License Agreement is invalid, then that provision will be removed from this
+License Agreement without affecting the rest of this License Agreement. The remaining provisions of
+this License Agreement will continue to be valid and enforceable.
+</p> 
+<p> 
+	14.4 You acknowledge and agree that each member of the group of companies of which Google is
+the parent shall be third party beneficiaries to this License Agreement and that such other
+companies shall be entitled to directly enforce, and rely upon, any provision of this License
+Agreement that confers a benefit on (or rights in favor of) them. Other than this, no other person
+or company shall be third party beneficiaries to this License Agreement.
+</p> 
+<p> 
+	14.5 EXPORT RESTRICTIONS. THE SDK IS SUBJECT TO UNITED STATES EXPORT LAWS AND REGULATIONS.
+YOU MUST COMPLY WITH ALL DOMESTIC AND INTERNATIONAL EXPORT LAWS AND REGULATIONS THAT APPLY TO THE
+SDK. THESE LAWS INCLUDE RESTRICTIONS ON DESTINATIONS, END USERS AND END USE.
+</p> 
+<p> 
+	14.6 The rights granted in this License Agreement may not be assigned or transferred by
+either you or Google without the prior written approval of the other party. Neither you nor Google
+shall be permitted to delegate their responsibilities or obligations under this License Agreement
+without the prior written approval of the other party.
+</p> 
+<p> 
+	14.7 This License Agreement, and your relationship with Google under this License Agreement,
+shall be governed by the laws of the State of California without regard to its conflict of laws
+provisions. You and Google agree to submit to the exclusive jurisdiction of the courts located
+within the county of Santa Clara, California to resolve any legal matter arising from this License
+Agreement. Notwithstanding this, you agree that Google shall still be allowed to apply for
+injunctive remedies (or an equivalent type of urgent legal relief) in any jurisdiction.
+</p> 
+<p> 
+	<em>April 10, 2009</em> 
+</p> 
\ No newline at end of file
diff --git a/include/android_runtime/AndroidRuntime.h b/include/android_runtime/AndroidRuntime.h
index 22c9b72..b02a057 100644
--- a/include/android_runtime/AndroidRuntime.h
+++ b/include/android_runtime/AndroidRuntime.h
@@ -46,17 +46,13 @@
         const char* className, const JNINativeMethod* gMethods, int numMethods);
 
     /**
-     * Call a static Java function that takes no arguments and returns void.
-     */
-    status_t callStatic(const char* className, const char* methodName);
-
-    /**
      * Call a class's static main method with the given arguments,
      */
-    status_t callMain(const char* className, int argc, const char* const argv[]);
+    status_t callMain(const char* className, jclass clazz, int argc,
+        const char* const argv[]);
 
     /**
-     * Find a class, with the input either of the form 
+     * Find a class, with the input either of the form
      * "package/class" or "package.class".
      */
     static jclass findClass(JNIEnv* env, const char* className);
@@ -67,7 +63,14 @@
     void start();       // start in android.util.RuntimeInit
 
     static AndroidRuntime* getRuntime();
-    
+
+    /**
+     * This gets called after the VM has been created, but before we
+     * run any code. Override it to make any FindClass calls that need
+     * to use CLASSPATH.
+     */
+    virtual void onVmCreated(JNIEnv* env);
+
     /**
      * This gets called after the JavaVM has initialized.  Override it
      * with the system's native entry point.
@@ -98,6 +101,9 @@
     /** return a pointer to the JNIEnv pointer for this thread */
     static JNIEnv* getJNIEnv();
 
+    /** return a new string corresponding to 'className' with all '.'s replaced by '/'s. */
+    static char* toSlashClassName(const char* className);
+
 private:
     static int startReg(JNIEnv* env);
     void parseExtraOpts(char* extraOptsBuf);
@@ -112,7 +118,7 @@
      * Thread creation helpers.
      */
     static int javaCreateThreadEtc(
-                                android_thread_func_t entryFunction, 
+                                android_thread_func_t entryFunction,
                                 void* userData,
                                 const char* threadName,
                                 int32_t threadPriority,
@@ -121,9 +127,6 @@
     static int javaThreadShell(void* args);
 };
 
-// Returns the Unix file descriptor for a ParcelFileDescriptor object
-extern int getParcelFileDescriptorFD(JNIEnv* env, jobject object);
-
 extern CursorWindow * get_window_from_object(JNIEnv * env, jobject javaWindow);
 
 }
diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h
index 32c9a1d5..bfe13f0 100644
--- a/include/binder/Parcel.h
+++ b/include/binder/Parcel.h
@@ -53,7 +53,8 @@
     
     status_t            setData(const uint8_t* buffer, size_t len);
 
-    status_t            appendFrom(Parcel *parcel, size_t start, size_t len);
+    status_t            appendFrom(const Parcel *parcel,
+                                   size_t start, size_t len);
 
     bool                hasFileDescriptors() const;
 
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index 7930f47..e272839 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -308,6 +308,44 @@
     // 0.3333, EV is -2.
     // Example value: "0.333333333" or "0.5". Read only.
     static const char KEY_EXPOSURE_COMPENSATION_STEP[];
+    // The maximum number of metering areas supported. This is the maximum
+    // length of KEY_METERING_AREAS.
+    // Example value: "0" or "2". Read only.
+    static const char KEY_MAX_NUM_METERING_AREAS[];
+    // Current metering areas. Camera driver uses these areas to decide
+    // exposure.
+    //
+    // Before accessing this parameter, apps should check
+    // KEY_MAX_NUM_METERING_AREAS first to know the maximum number of metering
+    // areas first. If the value is 0, metering area is not supported.
+    //
+    // Each metering area is a rectangle with specified weight. The direction is
+    // relative to the sensor orientation, that is, what the sensor sees. The
+    // direction is not affected by the rotation or mirroring of
+    // CAMERA_CMD_SET_DISPLAY_ORIENTATION. Coordinates of the rectangle range
+    // from -1000 to 1000. (-1000, -1000) is the upper left point. (1000, 1000)
+    // is the lower right point. The length and width of metering areas cannot
+    // be 0 or negative.
+    //
+    // The weight ranges from 1 to 1000. The sum of the weights of all metering
+    // areas must be 1000. Metering areas can partially overlap and the driver
+    // will add the weights in the overlap region. But apps should not set two
+    // metering areas that have identical coordinates.
+    //
+    // A special case of all-zero single metering area means driver to decide
+    // the metering area. For example, the driver may use more signals to decide
+    // metering areas and change them dynamically. Apps can set all-zero if they
+    // want the driver to decide metering areas.
+    //
+    // Metering areas are relative to the current field of view (KEY_ZOOM).
+    // No matter what the zoom level is, (-1000,-1000) represents the top of the
+    // currently visible camera frame. The metering area cannot be set to be
+    // outside the current field of view, even when using zoom.
+    //
+    // No matter what metering areas are, the final exposure are compensated
+    // by KEY_EXPOSURE_COMPENSATION.
+    // Example value: "(-10,-10,0,0,300),(0,0,10,10,700)". Read/write.
+    static const char KEY_METERING_AREAS[];
     // Current zoom value.
     // Example value: "0" or "6". Read/write.
     static const char KEY_ZOOM[];
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index 6a7809a..96828c6 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -269,12 +269,6 @@
     // allocate new GraphicBuffer objects.
     sp<IGraphicBufferAlloc> mGraphicBufferAlloc;
 
-    // mAllocdBuffers is mirror of the list of buffers that SurfaceFlinger is
-    // referencing. This is kept so that gralloc implementations do not need to
-    // properly handle the case where SurfaceFlinger no longer holds a reference
-    // to a buffer, but other processes do.
-    Vector<sp<GraphicBuffer> > mAllocdBuffers;
-
     // mFrameAvailableListener is the listener object that will be called when a
     // new frame becomes available. If it is not NULL it will be called from
     // queueBuffer.
diff --git a/include/media/IMediaPlayerClient.h b/include/media/IMediaPlayerClient.h
index eee6c97..daec1c7 100644
--- a/include/media/IMediaPlayerClient.h
+++ b/include/media/IMediaPlayerClient.h
@@ -28,7 +28,7 @@
 public:
     DECLARE_META_INTERFACE(MediaPlayerClient);
 
-    virtual void notify(int msg, int ext1, int ext2) = 0;
+    virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index 447942b..e1b6dd6 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -55,7 +55,8 @@
 
 
 // callback mechanism for passing messages to MediaPlayer object
-typedef void (*notify_callback_f)(void* cookie, int msg, int ext1, int ext2);
+typedef void (*notify_callback_f)(void* cookie,
+        int msg, int ext1, int ext2, const Parcel *obj);
 
 // abstract base class - use MediaPlayerInterface
 class MediaPlayerBase : public RefBase
@@ -159,9 +160,10 @@
         mCookie = cookie; mNotify = notifyFunc;
     }
 
-    void        sendEvent(int msg, int ext1=0, int ext2=0) {
+    void        sendEvent(int msg, int ext1=0, int ext2=0,
+                          const Parcel *obj=NULL) {
         Mutex::Autolock autoLock(mNotifyLock);
-        if (mNotify) mNotify(mCookie, msg, ext1, ext2);
+        if (mNotify) mNotify(mCookie, msg, ext1, ext2, obj);
     }
 
 private:
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index 528eeb9..748e489 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -37,6 +37,7 @@
     MEDIA_BUFFERING_UPDATE  = 3,
     MEDIA_SEEK_COMPLETE     = 4,
     MEDIA_SET_VIDEO_SIZE    = 5,
+    MEDIA_TIMED_TEXT        = 99,
     MEDIA_ERROR             = 100,
     MEDIA_INFO              = 200,
 };
@@ -129,7 +130,7 @@
 class MediaPlayerListener: virtual public RefBase
 {
 public:
-    virtual void notify(int msg, int ext1, int ext2) = 0;
+    virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) = 0;
 };
 
 class MediaPlayer : public BnMediaPlayerClient,
@@ -166,7 +167,7 @@
             status_t        setLooping(int loop);
             bool            isLooping();
             status_t        setVolume(float leftVolume, float rightVolume);
-            void            notify(int msg, int ext1, int ext2);
+            void            notify(int msg, int ext1, int ext2, const Parcel *obj = NULL);
     static  sp<IMemory>     decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
     static  sp<IMemory>     decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
             status_t        invoke(const Parcel& request, Parcel *reply);
diff --git a/include/surfaceflinger/IGraphicBufferAlloc.h b/include/surfaceflinger/IGraphicBufferAlloc.h
index d996af7..01e4bd9 100644
--- a/include/surfaceflinger/IGraphicBufferAlloc.h
+++ b/include/surfaceflinger/IGraphicBufferAlloc.h
@@ -32,18 +32,10 @@
 public:
     DECLARE_META_INTERFACE(GraphicBufferAlloc);
 
-    /* Create a new GraphicBuffer for the client to use.  The server will
-     * maintain a reference to the newly created GraphicBuffer until
-     * freeAllGraphicBuffers is called.
+    /* Create a new GraphicBuffer for the client to use.
      */
     virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
             PixelFormat format, uint32_t usage) = 0;
-
-    /* Free all but one of the GraphicBuffer objects that the server is
-     * currently referencing. If bufIndex is not a valid index of the buffers
-     * the server is referencing, then all buffers are freed.
-     */
-    virtual void freeAllGraphicBuffersExcept(int bufIndex) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/tts/TtsEngine.h b/include/tts/TtsEngine.h
deleted file mode 100644
index 998e353..0000000
--- a/include/tts/TtsEngine.h
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * 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 <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
-// 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 {
-
-#define ANDROID_TTS_ENGINE_PROPERTY_CONFIG "engineConfig"
-#define ANDROID_TTS_ENGINE_PROPERTY_PITCH  "pitch"
-#define ANDROID_TTS_ENGINE_PROPERTY_RATE   "rate"
-#define ANDROID_TTS_ENGINE_PROPERTY_VOLUME "volume"
-
-
-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. // TODO reword
-// The callback for synthesis completed takes:
-// @param [inout] void *&       - The userdata pointer set in the original
-//                                 synth call
-// @param [in]    uint32_t      - Track sampling rate in Hz
-// @param [in]    uint32_t      - The audio format
-// @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,
-        uint32_t, int, int8_t *&, size_t&, tts_synth_status);
-
-class TtsEngine;
-extern "C" TtsEngine* getTtsEngine();
-
-enum tts_result {
-    TTS_SUCCESS                 = 0,
-    TTS_FAILURE                 = -1,
-    TTS_FEATURE_UNSUPPORTED     = -2,
-    TTS_VALUE_INVALID           = -3,
-    TTS_PROPERTY_UNSUPPORTED    = -4,
-    TTS_PROPERTY_SIZE_TOO_SMALL = -5,
-    TTS_MISSING_RESOURCES       = -6
-};
-
-enum tts_support_result {
-    TTS_LANG_COUNTRY_VAR_AVAILABLE = 2,
-    TTS_LANG_COUNTRY_AVAILABLE = 1,
-    TTS_LANG_AVAILABLE = 0,
-    TTS_LANG_MISSING_DATA = -1,
-    TTS_LANG_NOT_SUPPORTED = -2
-};
-
-class TtsEngine
-{
-public:
-    virtual ~TtsEngine() {}
-
-    // Initialize the TTS engine and returns whether initialization succeeded.
-    // @param synthDoneCBPtr synthesis callback function pointer
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    virtual tts_result init(synthDoneCB_t synthDoneCBPtr, const char *engineConfig);
-
-    // Shut down the TTS engine and releases all associated resources.
-    // @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.
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    virtual tts_result stop();
-
-    // Returns the level of support for the language, country and variant.
-    // @return TTS_LANG_COUNTRY_VAR_AVAILABLE if the language, country and variant are supported,
-    //            and the corresponding resources are correctly installed
-    //         TTS_LANG_COUNTRY_AVAILABLE if the language and country are supported and the
-    //             corresponding resources are correctly installed, but there is no match for
-    //             the specified variant
-    //         TTS_LANG_AVAILABLE if the language is supported and the
-    //             corresponding resources are correctly installed, but there is no match for
-    //             the specified country and variant
-    //         TTS_LANG_MISSING_DATA if the required resources to provide any level of support
-    //             for the language are not correctly installed
-    //         TTS_LANG_NOT_SUPPORTED if the language is not supported by the TTS engine.
-    virtual tts_support_result isLanguageAvailable(const char *lang, const char *country,
-            const char *variant);
-
-    // 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 and country values are coded according to the ISO three
-    // letter codes for languages and countries, as can be retrieved from a java.util.Locale
-    // instance. The variant value is encoded as the variant string retrieved from a
-    // java.util.Locale instance built with that variant data.
-    // @param lang pointer to the ISO three letter code for the language
-    // @param country pointer to the ISO three letter code for the country
-    // @param variant pointer to the variant code
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    virtual tts_result loadLanguage(const char *lang, const char *country, const char *variant);
-
-    // Load the resources associated with the specified language, country and Locale variant.
-    // The loaded language will only be used once a call to setLanguageFromLocale() with the same
-    // language value is issued. Language and country values are coded according to the ISO three
-    // letter codes for languages and countries, as can be retrieved from a java.util.Locale
-    // instance. The variant value is encoded as the variant string retrieved from a
-    // java.util.Locale instance built with that variant data.
-    // @param lang pointer to the ISO three letter code for the language
-    // @param country pointer to the ISO three letter code for the country
-    // @param variant pointer to the variant code
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    virtual tts_result setLanguage(const char *lang, const char *country, const char *variant);
-
-    // Retrieve the currently set language, country and variant, or empty strings if none of
-    // parameters have been set. Language and country are represented by their 3-letter ISO code
-    // @param[out]   pointer to the retrieved 3-letter code language value
-    // @param[out]   pointer to the retrieved 3-letter code country value
-    // @param[out]   pointer to the retrieved variant value
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    virtual tts_result getLanguage(char *language, char *country, char *variant);
-
-    // Notifies the engine what audio parameters should be used for the synthesis.
-    // This is meant to be used as a hint, the engine implementation will set the output values
-    // to those of the synthesis format, based on a given hint.
-    // @param[inout] encoding in: the desired audio sample format
-    //                         out: the format used by the TTS engine
-    // @param[inout] rate in: the desired audio sample rate
-    //                         out: the sample rate used by the TTS engine
-    // @param[inout] channels in: the desired number of audio channels
-    //                         out: the number of channels used by the TTS engine
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    virtual tts_result setAudioFormat(AudioSystem::audio_format& encoding, uint32_t& rate,
-            int& channels);
-
-    // Set a property for the the TTS engine
-    // "size" is the maximum size of "value" for properties "property"
-    // @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,
-    //                  or TTS_VALUE_INVALID
-    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);
-
-    // Synthesize the 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 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:
-    //          o interpret-as
-    //    * phoneme
-    //    * voice:
-    //          o gender,
-    //          o age,
-    //          o variant,
-    //          o name
-    //    * emphasis
-    //    * break:
-    //          o strength,
-    //          o time
-    //    * prosody:
-    //          o pitch,
-    //          o contour,
-    //          o range,
-    //          o rate,
-    //          o duration,
-    //          o volume
-    //    * mark
-    // Differences between this text format and SSML are:
-    //    * full SSML documents are not supported
-    //    * namespaces are not supported
-    // 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, int8_t *buffer,
-            size_t bufferSize, void *userdata);
-
-};
-
-} // namespace android
-
diff --git a/include/utils/AssetManager.h b/include/utils/AssetManager.h
index 9e2bf37..a8c7ddb 100644
--- a/include/utils/AssetManager.h
+++ b/include/utils/AssetManager.h
@@ -222,6 +222,7 @@
     {
         String8 path;
         FileType type;
+        String8 idmap;
     };
 
     Asset* openInPathLocked(const char* fileName, AccessMode mode,
@@ -262,6 +263,16 @@
     void setLocaleLocked(const char* locale);
     void updateResourceParamsLocked() const;
 
+    bool createIdmapFileLocked(const String8& originalPath, const String8& overlayPath,
+                               const String8& idmapPath);
+
+    bool isIdmapStaleLocked(const String8& originalPath, const String8& overlayPath,
+                            const String8& idmapPath);
+
+    Asset* openIdmapLocked(const struct asset_path& ap) const;
+
+    bool getZipEntryCrcLocked(const String8& zipPath, const char* entryFilename, uint32_t* pCrc);
+
     class SharedZip : public RefBase {
     public:
         static sp<SharedZip> get(const String8& path);
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h
index 35792dc..173412e 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/utils/ResourceTypes.h
@@ -1798,9 +1798,9 @@
     ~ResTable();
 
     status_t add(const void* data, size_t size, void* cookie,
-                 bool copyData=false);
+                 bool copyData=false, const void* idmap = NULL);
     status_t add(Asset* asset, void* cookie,
-                 bool copyData=false);
+                 bool copyData=false, const void* idmap = NULL);
     status_t add(ResTable* src);
 
     status_t getError() const;
@@ -2046,6 +2046,24 @@
 
     void getLocales(Vector<String8>* locales) const;
 
+    // Generate an idmap.
+    //
+    // Return value: on success: NO_ERROR; caller is responsible for free-ing
+    // outData (using free(3)). On failure, any status_t value other than
+    // NO_ERROR; the caller should not free outData.
+    status_t createIdmap(const ResTable& overlay, uint32_t originalCrc, uint32_t overlayCrc,
+                         void** outData, size_t* outSize) const;
+
+    enum {
+        IDMAP_HEADER_SIZE_BYTES = 3 * sizeof(uint32_t),
+    };
+    // Retrieve idmap meta-data.
+    //
+    // This function only requires the idmap header (the first
+    // IDMAP_HEADER_SIZE_BYTES) bytes of an idmap file.
+    static bool getIdmapInfo(const void* idmap, size_t size,
+                             uint32_t* pOriginalCrc, uint32_t* pOverlayCrc);
+
 #ifndef HAVE_ANDROID_OS
     void print(bool inclValues) const;
     static String8 normalizeForOutput(const char* input);
@@ -2059,7 +2077,7 @@
     struct bag_set;
 
     status_t add(const void* data, size_t size, void* cookie,
-                 Asset* asset, bool copyData);
+                 Asset* asset, bool copyData, const Asset* idmap);
 
     ssize_t getResourcePackageIndex(uint32_t resID) const;
     ssize_t getEntry(
@@ -2068,7 +2086,7 @@
         const ResTable_type** outType, const ResTable_entry** outEntry,
         const Type** outTypeClass) const;
     status_t parsePackage(
-        const ResTable_package* const pkg, const Header* const header);
+        const ResTable_package* const pkg, const Header* const header, uint32_t idmap_id);
 
     void print_value(const Package* pkg, const Res_value& value) const;
     
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index d57f2c9..a0fc4d0 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -338,7 +338,7 @@
 
 status_t Parcel::setDataCapacity(size_t size)
 {
-    if (size > mDataSize) return continueWrite(size);
+    if (size > mDataCapacity) return continueWrite(size);
     return NO_ERROR;
 }
 
@@ -353,12 +353,12 @@
     return err;
 }
 
-status_t Parcel::appendFrom(Parcel *parcel, size_t offset, size_t len)
+status_t Parcel::appendFrom(const Parcel *parcel, size_t offset, size_t len)
 {
     const sp<ProcessState> proc(ProcessState::self());
     status_t err;
-    uint8_t *data = parcel->mData;
-    size_t *objects = parcel->mObjects;
+    const uint8_t *data = parcel->mData;
+    const size_t *objects = parcel->mObjects;
     size_t size = parcel->mObjectsSize;
     int startPos = mDataPos;
     int firstIndex = -1, lastIndex = -2;
@@ -386,10 +386,12 @@
     }
     int numObjects = lastIndex - firstIndex + 1;
 
-    // grow data
-    err = growData(len);
-    if (err != NO_ERROR) {
-        return err;
+    if ((mDataSize+len) > mDataCapacity) {
+        // grow data
+        err = growData(len);
+        if (err != NO_ERROR) {
+            return err;
+        }
     }
 
     // append data
@@ -1384,8 +1386,10 @@
                 return NO_MEMORY;
             }
         } else {
-            mDataSize = desired;
-            LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
+            if (mDataSize > desired) {
+                mDataSize = desired;
+                LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
+            }
             if (mDataPos > desired) {
                 mDataPos = desired;
                 LOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos);
diff --git a/libs/camera/CameraParameters.cpp b/libs/camera/CameraParameters.cpp
index 1e7abae..214cd4d 100644
--- a/libs/camera/CameraParameters.cpp
+++ b/libs/camera/CameraParameters.cpp
@@ -68,6 +68,8 @@
 const char CameraParameters::KEY_MAX_EXPOSURE_COMPENSATION[] = "max-exposure-compensation";
 const char CameraParameters::KEY_MIN_EXPOSURE_COMPENSATION[] = "min-exposure-compensation";
 const char CameraParameters::KEY_EXPOSURE_COMPENSATION_STEP[] = "exposure-compensation-step";
+const char CameraParameters::KEY_MAX_NUM_METERING_AREAS[] = "max-num-metering-areas";
+const char CameraParameters::KEY_METERING_AREAS[] = "metering-areas";
 const char CameraParameters::KEY_ZOOM[] = "zoom";
 const char CameraParameters::KEY_MAX_ZOOM[] = "max-zoom";
 const char CameraParameters::KEY_ZOOM_RATIOS[] = "zoom-ratios";
diff --git a/libs/gui/IGraphicBufferAlloc.cpp b/libs/gui/IGraphicBufferAlloc.cpp
index e05da72..0cd51da 100644
--- a/libs/gui/IGraphicBufferAlloc.cpp
+++ b/libs/gui/IGraphicBufferAlloc.cpp
@@ -32,7 +32,6 @@
 
 enum {
     CREATE_GRAPHIC_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
-    FREE_ALL_GRAPHIC_BUFFERS_EXCEPT,
 };
 
 class BpGraphicBufferAlloc : public BpInterface<IGraphicBufferAlloc>
@@ -46,8 +45,7 @@
     virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
             PixelFormat format, uint32_t usage) {
         Parcel data, reply;
-        data.writeInterfaceToken(
-                IGraphicBufferAlloc::getInterfaceDescriptor());
+        data.writeInterfaceToken(IGraphicBufferAlloc::getInterfaceDescriptor());
         data.writeInt32(w);
         data.writeInt32(h);
         data.writeInt32(format);
@@ -58,17 +56,12 @@
         if (nonNull) {
             graphicBuffer = new GraphicBuffer();
             reply.read(*graphicBuffer);
+            // reply.readStrongBinder();
+            // here we don't even have to read the BufferReference from
+            // the parcel, it'll die with the parcel.
         }
         return graphicBuffer;
     }
-
-    virtual void freeAllGraphicBuffersExcept(int bufIdx) {
-        Parcel data, reply;
-        data.writeInterfaceToken(
-                IGraphicBufferAlloc::getInterfaceDescriptor());
-        data.writeInt32(bufIdx);
-        remote()->transact(FREE_ALL_GRAPHIC_BUFFERS_EXCEPT, data, &reply);
-    }
 };
 
 IMPLEMENT_META_INTERFACE(GraphicBufferAlloc, "android.ui.IGraphicBufferAlloc");
@@ -80,6 +73,17 @@
 {
     // codes that don't require permission check
 
+    /* BufferReference just keeps a strong reference to a
+     * GraphicBuffer until it is destroyed (that is, until
+     * no local or remote process have a reference to it).
+     */
+    class BufferReference : public BBinder {
+        sp<GraphicBuffer> buffer;
+    public:
+        BufferReference(const sp<GraphicBuffer>& buffer) : buffer(buffer) { }
+    };
+
+
     switch(code) {
         case CREATE_GRAPHIC_BUFFER: {
             CHECK_INTERFACE(IGraphicBufferAlloc, data, reply);
@@ -91,15 +95,16 @@
             reply->writeInt32(result != 0);
             if (result != 0) {
                 reply->write(*result);
+                // We add a BufferReference to this parcel to make sure the
+                // buffer stays alive until the GraphicBuffer object on
+                // the other side has been created.
+                // This is needed so that the buffer handle can be
+                // registered before the buffer is destroyed on implementations
+                // that do not use file-descriptors to track their buffers.
+                reply->writeStrongBinder( new BufferReference(result) );
             }
             return NO_ERROR;
         } break;
-        case FREE_ALL_GRAPHIC_BUFFERS_EXCEPT: {
-            CHECK_INTERFACE(IGraphicBufferAlloc, data, reply);
-            int bufIdx = data.readInt32();
-            freeAllGraphicBuffersExcept(bufIdx);
-            return NO_ERROR;
-        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 38599bf..39418f0 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -175,7 +175,6 @@
             mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
             mSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
         }
-        mAllocdBuffers.add(graphicBuffer);
     }
     return graphicBuffer;
 }
@@ -473,19 +472,6 @@
             mSlots[i].mEglDisplay = EGL_NO_DISPLAY;
         }
     }
-
-    int exceptBuf = -1;
-    for (size_t i = 0; i < mAllocdBuffers.size(); i++) {
-        if (mAllocdBuffers[i] == mCurrentTextureBuf) {
-            exceptBuf = i;
-            break;
-        }
-    }
-    mAllocdBuffers.clear();
-    if (exceptBuf >= 0) {
-        mAllocdBuffers.add(mCurrentTextureBuf);
-    }
-    mGraphicBufferAlloc->freeAllGraphicBuffersExcept(exceptBuf);
 }
 
 EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
diff --git a/libs/rs/driver/rsdBcc.cpp b/libs/rs/driver/rsdBcc.cpp
index 6c5a55b..20aef49 100644
--- a/libs/rs/driver/rsdBcc.cpp
+++ b/libs/rs/driver/rsdBcc.cpp
@@ -381,7 +381,7 @@
     mtls.zEnd = rsMax((uint32_t)1, mtls.zEnd);
     mtls.arrayEnd = rsMax((uint32_t)1, mtls.arrayEnd);
 
-    rsAssert(ain->getType()->getDimZ() == 0);
+    rsAssert(!ain || (ain->getType()->getDimZ() == 0));
 
     Context *mrsc = (Context *)rsc;
     Script * oldTLS = setTLS(s);
diff --git a/libs/usb/src/com/android/future/usb/UsbManager.java b/libs/usb/src/com/android/future/usb/UsbManager.java
index d424b63..91d8e8a 100644
--- a/libs/usb/src/com/android/future/usb/UsbManager.java
+++ b/libs/usb/src/com/android/future/usb/UsbManager.java
@@ -28,7 +28,7 @@
 import android.util.Log;
 
 /**
- * This class allows you to access the state of USB, both in host and device mode.
+ * This is a wrapper class for the USB Manager to support USB accessories.
  *
  * <p>You can obtain an instance of this class by calling {@link #getInstance}
  *
@@ -141,7 +141,7 @@
     /**
      * Returns true if the caller has permission to access the accessory.
      * Permission might have been granted temporarily via
-     * {@link #requestPermission(android.hardware.usb.UsbAccessory} or
+     * {@link #requestPermission} or
      * by the user choosing the caller as the default application for the accessory.
      *
      * @param accessory to check permissions for
diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp
index 6e57d93..e41dd39 100644
--- a/libs/utils/AssetManager.cpp
+++ b/libs/utils/AssetManager.cpp
@@ -37,6 +37,19 @@
 #include <errno.h>
 #include <assert.h>
 #include <strings.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#ifndef TEMP_FAILURE_RETRY
+/* Used to retry syscalls that can return EINTR. */
+#define TEMP_FAILURE_RETRY(exp) ({         \
+    typeof (exp) _rc;                      \
+    do {                                   \
+        _rc = (exp);                       \
+    } while (_rc == -1 && errno == EINTR); \
+    _rc; })
+#endif
 
 using namespace android;
 
@@ -49,6 +62,7 @@
 static const char* kAssetsRoot = "assets";
 static const char* kAppZipName = NULL; //"classes.jar";
 static const char* kSystemAssets = "framework/framework-res.apk";
+static const char* kIdmapCacheDir = "resource-cache";
 
 static const char* kExcludeExtension = ".EXCLUDE";
 
@@ -56,6 +70,35 @@
 
 static volatile int32_t gCount = 0;
 
+namespace {
+    // Transform string /a/b/c.apk to /data/resource-cache/a@b@c.apk@idmap
+    String8 idmapPathForPackagePath(const String8& pkgPath)
+    {
+        const char* root = getenv("ANDROID_DATA");
+        LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_DATA not set");
+        String8 path(root);
+        path.appendPath(kIdmapCacheDir);
+
+        char buf[256]; // 256 chars should be enough for anyone...
+        strncpy(buf, pkgPath.string(), 255);
+        buf[255] = '\0';
+        char* filename = buf;
+        while (*filename && *filename == '/') {
+            ++filename;
+        }
+        char* p = filename;
+        while (*p) {
+            if (*p == '/') {
+                *p = '@';
+            }
+            ++p;
+        }
+        path.appendPath(filename);
+        path.append("@idmap");
+
+        return path;
+    }
+}
 
 /*
  * ===========================================================================
@@ -123,7 +166,7 @@
             return true;
         }
     }
-    
+
     LOGV("In %p Asset %s path: %s", this,
          ap.type == kFileTypeDirectory ? "dir" : "zip", ap.path.string());
 
@@ -134,9 +177,181 @@
         *cookie = (void*)mAssetPaths.size();
     }
 
+    // add overlay packages for /system/framework; apps are handled by the
+    // (Java) package manager
+    if (strncmp(path.string(), "/system/framework/", 18) == 0) {
+        // When there is an environment variable for /vendor, this
+        // should be changed to something similar to how ANDROID_ROOT
+        // and ANDROID_DATA are used in this file.
+        String8 overlayPath("/vendor/overlay/framework/");
+        overlayPath.append(path.getPathLeaf());
+        if (TEMP_FAILURE_RETRY(access(overlayPath.string(), R_OK)) == 0) {
+            asset_path oap;
+            oap.path = overlayPath;
+            oap.type = ::getFileType(overlayPath.string());
+            bool addOverlay = (oap.type == kFileTypeRegular); // only .apks supported as overlay
+            if (addOverlay) {
+                oap.idmap = idmapPathForPackagePath(overlayPath);
+
+                if (isIdmapStaleLocked(ap.path, oap.path, oap.idmap)) {
+                    addOverlay = createIdmapFileLocked(ap.path, oap.path, oap.idmap);
+                }
+            }
+            if (addOverlay) {
+                mAssetPaths.add(oap);
+            } else {
+                LOGW("failed to add overlay package %s\n", overlayPath.string());
+            }
+        }
+    }
+
     return true;
 }
 
+bool AssetManager::isIdmapStaleLocked(const String8& originalPath, const String8& overlayPath,
+                                      const String8& idmapPath)
+{
+    struct stat st;
+    if (TEMP_FAILURE_RETRY(stat(idmapPath.string(), &st)) == -1) {
+        if (errno == ENOENT) {
+            return true; // non-existing idmap is always stale
+        } else {
+            LOGW("failed to stat file %s: %s\n", idmapPath.string(), strerror(errno));
+            return false;
+        }
+    }
+    if (st.st_size < ResTable::IDMAP_HEADER_SIZE_BYTES) {
+        LOGW("file %s has unexpectedly small size=%zd\n", idmapPath.string(), (size_t)st.st_size);
+        return false;
+    }
+    int fd = TEMP_FAILURE_RETRY(::open(idmapPath.string(), O_RDONLY));
+    if (fd == -1) {
+        LOGW("failed to open file %s: %s\n", idmapPath.string(), strerror(errno));
+        return false;
+    }
+    char buf[ResTable::IDMAP_HEADER_SIZE_BYTES];
+    ssize_t bytesLeft = ResTable::IDMAP_HEADER_SIZE_BYTES;
+    for (;;) {
+        ssize_t r = TEMP_FAILURE_RETRY(read(fd, buf + ResTable::IDMAP_HEADER_SIZE_BYTES - bytesLeft,
+                                            bytesLeft));
+        if (r < 0) {
+            TEMP_FAILURE_RETRY(close(fd));
+            return false;
+        }
+        bytesLeft -= r;
+        if (bytesLeft == 0) {
+            break;
+        }
+    }
+    TEMP_FAILURE_RETRY(close(fd));
+
+    uint32_t cachedOriginalCrc, cachedOverlayCrc;
+    if (!ResTable::getIdmapInfo(buf, ResTable::IDMAP_HEADER_SIZE_BYTES,
+                                &cachedOriginalCrc, &cachedOverlayCrc)) {
+        return false;
+    }
+
+    uint32_t actualOriginalCrc, actualOverlayCrc;
+    if (!getZipEntryCrcLocked(originalPath, "resources.arsc", &actualOriginalCrc)) {
+        return false;
+    }
+    if (!getZipEntryCrcLocked(overlayPath, "resources.arsc", &actualOverlayCrc)) {
+        return false;
+    }
+    return cachedOriginalCrc != actualOriginalCrc || cachedOverlayCrc != actualOverlayCrc;
+}
+
+bool AssetManager::getZipEntryCrcLocked(const String8& zipPath, const char* entryFilename,
+                                        uint32_t* pCrc)
+{
+    asset_path ap;
+    ap.path = zipPath;
+    const ZipFileRO* zip = getZipFileLocked(ap);
+    if (zip == NULL) {
+        return false;
+    }
+    const ZipEntryRO entry = zip->findEntryByName(entryFilename);
+    if (entry == NULL) {
+        return false;
+    }
+    if (!zip->getEntryInfo(entry, NULL, NULL, NULL, NULL, NULL, (long*)pCrc)) {
+        return false;
+    }
+    return true;
+}
+
+bool AssetManager::createIdmapFileLocked(const String8& originalPath, const String8& overlayPath,
+                                         const String8& idmapPath)
+{
+    LOGD("%s: originalPath=%s overlayPath=%s idmapPath=%s\n",
+         __FUNCTION__, originalPath.string(), overlayPath.string(), idmapPath.string());
+    ResTable tables[2];
+    const String8* paths[2] = { &originalPath, &overlayPath };
+    uint32_t originalCrc, overlayCrc;
+    bool retval = false;
+    ssize_t offset = 0;
+    int fd = 0;
+    uint32_t* data = NULL;
+    size_t size;
+
+    for (int i = 0; i < 2; ++i) {
+        asset_path ap;
+        ap.type = kFileTypeRegular;
+        ap.path = *paths[i];
+        Asset* ass = openNonAssetInPathLocked("resources.arsc", Asset::ACCESS_BUFFER, ap);
+        if (ass == NULL) {
+            LOGW("failed to find resources.arsc in %s\n", ap.path.string());
+            goto error;
+        }
+        tables[i].add(ass, (void*)1, false);
+    }
+
+    if (!getZipEntryCrcLocked(originalPath, "resources.arsc", &originalCrc)) {
+        LOGW("failed to retrieve crc for resources.arsc in %s\n", originalPath.string());
+        goto error;
+    }
+    if (!getZipEntryCrcLocked(overlayPath, "resources.arsc", &overlayCrc)) {
+        LOGW("failed to retrieve crc for resources.arsc in %s\n", overlayPath.string());
+        goto error;
+    }
+
+    if (tables[0].createIdmap(tables[1], originalCrc, overlayCrc,
+                              (void**)&data, &size) != NO_ERROR) {
+        LOGW("failed to generate idmap data for file %s\n", idmapPath.string());
+        goto error;
+    }
+
+    // This should be abstracted (eg replaced by a stand-alone
+    // application like dexopt, triggered by something equivalent to
+    // installd).
+    fd = TEMP_FAILURE_RETRY(::open(idmapPath.string(), O_WRONLY | O_CREAT | O_TRUNC, 0644));
+    if (fd == -1) {
+        LOGW("failed to write idmap file %s (open: %s)\n", idmapPath.string(), strerror(errno));
+        goto error_free;
+    }
+    for (;;) {
+        ssize_t written = TEMP_FAILURE_RETRY(write(fd, data + offset, size));
+        if (written < 0) {
+            LOGW("failed to write idmap file %s (write: %s)\n", idmapPath.string(),
+                 strerror(errno));
+            goto error_close;
+        }
+        size -= (size_t)written;
+        offset += written;
+        if (size == 0) {
+            break;
+        }
+    }
+
+    retval = true;
+error_close:
+    TEMP_FAILURE_RETRY(close(fd));
+error_free:
+    free(data);
+error:
+    return retval;
+}
+
 bool AssetManager::addDefaultAssets()
 {
     const char* root = getenv("ANDROID_ROOT");
@@ -405,6 +620,7 @@
         ResTable* sharedRes = NULL;
         bool shared = true;
         const asset_path& ap = mAssetPaths.itemAt(i);
+        Asset* idmap = openIdmapLocked(ap);
         LOGV("Looking for resource asset in '%s'\n", ap.path.string());
         if (ap.type != kFileTypeDirectory) {
             if (i == 0) {
@@ -434,7 +650,7 @@
                     // can quickly copy it out for others.
                     LOGV("Creating shared resources for %s", ap.path.string());
                     sharedRes = new ResTable();
-                    sharedRes->add(ass, (void*)(i+1), false);
+                    sharedRes->add(ass, (void*)(i+1), false, idmap);
                     sharedRes = const_cast<AssetManager*>(this)->
                         mZipSet.setZipResourceTable(ap.path, sharedRes);
                 }
@@ -458,7 +674,7 @@
                 rt->add(sharedRes);
             } else {
                 LOGV("Parsing resources for %s", ap.path.string());
-                rt->add(ass, (void*)(i+1), !shared);
+                rt->add(ass, (void*)(i+1), !shared, idmap);
             }
 
             if (!shared) {
@@ -499,6 +715,21 @@
     res->setParameters(mConfig);
 }
 
+Asset* AssetManager::openIdmapLocked(const struct asset_path& ap) const
+{
+    Asset* ass = NULL;
+    if (ap.idmap.size() != 0) {
+        ass = const_cast<AssetManager*>(this)->
+            openAssetFromFileLocked(ap.idmap, Asset::ACCESS_BUFFER);
+        if (ass) {
+            LOGV("loading idmap %s\n", ap.idmap.string());
+        } else {
+            LOGW("failed to load idmap %s\n", ap.idmap.string());
+        }
+    }
+    return ass;
+}
+
 const ResTable& AssetManager::getResources(bool required) const
 {
     const ResTable* rt = getResTable(required);
diff --git a/libs/utils/README b/libs/utils/README
index 36a706d..01741e0 100644
--- a/libs/utils/README
+++ b/libs/utils/README
@@ -1,4 +1,6 @@
 Android Utility Function Library
+================================
+
 
 If you need a feature that is native to Linux but not present on other
 platforms, construct a platform-dependent implementation that shares
@@ -12,3 +14,276 @@
 layer.  The goal is to provide an optimized solution for Linux with
 reasonable implementations for other platforms.
 
+
+
+Resource overlay
+================
+
+
+Introduction
+------------
+
+Overlay packages are special .apk files which provide no code but
+additional resource values (and possibly new configurations) for
+resources in other packages. When an application requests resources,
+the system will return values from either the application's original
+package or any associated overlay package. Any redirection is completely
+transparent to the calling application.
+
+Resource values have the following precedence table, listed in
+descending precedence.
+
+ * overlay package, matching config (eg res/values-en-land)
+
+ * original package, matching config
+
+ * overlay package, no config (eg res/values)
+
+ * original package, no config
+
+During compilation, overlay packages are differentiated from regular
+packages by passing the -o flag to aapt.
+
+
+Background
+----------
+
+This section provides generic background material on resources in
+Android.
+
+
+How resources are bundled in .apk files
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Android .apk files are .zip files, usually housing .dex code,
+certificates and resources, though packages containing resources but
+no code are possible. Resources can be divided into the following
+categories; a `configuration' indicates a set of phone language, display
+density, network operator, etc.
+
+ * assets: uncompressed, raw files packaged as part of an .apk and
+           explicitly referenced by filename. These files are
+           independent of configuration.
+
+ * res/drawable: bitmap or xml graphics. Each file may have different
+                 values depending on configuration.
+
+ * res/values: integers, strings, etc. Each resource may have different
+               values depending on configuration.
+
+Resource meta information and information proper is stored in a binary
+format in a named file resources.arsc, bundled as part of the .apk.
+
+Resource IDs and lookup
+~~~~~~~~~~~~~~~~~~~~~~~
+During compilation, the aapt tool gathers application resources and
+generates a resources.arsc file. Each resource name is assigned an
+integer ID 0xppttiii (translated to a symbolic name via R.java), where
+
+ * pp: corresponds to the package namespace (details below).
+
+ * tt: corresponds to the resource type (string, int, etc). Every
+       resource of the same type within the same package has the same
+       tt value, but depending on available types, the actual numerical
+       value may be different between packages.
+
+ * iiii: sequential number, assigned in the order resources are found.
+
+Resource values are specified paired with a set of configuration
+constraints (the default being the empty set), eg res/values-sv-port
+which imposes restrictions on language (Swedish) and display orientation
+(portrait). During lookup, every constraint set is matched against the
+current configuration, and the value corresponding to the best matching
+constraint set is returned (ResourceTypes.{h,cpp}).
+
+Parsing of resources.arsc is handled by ResourceTypes.cpp; this utility
+is governed by AssetManager.cpp, which tracks loaded resources per
+process.
+
+Assets are looked up by path and filename in AssetManager.cpp. The path
+to resources in res/drawable are located by ResourceTypes.cpp and then
+handled like assets by AssetManager.cpp. Other resources are handled
+solely by ResourceTypes.cpp.
+
+Package ID as namespace
+~~~~~~~~~~~~~~~~~~~~~~~
+The pp part of a resource ID defines a namespace. Android currently
+defines two namespaces:
+
+ * 0x01: system resources (pre-installed in framework-res.apk)
+
+ * 0x7f: application resources (bundled in the application .apk)
+
+ResourceTypes.cpp supports package IDs between 0x01 and 0x7f
+(inclusive); values outside this range are invalid.
+
+Each running (Dalvik) process is assigned a unique instance of
+AssetManager, which in turn keeps a forest structure of loaded
+resource.arsc files. Normally, this forest is structured as follows,
+where mPackageMap is the internal vector employed in ResourceTypes.cpp.
+
+mPackageMap[0x00] -> system package
+mPackageMap[0x01] -> NULL
+mPackageMap[0x02] -> NULL
+...
+mPackageMap[0x7f - 2] -> NULL
+mPackageMap[0x7f - 1] -> application package
+
+
+
+The resource overlay extension
+------------------------------
+
+The resource overlay mechanism aims to (partly) shadow and extend
+existing resources with new values for defined and new configurations.
+Technically, this is achieved by adding resource-only packages (called
+overlay packages) to existing resource namespaces, like so:
+
+mPackageMap[0x00] -> system package -> system overlay package
+mPackageMap[0x01] -> NULL
+mPackageMap[0x02] -> NULL
+...
+mPackageMap[0x7f - 2] -> NULL
+mPackageMap[0x7f - 1] -> application package -> overlay 1 -> overlay 2
+
+The use of overlay resources is completely transparent to
+applications; no additional resource identifiers are introduced, only
+configuration/value pairs. Any number of overlay packages may be loaded
+at a time; overlay packages are agnostic to what they target -- both
+system and application resources are fair game.
+
+The package targeted by an overlay package is called the target or
+original package.
+
+Resource overlay operates on symbolic resources names. Hence, to
+override the string/str1 resources in a package, the overlay package
+would include a resource also named string/str1. The end user does not
+have to worry about the numeric resources IDs assigned by aapt, as this
+is resolved automatically by the system.
+
+As of this writing, the use of resource overlay has not been fully
+explored. Until it has, only OEMs are trusted to use resource overlay.
+For this reason, overlay packages must reside in /system/overlay.
+
+
+Resource ID mapping
+~~~~~~~~~~~~~~~~~~~
+Resource identifiers must be coherent within the same namespace (ie
+PackageGroup in ResourceTypes.cpp). Calling applications will refer to
+resources using the IDs defined in the original package, but there is no
+guarantee aapt has assigned the same ID to the corresponding resource in
+an overlay package. To translate between the two, a resource ID mapping
+{original ID -> overlay ID} is created during package installation
+(PackageManagerService.java) and used during resource lookup. The
+mapping is stored in /data/resource-cache, with a @idmap file name
+suffix.
+
+The idmap file format is documented in a separate section, below.
+
+
+Package management
+~~~~~~~~~~~~~~~~~~
+Packages are managed by the PackageManagerService. Addition and removal
+of packages are monitored via the inotify framework, exposed via
+android.os.FileObserver.
+
+During initialization of a Dalvik process, ActivityThread.java requests
+the process' AssetManager (by proxy, via AssetManager.java and JNI)
+to load a list of packages. This list includes overlay packages, if
+present.
+
+When a target package or a corresponding overlay package is installed,
+the target package's process is stopped and a new idmap is generated.
+This is similar to how applications are stopped when their packages are
+upgraded.
+
+
+Creating overlay packages
+-------------------------
+
+Overlay packages should contain no code, define (some) resources with
+the same type and name as in the original package, and be compiled with
+the -o flag passed to aapt.
+
+The aapt -o flag instructs aapt to create an overlay package.
+Technically, this means the package will be assigned package id 0x00.
+
+There are no restrictions on overlay packages names, though the naming
+convention <original.package.name>.overlay.<name> is recommended.
+
+
+Example overlay package
+~~~~~~~~~~~~~~~~~~~~~~~
+
+To overlay the resource bool/b in package com.foo.bar, to be applied
+when the display is in landscape mode, create a new package with
+no source code and a single .xml file under res/values-land, with
+an entry for bool/b. Compile with aapt -o and place the results in
+/system/overlay by adding the following to Android.mk:
+
+LOCAL_AAPT_FLAGS := -o com.foo.bar
+LOCAL_MODULE_PATH := $(TARGET_OUT)/overlay
+
+
+The ID map (idmap) file format
+------------------------------
+
+The idmap format is designed for lookup performance. However, leading
+and trailing undefined overlay values are discarded to reduce the memory
+footprint.
+
+
+idmap grammar
+~~~~~~~~~~~~~
+All atoms (names in square brackets) are uint32_t integers. The
+idmap-magic constant spells "idmp" in ASCII. Offsets are given relative
+to the data_header, not to the beginning of the file.
+
+map          := header data
+header       := idmap-magic <crc32-original-pkg> <crc32-overlay-pkg>
+idmap-magic  := <0x706d6469>
+data         := data_header type_block+
+data_header  := <m> header_block{m}
+header_block := <0> | <type_block_offset>
+type_block   := <n> <id_offset> entry{n}
+entry        := <resource_id_in_target_package>
+
+
+idmap example
+~~~~~~~~~~~~~
+Given a pair of target and overlay packages with CRC sums 0x216a8fe2
+and 0x6b9beaec, each defining the following resources
+
+Name          Target package  Overlay package
+string/str0   0x7f010000      -
+string/str1   0x7f010001      0x7f010000
+string/str2   0x7f010002      -
+string/str3   0x7f010003      0x7f010001
+string/str4   0x7f010004      -
+bool/bool0    0x7f020000      -
+integer/int0  0x7f030000      0x7f020000
+integer/int1  0x7f030001      -
+
+the corresponding resource map is
+
+0x706d6469 0x216a8fe2 0x6b9beaec 0x00000003 \
+0x00000004 0x00000000 0x00000009 0x00000003 \
+0x00000001 0x7f010000 0x00000000 0x7f010001 \
+0x00000001 0x00000000 0x7f020000
+
+or, formatted differently
+
+0x706d6469  # magic: all idmap files begin with this constant
+0x216a8fe2  # CRC32 of the resources.arsc file in the original package
+0x6b9beaec  # CRC32 of the resources.arsc file in the overlay package
+0x00000003  # header; three types (string, bool, integer) in the target package
+0x00000004  #   header_block for type 0 (string) is located at offset 4
+0x00000000  #   no bool type exists in overlay package -> no header_block
+0x00000009  #   header_block for type 2 (integer) is located at offset 9
+0x00000003  # header_block for string; overlay IDs span 3 elements
+0x00000001  #   the first string in target package is entry 1 == offset
+0x7f010000  #   target 0x7f01001 -> overlay 0x7f010000
+0x00000000  #   str2 not defined in overlay package
+0x7f010001  #   target 0x7f010003 -> overlay 0x7f010001
+0x00000001  # header_block for integer; overlay IDs span 1 element
+0x00000000  #   offset == 0
+0x7f020000  #   target 0x7f030000 -> overlay 0x7f020000
diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp
index ac9cdf9..784c9d2 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/utils/ResourceTypes.cpp
@@ -63,6 +63,10 @@
 #endif
 #endif
 
+#define IDMAP_MAGIC         0x706d6469
+// size measured in sizeof(uint32_t)
+#define IDMAP_HEADER_SIZE (ResTable::IDMAP_HEADER_SIZE_BYTES / sizeof(uint32_t))
+
 static void printToLogFunc(void* cookie, const char* txt)
 {
     LOGV("%s", txt);
@@ -214,6 +218,81 @@
     outData->colors = (uint32_t*) data;
 }
 
+static bool assertIdmapHeader(const uint32_t* map, size_t sizeBytes)
+{
+    if (sizeBytes < ResTable::IDMAP_HEADER_SIZE_BYTES) {
+        LOGW("idmap assertion failed: size=%d bytes\n", sizeBytes);
+        return false;
+    }
+    if (*map != htodl(IDMAP_MAGIC)) { // htodl: map data expected to be in correct endianess
+        LOGW("idmap assertion failed: invalid magic found (is 0x%08x, expected 0x%08x)\n",
+             *map, htodl(IDMAP_MAGIC));
+        return false;
+    }
+    return true;
+}
+
+static status_t idmapLookup(const uint32_t* map, size_t sizeBytes, uint32_t key, uint32_t* outValue)
+{
+    // see README for details on the format of map
+    if (!assertIdmapHeader(map, sizeBytes)) {
+        return UNKNOWN_ERROR;
+    }
+    map = map + IDMAP_HEADER_SIZE; // skip ahead to data segment
+    // size of data block, in uint32_t
+    const size_t size = (sizeBytes - ResTable::IDMAP_HEADER_SIZE_BYTES) / sizeof(uint32_t);
+    const uint32_t type = Res_GETTYPE(key) + 1; // add one, idmap stores "public" type id
+    const uint32_t entry = Res_GETENTRY(key);
+    const uint32_t typeCount = *map;
+
+    if (type > typeCount) {
+        LOGW("Resource ID map: type=%d exceeds number of types=%d\n", type, typeCount);
+        return UNKNOWN_ERROR;
+    }
+    if (typeCount > size) {
+        LOGW("Resource ID map: number of types=%d exceeds size of map=%d\n", typeCount, size);
+        return UNKNOWN_ERROR;
+    }
+    const uint32_t typeOffset = map[type];
+    if (typeOffset == 0) {
+        *outValue = 0;
+        return NO_ERROR;
+    }
+    if (typeOffset + 1 > size) {
+        LOGW("Resource ID map: type offset=%d exceeds reasonable value, size of map=%d\n",
+             typeOffset, size);
+        return UNKNOWN_ERROR;
+    }
+    const uint32_t entryCount = map[typeOffset];
+    const uint32_t entryOffset = map[typeOffset + 1];
+    if (entryCount == 0 || entry < entryOffset || entry - entryOffset > entryCount - 1) {
+        *outValue = 0;
+        return NO_ERROR;
+    }
+    const uint32_t index = typeOffset + 2 + entry - entryOffset;
+    if (index > size) {
+        LOGW("Resource ID map: entry index=%d exceeds size of map=%d\n", index, size);
+        *outValue = 0;
+        return NO_ERROR;
+    }
+    *outValue = map[index];
+
+    return NO_ERROR;
+}
+
+static status_t getIdmapPackageId(const uint32_t* map, size_t mapSize, uint32_t *outId)
+{
+    if (!assertIdmapHeader(map, mapSize)) {
+        return UNKNOWN_ERROR;
+    }
+    const uint32_t* p = map + IDMAP_HEADER_SIZE + 1;
+    while (*p == 0) {
+        ++p;
+    }
+    *outId = (map[*p + IDMAP_HEADER_SIZE + 2] >> 24) & 0x000000ff;
+    return NO_ERROR;
+}
+
 Res_png_9patch* Res_png_9patch::deserialize(const void* inData)
 {
     if (sizeof(void*) != sizeof(int32_t)) {
@@ -1290,7 +1369,13 @@
 
 struct ResTable::Header
 {
-    Header(ResTable* _owner) : owner(_owner), ownedData(NULL), header(NULL) { }
+    Header(ResTable* _owner) : owner(_owner), ownedData(NULL), header(NULL),
+        resourceIDMap(NULL), resourceIDMapSize(0) { }
+
+    ~Header()
+    {
+        free(resourceIDMap);
+    }
 
     ResTable* const                 owner;
     void*                           ownedData;
@@ -1301,6 +1386,8 @@
     void*                           cookie;
 
     ResStringPool                   values;
+    uint32_t*                       resourceIDMap;
+    size_t                          resourceIDMapSize;
 };
 
 struct ResTable::Type
@@ -1716,12 +1803,13 @@
     return ((ssize_t)mPackageMap[Res_GETPACKAGE(resID)+1])-1;
 }
 
-status_t ResTable::add(const void* data, size_t size, void* cookie, bool copyData)
+status_t ResTable::add(const void* data, size_t size, void* cookie, bool copyData,
+                       const void* idmap)
 {
-    return add(data, size, cookie, NULL, copyData);
+    return add(data, size, cookie, NULL, copyData, reinterpret_cast<const Asset*>(idmap));
 }
 
-status_t ResTable::add(Asset* asset, void* cookie, bool copyData)
+status_t ResTable::add(Asset* asset, void* cookie, bool copyData, const void* idmap)
 {
     const void* data = asset->getBuffer(true);
     if (data == NULL) {
@@ -1729,7 +1817,7 @@
         return UNKNOWN_ERROR;
     }
     size_t size = (size_t)asset->getLength();
-    return add(data, size, cookie, asset, copyData);
+    return add(data, size, cookie, asset, copyData, reinterpret_cast<const Asset*>(idmap));
 }
 
 status_t ResTable::add(ResTable* src)
@@ -1757,19 +1845,30 @@
 }
 
 status_t ResTable::add(const void* data, size_t size, void* cookie,
-                       Asset* asset, bool copyData)
+                       Asset* asset, bool copyData, const Asset* idmap)
 {
     if (!data) return NO_ERROR;
     Header* header = new Header(this);
     header->index = mHeaders.size();
     header->cookie = cookie;
+    if (idmap != NULL) {
+        const size_t idmap_size = idmap->getLength();
+        const void* idmap_data = const_cast<Asset*>(idmap)->getBuffer(true);
+        header->resourceIDMap = (uint32_t*)malloc(idmap_size);
+        if (header->resourceIDMap == NULL) {
+            delete header;
+            return (mError = NO_MEMORY);
+        }
+        memcpy((void*)header->resourceIDMap, idmap_data, idmap_size);
+        header->resourceIDMapSize = idmap_size;
+    }
     mHeaders.add(header);
 
     const bool notDeviceEndian = htods(0xf0) != 0xf0;
 
     LOAD_TABLE_NOISY(
-        LOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%p, asset=%p, copy=%d\n",
-             data, size, cookie, asset, copyData));
+        LOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%p, asset=%p, copy=%d "
+             "idmap=%p\n", data, size, cookie, asset, copyData, idmap));
     
     if (copyData || notDeviceEndian) {
         header->ownedData = malloc(size);
@@ -1836,7 +1935,16 @@
                      dtohl(header->header->packageCount));
                 return (mError=BAD_TYPE);
             }
-            if (parsePackage((ResTable_package*)chunk, header) != NO_ERROR) {
+            uint32_t idmap_id = 0;
+            if (idmap != NULL) {
+                uint32_t tmp;
+                if (getIdmapPackageId(header->resourceIDMap,
+                                      header->resourceIDMapSize,
+                                      &tmp) == NO_ERROR) {
+                    idmap_id = tmp;
+                }
+            }
+            if (parsePackage((ResTable_package*)chunk, header, idmap_id) != NO_ERROR) {
                 return mError;
             }
             curPackage++;
@@ -1858,6 +1966,7 @@
     if (mError != NO_ERROR) {
         LOGW("No string values found in resource table!");
     }
+
     TABLE_NOISY(LOGV("Returning from add with mError=%d\n", mError));
     return mError;
 }
@@ -2002,17 +2111,38 @@
     size_t ip = grp->packages.size();
     while (ip > 0) {
         ip--;
+        int T = t;
+        int E = e;
 
         const Package* const package = grp->packages[ip];
+        if (package->header->resourceIDMap) {
+            uint32_t overlayResID = 0x0;
+            status_t retval = idmapLookup(package->header->resourceIDMap,
+                                          package->header->resourceIDMapSize,
+                                          resID, &overlayResID);
+            if (retval == NO_ERROR && overlayResID != 0x0) {
+                // for this loop iteration, this is the type and entry we really want
+                LOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID);
+                T = Res_GETTYPE(overlayResID);
+                E = Res_GETENTRY(overlayResID);
+            } else {
+                // resource not present in overlay package, continue with the next package
+                continue;
+            }
+        }
 
         const ResTable_type* type;
         const ResTable_entry* entry;
         const Type* typeClass;
-        ssize_t offset = getEntry(package, t, e, desiredConfig, &type, &entry, &typeClass);
+        ssize_t offset = getEntry(package, T, E, desiredConfig, &type, &entry, &typeClass);
         if (offset <= 0) {
-            if (offset < 0) {
+            // No {entry, appropriate config} pair found in package. If this
+            // package is an overlay package (ip != 0), this simply means the
+            // overlay package did not specify a default.
+            // Non-overlay packages are still required to provide a default.
+            if (offset < 0 && ip == 0) {
                 LOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %zd (error %d)\n",
-                        resID, t, e, ip, (int)offset);
+                        resID, T, E, ip, (int)offset);
                 rc = offset;
                 goto out;
             }
@@ -2044,13 +2174,16 @@
 
         if (outSpecFlags != NULL) {
             if (typeClass->typeSpecFlags != NULL) {
-                *outSpecFlags |= dtohl(typeClass->typeSpecFlags[e]);
+                *outSpecFlags |= dtohl(typeClass->typeSpecFlags[E]);
             } else {
                 *outSpecFlags = -1;
             }
         }
-        
-        if (bestPackage != NULL && bestItem.isMoreSpecificThan(thisConfig)) {
+
+        if (bestPackage != NULL &&
+            (bestItem.isMoreSpecificThan(thisConfig) || bestItem.diff(thisConfig) == 0)) {
+            // Discard thisConfig not only if bestItem is more specific, but also if the two configs
+            // are identical (diff == 0), or overlay packages will not take effect.
             continue;
         }
         
@@ -2250,21 +2383,45 @@
 
     TABLE_NOISY(LOGI("Building bag: %p\n", (void*)resID));
     
+    ResTable_config bestConfig;
+    memset(&bestConfig, 0, sizeof(bestConfig));
+
     // Now collect all bag attributes from all packages.
     size_t ip = grp->packages.size();
     while (ip > 0) {
         ip--;
+        int T = t;
+        int E = e;
 
         const Package* const package = grp->packages[ip];
+        if (package->header->resourceIDMap) {
+            uint32_t overlayResID = 0x0;
+            status_t retval = idmapLookup(package->header->resourceIDMap,
+                                          package->header->resourceIDMapSize,
+                                          resID, &overlayResID);
+            if (retval == NO_ERROR && overlayResID != 0x0) {
+                // for this loop iteration, this is the type and entry we really want
+                LOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID);
+                T = Res_GETTYPE(overlayResID);
+                E = Res_GETENTRY(overlayResID);
+            } else {
+                // resource not present in overlay package, continue with the next package
+                continue;
+            }
+        }
 
         const ResTable_type* type;
         const ResTable_entry* entry;
         const Type* typeClass;
-        LOGV("Getting entry pkg=%p, t=%d, e=%d\n", package, t, e);
-        ssize_t offset = getEntry(package, t, e, &mParams, &type, &entry, &typeClass);
+        LOGV("Getting entry pkg=%p, t=%d, e=%d\n", package, T, E);
+        ssize_t offset = getEntry(package, T, E, &mParams, &type, &entry, &typeClass);
         LOGV("Resulting offset=%d\n", offset);
         if (offset <= 0) {
-            if (offset < 0) {
+            // No {entry, appropriate config} pair found in package. If this
+            // package is an overlay package (ip != 0), this simply means the
+            // overlay package did not specify a default.
+            // Non-overlay packages are still required to provide a default.
+            if (offset < 0 && ip == 0) {
                 if (set) free(set);
                 return offset;
             }
@@ -2277,6 +2434,15 @@
             continue;
         }
 
+        if (set != NULL && !type->config.isBetterThan(bestConfig, NULL)) {
+            continue;
+        }
+        bestConfig = type->config;
+        if (set) {
+            free(set);
+            set = NULL;
+        }
+
         const uint16_t entrySize = dtohs(entry->size);
         const uint32_t parent = entrySize >= sizeof(ResTable_map_entry)
             ? dtohl(((const ResTable_map_entry*)entry)->parent.ident) : 0;
@@ -2288,43 +2454,41 @@
         TABLE_NOISY(LOGI("Found map: size=%p parent=%p count=%d\n",
                          entrySize, parent, count));
 
-        if (set == NULL) {
-            // If this map inherits from another, we need to start
-            // with its parent's values.  Otherwise start out empty.
-            TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n",
-                         entrySize, parent));
-            if (parent) {
-                const bag_entry* parentBag;
-                uint32_t parentTypeSpecFlags = 0;
-                const ssize_t NP = getBagLocked(parent, &parentBag, &parentTypeSpecFlags);
-                const size_t NT = ((NP >= 0) ? NP : 0) + N;
-                set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT);
-                if (set == NULL) {
-                    return NO_MEMORY;
-                }
-                if (NP > 0) {
-                    memcpy(set+1, parentBag, NP*sizeof(bag_entry));
-                    set->numAttrs = NP;
-                    TABLE_NOISY(LOGI("Initialized new bag with %d inherited attributes.\n", NP));
-                } else {
-                    TABLE_NOISY(LOGI("Initialized new bag with no inherited attributes.\n"));
-                    set->numAttrs = 0;
-                }
-                set->availAttrs = NT;
-                set->typeSpecFlags = parentTypeSpecFlags;
-            } else {
-                set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N);
-                if (set == NULL) {
-                    return NO_MEMORY;
-                }
-                set->numAttrs = 0;
-                set->availAttrs = N;
-                set->typeSpecFlags = 0;
+        // If this map inherits from another, we need to start
+        // with its parent's values.  Otherwise start out empty.
+        TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n",
+                           entrySize, parent));
+        if (parent) {
+            const bag_entry* parentBag;
+            uint32_t parentTypeSpecFlags = 0;
+            const ssize_t NP = getBagLocked(parent, &parentBag, &parentTypeSpecFlags);
+            const size_t NT = ((NP >= 0) ? NP : 0) + N;
+            set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT);
+            if (set == NULL) {
+                return NO_MEMORY;
             }
+            if (NP > 0) {
+                memcpy(set+1, parentBag, NP*sizeof(bag_entry));
+                set->numAttrs = NP;
+                TABLE_NOISY(LOGI("Initialized new bag with %d inherited attributes.\n", NP));
+            } else {
+                TABLE_NOISY(LOGI("Initialized new bag with no inherited attributes.\n"));
+                set->numAttrs = 0;
+            }
+            set->availAttrs = NT;
+            set->typeSpecFlags = parentTypeSpecFlags;
+        } else {
+            set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N);
+            if (set == NULL) {
+                return NO_MEMORY;
+            }
+            set->numAttrs = 0;
+            set->availAttrs = N;
+            set->typeSpecFlags = 0;
         }
 
         if (typeClass->typeSpecFlags != NULL) {
-            set->typeSpecFlags |= dtohl(typeClass->typeSpecFlags[e]);
+            set->typeSpecFlags |= dtohl(typeClass->typeSpecFlags[E]);
         } else {
             set->typeSpecFlags = -1;
         }
@@ -3870,7 +4034,7 @@
 }
 
 status_t ResTable::parsePackage(const ResTable_package* const pkg,
-                                const Header* const header)
+                                const Header* const header, uint32_t idmap_id)
 {
     const uint8_t* base = (const uint8_t*)pkg;
     status_t err = validate_chunk(&pkg->header, sizeof(*pkg),
@@ -3904,8 +4068,12 @@
     
     Package* package = NULL;
     PackageGroup* group = NULL;
-    uint32_t id = dtohl(pkg->id);
-    if (id != 0 && id < 256) {
+    uint32_t id = idmap_id != 0 ? idmap_id : dtohl(pkg->id);
+    // If at this point id == 0, pkg is an overlay package without a
+    // corresponding idmap. During regular usage, overlay packages are
+    // always loaded alongside their idmaps, but during idmap creation
+    // the package is temporarily loaded by itself.
+    if (id < 256) {
     
         package = new Package(this, header, pkg);
         if (package == NULL) {
@@ -3958,7 +4126,7 @@
             return (mError=err);
         }
     } else {
-        LOG_ALWAYS_FATAL("Skins not supported!");
+        LOG_ALWAYS_FATAL("Package id out of range");
         return NO_ERROR;
     }
 
@@ -4112,6 +4280,136 @@
     return NO_ERROR;
 }
 
+status_t ResTable::createIdmap(const ResTable& overlay, uint32_t originalCrc, uint32_t overlayCrc,
+                               void** outData, size_t* outSize) const
+{
+    // see README for details on the format of map
+    if (mPackageGroups.size() == 0) {
+        return UNKNOWN_ERROR;
+    }
+    if (mPackageGroups[0]->packages.size() == 0) {
+        return UNKNOWN_ERROR;
+    }
+
+    Vector<Vector<uint32_t> > map;
+    const PackageGroup* pg = mPackageGroups[0];
+    const Package* pkg = pg->packages[0];
+    size_t typeCount = pkg->types.size();
+    // starting size is header + first item (number of types in map)
+    *outSize = (IDMAP_HEADER_SIZE + 1) * sizeof(uint32_t);
+    const String16 overlayPackage(overlay.mPackageGroups[0]->packages[0]->package->name);
+    const uint32_t pkg_id = pkg->package->id << 24;
+
+    for (size_t typeIndex = 0; typeIndex < typeCount; ++typeIndex) {
+        ssize_t offset = -1;
+        const Type* typeConfigs = pkg->getType(typeIndex);
+        ssize_t mapIndex = map.add();
+        if (mapIndex < 0) {
+            return NO_MEMORY;
+        }
+        Vector<uint32_t>& vector = map.editItemAt(mapIndex);
+        for (size_t entryIndex = 0; entryIndex < typeConfigs->entryCount; ++entryIndex) {
+            uint32_t resID = (0xff000000 & ((pkg->package->id)<<24))
+                | (0x00ff0000 & ((typeIndex+1)<<16))
+                | (0x0000ffff & (entryIndex));
+            resource_name resName;
+            if (!this->getResourceName(resID, &resName)) {
+                return UNKNOWN_ERROR;
+            }
+
+            const String16 overlayType(resName.type, resName.typeLen);
+            const String16 overlayName(resName.name, resName.nameLen);
+            uint32_t overlayResID = overlay.identifierForName(overlayName.string(),
+                                                              overlayName.size(),
+                                                              overlayType.string(),
+                                                              overlayType.size(),
+                                                              overlayPackage.string(),
+                                                              overlayPackage.size());
+            if (overlayResID != 0) {
+                // overlay package has package ID == 0, use original package's ID instead
+                overlayResID |= pkg_id;
+            }
+            vector.push(overlayResID);
+            if (overlayResID != 0 && offset == -1) {
+                offset = Res_GETENTRY(resID);
+            }
+#if 0
+            if (overlayResID != 0) {
+                LOGD("%s/%s 0x%08x -> 0x%08x\n",
+                     String8(String16(resName.type)).string(),
+                     String8(String16(resName.name)).string(),
+                     resID, overlayResID);
+            }
+#endif
+        }
+
+        if (offset != -1) {
+            // shave off leading and trailing entries which lack overlay values
+            vector.removeItemsAt(0, offset);
+            vector.insertAt((uint32_t)offset, 0, 1);
+            while (vector.top() == 0) {
+                vector.pop();
+            }
+            // reserve space for number and offset of entries, and the actual entries
+            *outSize += (2 + vector.size()) * sizeof(uint32_t);
+        } else {
+            // no entries of current type defined in overlay package
+            vector.clear();
+            // reserve space for type offset
+            *outSize += 1 * sizeof(uint32_t);
+        }
+    }
+
+    if ((*outData = malloc(*outSize)) == NULL) {
+        return NO_MEMORY;
+    }
+    uint32_t* data = (uint32_t*)*outData;
+    *data++ = htodl(IDMAP_MAGIC);
+    *data++ = htodl(originalCrc);
+    *data++ = htodl(overlayCrc);
+    const size_t mapSize = map.size();
+    *data++ = htodl(mapSize);
+    size_t offset = mapSize;
+    for (size_t i = 0; i < mapSize; ++i) {
+        const Vector<uint32_t>& vector = map.itemAt(i);
+        const size_t N = vector.size();
+        if (N == 0) {
+            *data++ = htodl(0);
+        } else {
+            offset++;
+            *data++ = htodl(offset);
+            offset += N;
+        }
+    }
+    for (size_t i = 0; i < mapSize; ++i) {
+        const Vector<uint32_t>& vector = map.itemAt(i);
+        const size_t N = vector.size();
+        if (N == 0) {
+            continue;
+        }
+        *data++ = htodl(N - 1); // do not count the offset (which is vector's first element)
+        for (size_t j = 0; j < N; ++j) {
+            const uint32_t& overlayResID = vector.itemAt(j);
+            *data++ = htodl(overlayResID);
+        }
+    }
+
+    return NO_ERROR;
+}
+
+bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes,
+                            uint32_t* pOriginalCrc, uint32_t* pOverlayCrc)
+{
+    const uint32_t* map = (const uint32_t*)idmap;
+    if (!assertIdmapHeader(map, sizeBytes)) {
+        return false;
+    }
+    *pOriginalCrc = map[1];
+    *pOverlayCrc = map[2];
+    return true;
+}
+
+
 #ifndef HAVE_ANDROID_OS
 #define CHAR16_TO_CSTR(c16, len) (String8(String16(c16,len)).string())
 
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 9d0cba3..504cfde 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -111,6 +111,7 @@
     private static final int MSG_BTA2DP_DOCK_TIMEOUT = 8;
     private static final int MSG_LOAD_SOUND_EFFECTS = 9;
     private static final int MSG_SET_FORCE_USE = 10;
+    private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 11;
 
 
     private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
@@ -355,6 +356,12 @@
         intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
         context.registerReceiver(mReceiver, intentFilter);
 
+        // Register for package removal intent broadcasts for media button receiver persistence
+        IntentFilter pkgFilter = new IntentFilter();
+        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        pkgFilter.addDataScheme("package");
+        context.registerReceiver(mReceiver, pkgFilter);
+
         // Register for media button intent broadcasts.
         intentFilter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON);
         intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
@@ -444,6 +451,9 @@
         // Broadcast vibrate settings
         broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
         broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION);
+
+        // Restore the default media button receiver from the system settings
+        restoreMediaButtonReceiver();
     }
 
     private void setStreamVolumeIndex(int stream, int index) {
@@ -1912,6 +1922,11 @@
             }
         }
 
+        private void persistMediaButtonReceiver(ComponentName receiver) {
+            Settings.System.putString(mContentResolver, Settings.System.MEDIA_BUTTON_RECEIVER,
+                    receiver == null ? "" : receiver.flattenToString());
+        }
+
         private void cleanupPlayer(MediaPlayer mp) {
             if (mp != null) {
                 try {
@@ -2022,6 +2037,10 @@
                 case MSG_SET_FORCE_USE:
                     setForceUse(msg.arg1, msg.arg2);
                     break;
+
+                case MSG_PERSIST_MEDIABUTTONRECEIVER:
+                    persistMediaButtonReceiver( (ComponentName) msg.obj );
+                    break;
             }
         }
     }
@@ -2354,6 +2373,14 @@
                 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
                         AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
                 mContext.sendStickyBroadcast(newIntent);
+            } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
+                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                    // a package is being removed, not replaced
+                    String packageName = intent.getData().getSchemeSpecificPart();
+                    if (packageName != null) {
+                        removeMediaButtonReceiverForPackage(packageName);
+                    }
+                }
             }
         }
     }
@@ -2469,7 +2496,7 @@
                 if(fse.mClientId.equals(clientToRemove)) {
                     Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
                             + fse.mClientId);
-                    mFocusStack.remove(fse);
+                    stackIterator.remove();
                 }
             }
         }
@@ -2489,7 +2516,7 @@
             if(fse.mSourceRef.equals(cb)) {
                 Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
                         + fse.mClientId);
-                mFocusStack.remove(fse);
+                stackIterator.remove();
             }
         }
         if (isTopOfStackForClientToRemove) {
@@ -2701,6 +2728,56 @@
 
     /**
      * Helper function:
+     * Remove any entry in the remote control stack that has the same package name as packageName
+     * Pre-condition: packageName != null
+     */
+    private void removeMediaButtonReceiverForPackage(String packageName) {
+        synchronized(mRCStack) {
+            if (mRCStack.empty()) {
+                return;
+            } else {
+                RemoteControlStackEntry oldTop = mRCStack.peek();
+                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+                // iterate over the stack entries
+                while(stackIterator.hasNext()) {
+                    RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
+                    if (packageName.equalsIgnoreCase(rcse.mReceiverComponent.getPackageName())) {
+                        // a stack entry is from the package being removed, remove it from the stack
+                        stackIterator.remove();
+                    }
+                }
+                if (mRCStack.empty()) {
+                    // no saved media button receiver
+                    mAudioHandler.sendMessage(
+                            mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
+                                    null));
+                    return;
+                } else if (oldTop != mRCStack.peek()) {
+                    // the top of the stack has changed, save it in the system settings
+                    // by posting a message to persist it
+                    mAudioHandler.sendMessage(
+                            mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
+                                    mRCStack.peek().mReceiverComponent));
+                }
+            }
+        }
+    }
+
+    /**
+     * Helper function:
+     * Restore remote control receiver from the system settings
+     */
+    private void restoreMediaButtonReceiver() {
+        String receiverName = Settings.System.getString(mContentResolver,
+                Settings.System.MEDIA_BUTTON_RECEIVER);
+        if ((null != receiverName) && !receiverName.isEmpty()) {
+            ComponentName receiverComponentName = ComponentName.unflattenFromString(receiverName);
+            registerMediaButtonEventReceiver(receiverComponentName);
+        }
+    }
+
+    /**
+     * Helper function:
      * Set the new remote control receiver at the top of the RC focus stack
      */
     private void pushMediaButtonReceiver(ComponentName newReceiver) {
@@ -2712,11 +2789,15 @@
         while(stackIterator.hasNext()) {
             RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
             if(rcse.mReceiverComponent.equals(newReceiver)) {
-                mRCStack.remove(rcse);
+                stackIterator.remove();
                 break;
             }
         }
         mRCStack.push(new RemoteControlStackEntry(newReceiver));
+
+        // post message to persist the default media button receiver
+        mAudioHandler.sendMessage( mAudioHandler.obtainMessage(
+                MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, newReceiver/*obj*/) );
     }
 
     /**
@@ -2728,7 +2809,7 @@
         while(stackIterator.hasNext()) {
             RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
             if(rcse.mReceiverComponent.equals(newReceiver)) {
-                mRCStack.remove(rcse);
+                stackIterator.remove();
                 break;
             }
         }
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 0b0d145a..1e4b585 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1120,6 +1120,7 @@
         mOnErrorListener = null;
         mOnInfoListener = null;
         mOnVideoSizeChangedListener = null;
+        mOnTimedTextListener = null;
         _release();
     }
 
@@ -1301,6 +1302,7 @@
     private static final int MEDIA_BUFFERING_UPDATE = 3;
     private static final int MEDIA_SEEK_COMPLETE = 4;
     private static final int MEDIA_SET_VIDEO_SIZE = 5;
+    private static final int MEDIA_TIMED_TEXT = 99;
     private static final int MEDIA_ERROR = 100;
     private static final int MEDIA_INFO = 200;
 
@@ -1369,6 +1371,11 @@
                 }
                 // No real default action so far.
                 return;
+            case MEDIA_TIMED_TEXT:
+                if (mOnTimedTextListener != null) {
+                    mOnTimedTextListener.onTimedText(mMediaPlayer, (String)msg.obj);
+                }
+                return;
 
             case MEDIA_NOP: // interface test message - ignore
                 break;
@@ -1545,6 +1552,39 @@
 
     private OnVideoSizeChangedListener mOnVideoSizeChangedListener;
 
+    /**
+     * Interface definition of a callback to be invoked when a
+     * timed text is available for display.
+     * {@hide}
+     */
+    public interface OnTimedTextListener
+    {
+        /**
+         * Called to indicate the video size
+         *
+         * @param mp             the MediaPlayer associated with this callback
+         * @param text           the timed text sample which contains the
+         *                       text needed to be displayed.
+         * {@hide}
+         */
+        public void onTimedText(MediaPlayer mp, String text);
+    }
+
+    /**
+     * Register a callback to be invoked when a timed text is available
+     * for display.
+     *
+     * @param listener the callback that will be run
+     * {@hide}
+     */
+    public void setOnTimedTextListener(OnTimedTextListener listener)
+    {
+        mOnTimedTextListener = listener;
+    }
+
+    private OnTimedTextListener mOnTimedTextListener;
+
+
     /* Do not change these values without updating their counterparts
      * in include/media/mediaplayer.h!
      */
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index a219623..2ba9b3f 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -241,7 +241,7 @@
         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
         return;
     }
-    int fd = getParcelFileDescriptorFD(env, fileDescriptor);
+    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
     if (offset < 0 || length < 0 || fd < 0) {
         if (offset < 0) {
             LOGE("negative offset (%lld)", offset);
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index ecbd288..23a77d4 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -69,7 +69,7 @@
 public:
     JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz);
     ~JNIMediaPlayerListener();
-    void notify(int msg, int ext1, int ext2);
+    virtual void notify(int msg, int ext1, int ext2, const Parcel *obj = NULL);
 private:
     JNIMediaPlayerListener();
     jclass      mClass;     // Reference to MediaPlayer class
@@ -102,10 +102,23 @@
     env->DeleteGlobalRef(mClass);
 }
 
-void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2)
+void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj)
 {
     JNIEnv *env = AndroidRuntime::getJNIEnv();
-    env->CallStaticVoidMethod(mClass, fields.post_event, mObject, msg, ext1, ext2, 0);
+    if (obj && obj->dataSize() > 0) {
+        jbyteArray jArray = env->NewByteArray(obj->dataSize());
+        if (jArray != NULL) {
+            jbyte *nArray = env->GetByteArrayElements(jArray, NULL);
+            memcpy(nArray, obj->data(), obj->dataSize());
+            env->ReleaseByteArrayElements(jArray, nArray, 0);
+            env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
+                    msg, ext1, ext2, jArray);
+            env->DeleteLocalRef(jArray);
+        }
+    } else {
+        env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
+                msg, ext1, ext2, NULL);
+    }
 }
 
 // ----------------------------------------------------------------------------
@@ -325,7 +338,7 @@
         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
         return;
     }
-    int fd = getParcelFileDescriptorFD(env, fileDescriptor);
+    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
     LOGV("setDataSourceFD: fd %d", fd);
     process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
 }
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index a8a46c1..2c246958 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -253,7 +253,7 @@
         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
         return;
     }
-    int fd = getParcelFileDescriptorFD(env, fileDescriptor);
+    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
     sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
     status_t opStatus = mr->setOutputFile(fd, offset, length);
     process_media_recorder_call(env, opStatus, "java/io/IOException", "setOutputFile failed.");
@@ -267,7 +267,7 @@
         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
         return;
     }
-    int fd = getParcelFileDescriptorFD(env, fileDescriptor);
+    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
     sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
     status_t opStatus = mr->setOutputFileAuxiliary(fd);
     process_media_recorder_call(env, opStatus, "java/io/IOException", "setOutputFile failed.");
@@ -404,38 +404,32 @@
 
     clazz = env->FindClass("android/media/MediaRecorder");
     if (clazz == NULL) {
-        jniThrowException(env, "java/lang/RuntimeException", "Can't find android/media/MediaRecorder");
         return;
     }
 
     fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
     if (fields.context == NULL) {
-        jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaRecorder.mNativeContext");
         return;
     }
 
     fields.surface = env->GetFieldID(clazz, "mSurface", "Landroid/view/Surface;");
     if (fields.surface == NULL) {
-        jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaRecorder.mSurface");
         return;
     }
 
     jclass surface = env->FindClass("android/view/Surface");
     if (surface == NULL) {
-        jniThrowException(env, "java/lang/RuntimeException", "Can't find android/view/Surface");
         return;
     }
 
     fields.surface_native = env->GetFieldID(surface, ANDROID_VIEW_SURFACE_JNI_ID, "I");
     if (fields.surface_native == NULL) {
-        jniThrowException(env, "java/lang/RuntimeException", "Can't find Surface.mSurface");
         return;
     }
 
     fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
                                                "(Ljava/lang/Object;IIILjava/lang/Object;)V");
     if (fields.post_event == NULL) {
-        jniThrowException(env, "java/lang/RuntimeException", "MediaRecorder.postEventFromNative");
         return;
     }
 }
diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp
index 06058dc..a3dd136 100644
--- a/media/jni/android_media_MediaScanner.cpp
+++ b/media/jni/android_media_MediaScanner.cpp
@@ -303,7 +303,7 @@
         return NULL;
     }
 
-    int fd = getParcelFileDescriptorFD(env, fileDescriptor);
+    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
     char* data = mp->extractAlbumArt(fd);
     if (!data) {
         return NULL;
@@ -335,15 +335,11 @@
     LOGV("native_init");
     jclass clazz = env->FindClass(kClassMediaScanner);
     if (clazz == NULL) {
-        const char* err = "Can't find android/media/MediaScanner";
-        jniThrowException(env, kRunTimeException, err);
         return;
     }
 
     fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
     if (fields.context == NULL) {
-        const char* err = "Can't find MediaScanner.mNativeContext";
-        jniThrowException(env, kRunTimeException, err);
         return;
     }
 }
@@ -426,5 +422,3 @@
     return AndroidRuntime::registerNativeMethods(env,
                 kClassMediaScanner, gMethods, NELEM(gMethods));
 }
-
-
diff --git a/media/jni/soundpool/android_media_SoundPool.cpp b/media/jni/soundpool/android_media_SoundPool.cpp
index 447f931..03d3388 100644
--- a/media/jni/soundpool/android_media_SoundPool.cpp
+++ b/media/jni/soundpool/android_media_SoundPool.cpp
@@ -60,7 +60,7 @@
     LOGV("android_media_SoundPool_load_FD");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap == NULL) return 0;
-    return ap->load(getParcelFileDescriptorFD(env, fileDescriptor),
+    return ap->load(jniGetFDFromFileDescriptor(env, fileDescriptor),
             int64_t(offset), int64_t(length), int(priority));
 }
 
diff --git a/media/libmedia/IMediaPlayerClient.cpp b/media/libmedia/IMediaPlayerClient.cpp
index bf51829..1f135c4 100644
--- a/media/libmedia/IMediaPlayerClient.cpp
+++ b/media/libmedia/IMediaPlayerClient.cpp
@@ -35,13 +35,16 @@
     {
     }
 
-    virtual void notify(int msg, int ext1, int ext2)
+    virtual void notify(int msg, int ext1, int ext2, const Parcel *obj)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IMediaPlayerClient::getInterfaceDescriptor());
         data.writeInt32(msg);
         data.writeInt32(ext1);
         data.writeInt32(ext2);
+        if (obj && obj->dataSize() > 0) {
+            data.appendFrom(const_cast<Parcel *>(obj), 0, obj->dataSize());
+        }
         remote()->transact(NOTIFY, data, &reply, IBinder::FLAG_ONEWAY);
     }
 };
@@ -59,7 +62,12 @@
             int msg = data.readInt32();
             int ext1 = data.readInt32();
             int ext2 = data.readInt32();
-            notify(msg, ext1, ext2);
+            Parcel obj;
+            if (data.dataAvail() > 0) {
+                obj.appendFrom(const_cast<Parcel *>(&data), data.dataPosition(), data.dataAvail());
+            }
+
+            notify(msg, ext1, ext2, &obj);
             return NO_ERROR;
         } break;
         default:
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index 0ee0249a..e80e742 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -551,7 +551,7 @@
     return mPlayer->attachAuxEffect(effectId);
 }
 
-void MediaPlayer::notify(int msg, int ext1, int ext2)
+void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj)
 {
     LOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
     bool send = true;
@@ -641,6 +641,9 @@
         mVideoWidth = ext1;
         mVideoHeight = ext2;
         break;
+    case MEDIA_TIMED_TEXT:
+        LOGV("Received timed text message");
+        break;
     default:
         LOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2);
         break;
@@ -653,7 +656,7 @@
     if ((listener != 0) && send) {
         Mutex::Autolock _l(mNotifyLock);
         LOGV("callback application");
-        listener->notify(msg, ext1, ext2);
+        listener->notify(msg, ext1, ext2, obj);
         LOGV("back from callback");
     }
 }
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 9c9ac97..6b97708 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1022,7 +1022,8 @@
     return NO_ERROR;
 }
 
-void MediaPlayerService::Client::notify(void* cookie, int msg, int ext1, int ext2)
+void MediaPlayerService::Client::notify(
+        void* cookie, int msg, int ext1, int ext2, const Parcel *obj)
 {
     Client* client = static_cast<Client*>(cookie);
 
@@ -1039,7 +1040,7 @@
         client->addNewMetadataUpdate(metadata_type);
     }
     LOGV("[%d] notify (%p, %d, %d, %d)", client->mConnId, cookie, msg, ext1, ext2);
-    client->mClient->notify(msg, ext1, ext2);
+    client->mClient->notify(msg, ext1, ext2, obj);
 }
 
 
@@ -1623,7 +1624,8 @@
     return mError;
 }
 
-void MediaPlayerService::AudioCache::notify(void* cookie, int msg, int ext1, int ext2)
+void MediaPlayerService::AudioCache::notify(
+        void* cookie, int msg, int ext1, int ext2, const Parcel *obj)
 {
     LOGV("notify(%p, %d, %d, %d)", cookie, msg, ext1, ext2);
     AudioCache* p = static_cast<AudioCache*>(cookie);
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index ff6ccf54..5539a37 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -156,7 +156,8 @@
 
                 sp<IMemoryHeap> getHeap() const { return mHeap; }
 
-        static  void            notify(void* cookie, int msg, int ext1, int ext2);
+        static  void            notify(void* cookie, int msg,
+                                       int ext1, int ext2, const Parcel *obj);
         virtual status_t        dump(int fd, const Vector<String16>& args) const;
 
     private:
@@ -287,7 +288,8 @@
 
                 status_t        setDataSource(const sp<IStreamSource> &source);
 
-        static  void            notify(void* cookie, int msg, int ext1, int ext2);
+        static  void            notify(void* cookie, int msg,
+                                       int ext1, int ext2, const Parcel *obj);
 
                 pid_t           pid() const { return mPid; }
         virtual status_t        dump(int fd, const Vector<String16>& args) const;
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index f31c2ac..1bfdd8e 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -184,7 +184,8 @@
       mFlags(0),
       mExtractorFlags(0),
       mVideoBuffer(NULL),
-      mDecryptHandle(NULL) {
+      mDecryptHandle(NULL),
+      mLastVideoTimeUs(-1) {
     CHECK_EQ(mClient.connect(), (status_t)OK);
 
     DataSource::RegisterDefaultSniffers();
@@ -470,28 +471,13 @@
 
     mVideoRenderer.clear();
 
-    if (mVideoBuffer) {
-        mVideoBuffer->release();
-        mVideoBuffer = NULL;
-    }
-
     if (mRTSPController != NULL) {
         mRTSPController->disconnect();
         mRTSPController.clear();
     }
 
     if (mVideoSource != NULL) {
-        mVideoSource->stop();
-
-        // The following hack is necessary to ensure that the OMX
-        // component is completely released by the time we may try
-        // to instantiate it again.
-        wp<MediaSource> tmp = mVideoSource;
-        mVideoSource.clear();
-        while (tmp.promote() != NULL) {
-            usleep(1000);
-        }
-        IPCThreadState::self()->flushCommands();
+        shutdownVideoDecoder_l();
     }
 
     mDurationUs = -1;
@@ -510,6 +496,7 @@
     mFileSource.clear();
 
     mBitrate = -1;
+    mLastVideoTimeUs = -1;
 }
 
 void AwesomePlayer::notifyListener_l(int msg, int ext1, int ext2) {
@@ -1012,7 +999,7 @@
     Mutex::Autolock autoLock(mLock);
 
     mSurface = surface;
-    mNativeWindow = surface;
+    setNativeWindow_l(surface);
 }
 
 void AwesomePlayer::setSurfaceTexture(const sp<ISurfaceTexture> &surfaceTexture) {
@@ -1020,9 +1007,57 @@
 
     mSurface.clear();
     if (surfaceTexture != NULL) {
-        mNativeWindow = new SurfaceTextureClient(surfaceTexture);
+        setNativeWindow_l(new SurfaceTextureClient(surfaceTexture));
+    }
+}
+
+void AwesomePlayer::shutdownVideoDecoder_l() {
+    if (mVideoBuffer) {
+        mVideoBuffer->release();
+        mVideoBuffer = NULL;
     }
 
+    mVideoSource->stop();
+
+    // The following hack is necessary to ensure that the OMX
+    // component is completely released by the time we may try
+    // to instantiate it again.
+    wp<MediaSource> tmp = mVideoSource;
+    mVideoSource.clear();
+    while (tmp.promote() != NULL) {
+        usleep(1000);
+    }
+    IPCThreadState::self()->flushCommands();
+}
+
+void AwesomePlayer::setNativeWindow_l(const sp<ANativeWindow> &native) {
+    mNativeWindow = native;
+
+    if (mVideoSource == NULL) {
+        return;
+    }
+
+    LOGI("attempting to reconfigure to use new surface");
+
+    bool wasPlaying = (mFlags & PLAYING) != 0;
+
+    pause_l();
+    mVideoRenderer.clear();
+
+    shutdownVideoDecoder_l();
+
+    CHECK_EQ(initVideoDecoder(), (status_t)OK);
+
+    if (mLastVideoTimeUs >= 0) {
+        mSeeking = SEEK;
+        mSeekNotificationSent = true;
+        mSeekTimeUs = mLastVideoTimeUs;
+        mFlags &= ~(AT_EOS | AUDIO_AT_EOS | VIDEO_AT_EOS);
+    }
+
+    if (wasPlaying) {
+        play_l();
+    }
 }
 
 void AwesomePlayer::setAudioSink(
@@ -1412,6 +1447,8 @@
     int64_t timeUs;
     CHECK(mVideoBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
 
+    mLastVideoTimeUs = timeUs;
+
     if (mSeeking == SEEK_VIDEO_ONLY) {
         if (mSeekTimeUs > timeUs) {
             LOGI("XXX mSeekTimeUs = %lld us, timeUs = %lld us",
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index 03ce202..4bdfc6f 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -48,7 +48,7 @@
 bool MP3Extractor::get_mp3_frame_size(
         uint32_t header, size_t *frame_size,
         int *out_sampling_rate, int *out_channels,
-        int *out_bitrate) {
+        int *out_bitrate, int *out_num_samples) {
     *frame_size = 0;
 
     if (out_sampling_rate) {
@@ -63,6 +63,10 @@
         *out_bitrate = 0;
     }
 
+    if (out_num_samples) {
+        *out_num_samples = 1152;
+    }
+
     if ((header & 0xffe00000) != 0xffe00000) {
         return false;
     }
@@ -127,6 +131,10 @@
         }
 
         *frame_size = (12000 * bitrate / sampling_rate + padding) * 4;
+
+        if (out_num_samples) {
+            *out_num_samples = 384;
+        }
     } else {
         // layer II or III
 
@@ -150,10 +158,17 @@
             bitrate = (layer == 2 /* L2 */)
                 ? kBitrateV1L2[bitrate_index - 1]
                 : kBitrateV1L3[bitrate_index - 1];
+
+            if (out_num_samples) {
+                *out_num_samples = 1152;
+            }
         } else {
             // V2 (or 2.5)
 
             bitrate = kBitrateV2[bitrate_index - 1];
+            if (out_num_samples) {
+                *out_num_samples = 576;
+            }
         }
 
         if (out_bitrate) {
@@ -374,6 +389,9 @@
     sp<MP3Seeker> mSeeker;
     MediaBufferGroup *mGroup;
 
+    int64_t mBasisTimeUs;
+    int64_t mSamplesRead;
+
     MP3Source(const MP3Source &);
     MP3Source &operator=(const MP3Source &);
 };
@@ -489,7 +507,9 @@
       mCurrentTimeUs(0),
       mStarted(false),
       mSeeker(seeker),
-      mGroup(NULL) {
+      mGroup(NULL),
+      mBasisTimeUs(0),
+      mSamplesRead(0) {
 }
 
 MP3Source::~MP3Source() {
@@ -509,6 +529,9 @@
     mCurrentPos = mFirstFramePos;
     mCurrentTimeUs = 0;
 
+    mBasisTimeUs = mCurrentTimeUs;
+    mSamplesRead = 0;
+
     mStarted = true;
 
     return OK;
@@ -552,6 +575,9 @@
         } else {
             mCurrentTimeUs = actualSeekTimeUs;
         }
+
+        mBasisTimeUs = mCurrentTimeUs;
+        mSamplesRead = 0;
     }
 
     MediaBuffer *buffer;
@@ -562,6 +588,8 @@
 
     size_t frame_size;
     int bitrate;
+    int num_samples;
+    int sample_rate;
     for (;;) {
         ssize_t n = mDataSource->readAt(mCurrentPos, buffer->data(), 4);
         if (n < 4) {
@@ -575,7 +603,7 @@
 
         if ((header & kMask) == (mFixedHeader & kMask)
             && MP3Extractor::get_mp3_frame_size(
-                header, &frame_size, NULL, NULL, &bitrate)) {
+                header, &frame_size, &sample_rate, NULL, &bitrate, &num_samples)) {
             break;
         }
 
@@ -613,7 +641,9 @@
     buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
 
     mCurrentPos += frame_size;
-    mCurrentTimeUs += frame_size * 8000ll / bitrate;
+
+    mSamplesRead += num_samples;
+    mCurrentTimeUs = mBasisTimeUs + ((mSamplesRead * 1000000) / sample_rate);
 
     *out = buffer;
 
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 4095fbf..7621f2c 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -146,7 +146,8 @@
 
     int64_t thumbNailTime;
     if (frameTimeUs < 0) {
-        if (!trackMeta->findInt64(kKeyThumbnailTime, &thumbNailTime)) {
+        if (!trackMeta->findInt64(kKeyThumbnailTime, &thumbNailTime)
+                || thumbNailTime < 0) {
             thumbNailTime = 0;
         }
         options.setSeekTo(thumbNailTime, mode);
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 7fd7724..8c61064 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -218,6 +218,8 @@
     DrmManagerClient *mDrmManagerClient;
     sp<DecryptHandle> mDecryptHandle;
 
+    int64_t mLastVideoTimeUs;
+
     status_t setDataSource_l(
             const char *uri,
             const KeyedVector<String8, String8> *headers = NULL);
@@ -267,6 +269,9 @@
 
     status_t startAudioPlayer_l();
 
+    void shutdownVideoDecoder_l();
+    void setNativeWindow_l(const sp<ANativeWindow> &native);
+
     AwesomePlayer(const AwesomePlayer &);
     AwesomePlayer &operator=(const AwesomePlayer &);
 };
diff --git a/media/libstagefright/include/MP3Extractor.h b/media/libstagefright/include/MP3Extractor.h
index ef71b8f..cf1146b 100644
--- a/media/libstagefright/include/MP3Extractor.h
+++ b/media/libstagefright/include/MP3Extractor.h
@@ -42,7 +42,7 @@
     static bool get_mp3_frame_size(
             uint32_t header, size_t *frame_size,
             int *out_sampling_rate = NULL, int *out_channels = NULL,
-            int *out_bitrate = NULL);
+            int *out_bitrate = NULL, int *out_num_samples = NULL);
 
 private:
     status_t mInitCheck;
diff --git a/native/include/android/tts.h b/native/include/android/tts.h
deleted file mode 100644
index fb15108..0000000
--- a/native/include/android/tts.h
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * 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.
- */
-#ifndef ANDROID_TTS_H
-#define ANDROID_TTS_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.
-
-// The shared library must contain a function named "android_getTtsEngine"
-// that returns an 'android_tts_engine_t' instance.
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define ANDROID_TTS_ENGINE_PROPERTY_CONFIG "engineConfig"
-#define ANDROID_TTS_ENGINE_PROPERTY_PITCH  "pitch"
-#define ANDROID_TTS_ENGINE_PROPERTY_RATE   "rate"
-#define ANDROID_TTS_ENGINE_PROPERTY_VOLUME "volume"
-
-typedef enum {
-    ANDROID_TTS_SUCCESS                 = 0,
-    ANDROID_TTS_FAILURE                 = -1,
-    ANDROID_TTS_FEATURE_UNSUPPORTED     = -2,
-    ANDROID_TTS_VALUE_INVALID           = -3,
-    ANDROID_TTS_PROPERTY_UNSUPPORTED    = -4,
-    ANDROID_TTS_PROPERTY_SIZE_TOO_SMALL = -5,
-    ANDROID_TTS_MISSING_RESOURCES       = -6
-} android_tts_result_t;
-
-typedef enum {
-    ANDROID_TTS_LANG_COUNTRY_VAR_AVAILABLE = 2,
-    ANDROID_TTS_LANG_COUNTRY_AVAILABLE    = 1,
-    ANDROID_TTS_LANG_AVAILABLE            = 0,
-    ANDROID_TTS_LANG_MISSING_DATA         = -1,
-    ANDROID_TTS_LANG_NOT_SUPPORTED        = -2
-} android_tts_support_result_t;
-
-typedef enum {
-    ANDROID_TTS_SYNTH_DONE              = 0,
-    ANDROID_TTS_SYNTH_PENDING           = 1
-} android_tts_synth_status_t;
-
-typedef enum {
-    ANDROID_TTS_CALLBACK_HALT           = 0,
-    ANDROID_TTS_CALLBACK_CONTINUE       = 1
-} android_tts_callback_status_t;
-
-// Supported audio formats
-typedef enum {
-    ANDROID_TTS_AUDIO_FORMAT_INVALID    = -1,
-    ANDROID_TTS_AUDIO_FORMAT_DEFAULT    = 0,
-    ANDROID_TTS_AUDIO_FORMAT_PCM_16_BIT = 1,
-    ANDROID_TTS_AUDIO_FORMAT_PCM_8_BIT  = 2,
-} android_tts_audio_format_t;
-
-
-/* An android_tts_engine_t object can be anything, but must have,
- * as its first field, a pointer to a table of functions.
- *
- * See the full definition of struct android_tts_engine_t_funcs_t
- * below for details.
- */
-typedef struct android_tts_engine_funcs_t  android_tts_engine_funcs_t;
-
-typedef struct {
-    android_tts_engine_funcs_t *funcs;
-} android_tts_engine_t;
-
-/* This function must be located in the TTS Engine shared library
- * and must return the address of an android_tts_engine_t library.
- */
-extern android_tts_engine_t *android_getTtsEngine();
-
-/* Including the old version for legacy support (Froyo compatibility).
- * This should return the same thing as android_getTtsEngine.
- */
-extern "C" android_tts_engine_t *getTtsEngine();
-
-// A callback type used to notify the framework of new synthetized
-// audio samples, status will be SYNTH_DONE for the last sample of
-// the last request, of SYNTH_PENDING otherwise.
-//
-// This is passed by the framework to the engine through the
-// 'engine_init' function (see below).
-//
-// The callback for synthesis completed takes:
-// @param [inout] void *&       - The userdata pointer set in the original
-//                                 synth call
-// @param [in]    uint32_t      - Track sampling rate in Hz
-// @param [in]    uint32_t      - The audio format
-// @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 android_tts_callback_status_t (*android_tts_synth_cb_t)
-            (void **pUserData,
-             uint32_t trackSamplingHz,
-             android_tts_audio_format_t audioFormat,
-             int channelCount,
-             int8_t **pAudioBuffer,
-             size_t *pBufferSize,
-             android_tts_synth_status_t status);
-
-
-// The table of function pointers that the android_tts_engine_t must point to.
-// Note that each of these functions will take a handle to the engine itself
-// as their first parameter.
-//
-
-struct android_tts_engine_funcs_t {
-    // reserved fields, ignored by the framework
-    // they must be placed here to ensure binary compatibility
-    // of legacy binary plugins.
-    void *reserved[2];
-
-    // Initialize the TTS engine and returns whether initialization succeeded.
-    // @param synthDoneCBPtr synthesis callback function pointer
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    android_tts_result_t (*init)
-            (void *engine,
-             android_tts_synth_cb_t synthDonePtr,
-             const char *engineConfig);
-
-    // Shut down the TTS engine and releases all associated resources.
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    android_tts_result_t (*shutdown)
-            (void *engine);
-
-    // 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
-    android_tts_result_t (*stop)
-            (void *engine);
-
-    // Returns the level of support for the language, country and variant.
-    // @return TTS_LANG_COUNTRY_VAR_AVAILABLE if the language, country and variant are supported,
-    //            and the corresponding resources are correctly installed
-    //         TTS_LANG_COUNTRY_AVAILABLE if the language and country are supported and the
-    //             corresponding resources are correctly installed, but there is no match for
-    //             the specified variant
-    //         TTS_LANG_AVAILABLE if the language is supported and the
-    //             corresponding resources are correctly installed, but there is no match for
-    //             the specified country and variant
-    //         TTS_LANG_MISSING_DATA if the required resources to provide any level of support
-    //             for the language are not correctly installed
-    //         TTS_LANG_NOT_SUPPORTED if the language is not supported by the TTS engine.
-    android_tts_support_result_t (*isLanguageAvailable)
-            (void *engine,
-             const char *lang,
-             const char *country,
-             const char *variant);
-
-    // 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 and country values are coded according to the ISO three
-    // letter codes for languages and countries, as can be retrieved from a java.util.Locale
-    // instance. The variant value is encoded as the variant string retrieved from a
-    // java.util.Locale instance built with that variant data.
-    // @param lang pointer to the ISO three letter code for the language
-    // @param country pointer to the ISO three letter code for the country
-    // @param variant pointer to the variant code
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    android_tts_result_t (*loadLanguage)
-            (void *engine,
-             const char *lang,
-             const char *country,
-             const char *variant);
-
-    // Load the resources associated with the specified language, country and Locale variant.
-    // The loaded language will only be used once a call to setLanguageFromLocale() with the same
-    // language value is issued. Language and country values are coded according to the ISO three
-    // letter codes for languages and countries, as can be retrieved from a java.util.Locale
-    // instance. The variant value is encoded as the variant string retrieved from a
-    // java.util.Locale instance built with that variant data.
-    // @param lang pointer to the ISO three letter code for the language
-    // @param country pointer to the ISO three letter code for the country
-    // @param variant pointer to the variant code
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    android_tts_result_t (*setLanguage)
-            (void *engine,
-             const char *lang,
-             const char *country,
-             const char *variant);
-
-    // Retrieve the currently set language, country and variant, or empty strings if none of
-    // parameters have been set. Language and country are represented by their 3-letter ISO code
-    // @param[out]   pointer to the retrieved 3-letter code language value
-    // @param[out]   pointer to the retrieved 3-letter code country value
-    // @param[out]   pointer to the retrieved variant value
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    android_tts_result_t (*getLanguage)
-            (void *engine,
-             char *language,
-             char *country,
-             char *variant);
-
-    // Notifies the engine what audio parameters should be used for the synthesis.
-    // This is meant to be used as a hint, the engine implementation will set the output values
-    // to those of the synthesis format, based on a given hint.
-    // @param[inout] encoding in: the desired audio sample format
-    //                         out: the format used by the TTS engine
-    // @param[inout] rate in: the desired audio sample rate
-    //                         out: the sample rate used by the TTS engine
-    // @param[inout] channels in: the desired number of audio channels
-    //                         out: the number of channels used by the TTS engine
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    android_tts_result_t (*setAudioFormat)
-            (void *engine,
-             android_tts_audio_format_t* pEncoding,
-             uint32_t* pRate,
-             int* pChannels);
-
-    // Set a property for the the TTS engine
-    // "size" is the maximum size of "value" for properties "property"
-    // @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,
-    //                  or TTS_VALUE_INVALID
-    android_tts_result_t (*setProperty)
-            (void *engine,
-             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
-    android_tts_result_t (*getProperty)
-            (void *engine,
-             const char *property,
-             char *value,
-             size_t *iosize);
-
-    // Synthesize the 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 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:
-    //          o interpret-as
-    //    * phoneme
-    //    * voice:
-    //          o gender,
-    //          o age,
-    //          o variant,
-    //          o name
-    //    * emphasis
-    //    * break:
-    //          o strength,
-    //          o time
-    //    * prosody:
-    //          o pitch,
-    //          o contour,
-    //          o range,
-    //          o rate,
-    //          o duration,
-    //          o volume
-    //    * mark
-    // Differences between this text format and SSML are:
-    //    * full SSML documents are not supported
-    //    * namespaces are not supported
-    // 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
-    android_tts_result_t (*synthesizeText)
-            (void *engine,
-             const char *text,
-             int8_t *buffer,
-             size_t bufferSize,
-             void *userdata);
-};
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* ANDROID_TTS_H */
diff --git a/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java b/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java
index ed1af49..9bd8f36 100644
--- a/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java
+++ b/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java
@@ -18,6 +18,7 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.nfc.ApduList;
 import android.nfc.INfcAdapterExtras;
 import android.nfc.NfcAdapter;
 import android.os.RemoteException;
@@ -184,4 +185,20 @@
     public NfcExecutionEnvironment getEmbeddedExecutionEnvironment() {
         return sEmbeddedEe;
     }
+
+    public void registerTearDownApdus(String packageName, ApduList apdus) {
+        try {
+            sService.registerTearDownApdus(packageName, apdus);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        }
+    }
+
+    public void unregisterTearDownApdus(String packageName) {
+        try {
+            sService.unregisterTearDownApdus(packageName);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        }
+    }
 }
diff --git a/opengl/java/android/opengl/GLES10.java b/opengl/java/android/opengl/GLES10.java
index 7c0f949..790acbd 100644
--- a/opengl/java/android/opengl/GLES10.java
+++ b/opengl/java/android/opengl/GLES10.java
@@ -1,18 +1,19 @@
 /*
- * 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.
- */
+**
+** 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.
+*/
 
 // This source file is automatically generated
 
diff --git a/opengl/java/android/opengl/GLES10Ext.java b/opengl/java/android/opengl/GLES10Ext.java
index 116ab9d..81fc59e 100644
--- a/opengl/java/android/opengl/GLES10Ext.java
+++ b/opengl/java/android/opengl/GLES10Ext.java
@@ -1,18 +1,19 @@
 /*
- * 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.
- */
+**
+** 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.
+*/
 
 // This source file is automatically generated
 
diff --git a/opengl/java/android/opengl/GLES11.java b/opengl/java/android/opengl/GLES11.java
index b24c043..1ca179b 100644
--- a/opengl/java/android/opengl/GLES11.java
+++ b/opengl/java/android/opengl/GLES11.java
@@ -1,18 +1,19 @@
 /*
- * 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.
- */
+**
+** 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.
+*/
 
 // This source file is automatically generated
 
diff --git a/opengl/java/android/opengl/GLES11Ext.java b/opengl/java/android/opengl/GLES11Ext.java
index a9a7a22..25d5467 100644
--- a/opengl/java/android/opengl/GLES11Ext.java
+++ b/opengl/java/android/opengl/GLES11Ext.java
@@ -1,18 +1,19 @@
 /*
- * 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.
- */
+**
+** 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.
+*/
 
 // This source file is automatically generated
 
diff --git a/opengl/java/android/opengl/GLES20.java b/opengl/java/android/opengl/GLES20.java
index 700164b..635f811 100644
--- a/opengl/java/android/opengl/GLES20.java
+++ b/opengl/java/android/opengl/GLES20.java
@@ -1,18 +1,19 @@
 /*
- * 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.
- */
+**
+** 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.
+*/
 
 // This source file is automatically generated
 
diff --git a/opengl/java/com/google/android/gles_jni/GLImpl.java b/opengl/java/com/google/android/gles_jni/GLImpl.java
index 50f6760..090c0cb7 100644
--- a/opengl/java/com/google/android/gles_jni/GLImpl.java
+++ b/opengl/java/com/google/android/gles_jni/GLImpl.java
@@ -1,18 +1,19 @@
-/*
- * Copyright (C) 2006 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.
- */
+/* //device/java/android/com/google/android/gles_jni/GLImpl.java
+**
+** Copyright 2006, 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.
+*/
 
 // This source file is automatically generated
 
diff --git a/opengl/java/javax/microedition/khronos/opengles/GL.java b/opengl/java/javax/microedition/khronos/opengles/GL.java
index d5b60c6..3b78f3d 100644
--- a/opengl/java/javax/microedition/khronos/opengles/GL.java
+++ b/opengl/java/javax/microedition/khronos/opengles/GL.java
@@ -1,18 +1,19 @@
-/*
- * Copyright (C) 2006 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.
- */
+/* //device/java/android/javax/microedition/khronos/opengles/GL.java
+**
+** Copyright 2006, 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 javax.microedition.khronos.opengles;
 
diff --git a/opengl/java/javax/microedition/khronos/opengles/GL10.java b/opengl/java/javax/microedition/khronos/opengles/GL10.java
index f48ecde..4fcfb52 100644
--- a/opengl/java/javax/microedition/khronos/opengles/GL10.java
+++ b/opengl/java/javax/microedition/khronos/opengles/GL10.java
@@ -1,18 +1,19 @@
-/*
- * Copyright (C) 2006 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.
- */
+/* //device/java/android/javax/microedition/khronos/opengles/GL10.java
+**
+** Copyright 2006, 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.
+*/
 
 // This source file is automatically generated
 
diff --git a/opengl/java/javax/microedition/khronos/opengles/GL10Ext.java b/opengl/java/javax/microedition/khronos/opengles/GL10Ext.java
index f3252ab..562b20a 100644
--- a/opengl/java/javax/microedition/khronos/opengles/GL10Ext.java
+++ b/opengl/java/javax/microedition/khronos/opengles/GL10Ext.java
@@ -1,18 +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.
- */
+/* //device/java/android/javax/microedition/khronos/opengles/GL10Ext.java
+**
+** Copyright 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.
+*/
 
 // This source file is automatically generated
 
diff --git a/opengl/java/javax/microedition/khronos/opengles/GL11.java b/opengl/java/javax/microedition/khronos/opengles/GL11.java
index 943a5be..3ba110c 100644
--- a/opengl/java/javax/microedition/khronos/opengles/GL11.java
+++ b/opengl/java/javax/microedition/khronos/opengles/GL11.java
@@ -1,18 +1,19 @@
-/*
- * Copyright (C) 2006 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.
- */
+/* //device/java/android/javax/microedition/khronos/opengles/GL11.java
+**
+** Copyright 2006, 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.
+*/
 
 // This source file is automatically generated
 
diff --git a/opengl/java/javax/microedition/khronos/opengles/GL11Ext.java b/opengl/java/javax/microedition/khronos/opengles/GL11Ext.java
index 842db7a..459a1ab 100644
--- a/opengl/java/javax/microedition/khronos/opengles/GL11Ext.java
+++ b/opengl/java/javax/microedition/khronos/opengles/GL11Ext.java
@@ -1,18 +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.
- */
+/* //device/java/android/javax/microedition/khronos/opengles/GL11Ext.java
+**
+** Copyright 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.
+*/
 
 // This source file is automatically generated
 
diff --git a/opengl/java/javax/microedition/khronos/opengles/GL11ExtensionPack.java b/opengl/java/javax/microedition/khronos/opengles/GL11ExtensionPack.java
index 97d5fd8..933c91e 100644
--- a/opengl/java/javax/microedition/khronos/opengles/GL11ExtensionPack.java
+++ b/opengl/java/javax/microedition/khronos/opengles/GL11ExtensionPack.java
@@ -1,18 +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.
- */
+/* //device/java/android/javax/microedition/khronos/opengles/GL11ExtensionPack.java
+**
+** Copyright 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.
+*/
 
 // This source file is automatically generated
 
diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk
index 7d72729..0747efb 100644
--- a/opengl/libs/Android.mk
+++ b/opengl/libs/Android.mk
@@ -13,8 +13,8 @@
 	EGL/hooks.cpp 	       \
 	EGL/Loader.cpp 	       \
 #
-LOCAL_STATIC_LIBRARIES += libGLESv2_dbg libprotobuf-cpp-2.3.0-lite liblzf
-LOCAL_SHARED_LIBRARIES += libcutils libutils libstlport
+
+LOCAL_SHARED_LIBRARIES += libcutils libutils libGLESv2_dbg
 LOCAL_LDLIBS := -lpthread -ldl
 LOCAL_MODULE:= libEGL
 LOCAL_LDFLAGS += -Wl,--exclude-libs=ALL
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 6474c87..9cf7223 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -46,6 +46,7 @@
 #include "egl_impl.h"
 #include "Loader.h"
 #include "glesv2dbg.h"
+#include "egl_tls.h"
 
 #define setError(_e, _r) setErrorEtc(__FUNCTION__, __LINE__, _e, _r)
 
@@ -58,7 +59,7 @@
 static char const * const gVendorString     = "Android";
 static char const * const gVersionString    = "1.4 Android META-EGL";
 static char const * const gClientApiString  = "OpenGL ES";
-static char const * const gExtensionString  = 
+static char const * const gExtensionString  =
         "EGL_KHR_image "
         "EGL_KHR_image_base "
         "EGL_KHR_image_pixmap "
@@ -221,18 +222,15 @@
 struct egl_context_t : public egl_object_t
 {
     typedef egl_object_t::LocalRef<egl_context_t, EGLContext> Ref;
-    
+
     egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config,
-            int impl, egl_connection_t const* cnx, int version) 
-    : egl_object_t(dpy), dpy(dpy), context(context), config(config), read(0), draw(0), 
-      impl(impl), cnx(cnx), version(version), dbg(NULL)
+            int impl, egl_connection_t const* cnx, int version)
+    : egl_object_t(dpy), dpy(dpy), context(context), config(config), read(0), draw(0),
+      impl(impl), cnx(cnx), version(version)
     {
     }
     ~egl_context_t()
     {
-        if (dbg)
-            DestroyDbgContext(dbg);
-        dbg = NULL;
     }
     EGLDisplay                  dpy;
     EGLContext                  context;
@@ -242,7 +240,6 @@
     int                         impl;
     egl_connection_t const*     cnx;
     int                         version;
-    DbgContext *                dbg;
 };
 
 struct egl_image_t : public egl_object_t
@@ -277,15 +274,6 @@
 typedef egl_image_t::Ref    ImageRef;
 typedef egl_sync_t::Ref     SyncRef;
 
-struct tls_t
-{
-    tls_t() : error(EGL_SUCCESS), ctx(0), logCallWithNoContext(EGL_TRUE) { }
-    EGLint      error;
-    EGLContext  ctx;
-    EGLBoolean  logCallWithNoContext;
-};
-
-
 // ----------------------------------------------------------------------------
 
 static egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS];
@@ -323,7 +311,7 @@
     int propertyLevel = atoi(value);
     int applicationLevel = gEGLApplicationTraceLevel;
     gEGLTraceLevel = propertyLevel > applicationLevel ? propertyLevel : applicationLevel;
-    
+
     property_get("debug.egl.debug_proc", value, "");
     long pid = getpid();
     char procPath[128] = {};
@@ -336,14 +324,20 @@
         {
             if (!strcmp(value, cmdline))
                 gEGLDebugLevel = 1;
-        }    
+        }
         fclose(file);
     }
-    
+
     if (gEGLDebugLevel > 0)
     {
         property_get("debug.egl.debug_port", value, "5039");
-        StartDebugServer(atoi(value));
+        const unsigned short port = (unsigned short)atoi(value);
+        property_get("debug.egl.debug_forceUseFile", value, "0");
+        const bool forceUseFile = (bool)atoi(value);
+        property_get("debug.egl.debug_maxFileSize", value, "8");
+        const unsigned int maxFileSize = atoi(value) << 20;
+        property_get("debug.egl.debug_filePath", value, "/data/local/tmp/dump.gles2dbg");
+        StartDebugServer(port, forceUseFile, maxFileSize, value);
     }
 }
 
@@ -586,7 +580,7 @@
 }
 
 static inline
-egl_surface_t* get_surface(EGLSurface surface) {   
+egl_surface_t* get_surface(EGLSurface surface) {
     return egl_to_native_cast<egl_surface_t>(surface);
 }
 
@@ -595,11 +589,6 @@
     return egl_to_native_cast<egl_context_t>(context);
 }
 
-DbgContext * getDbgContextThreadSpecific()
-{
-    return get_context(getContext())->dbg;
-}
-
 static inline
 egl_image_t* get_image(EGLImageKHR image) {
     return egl_to_native_cast<egl_image_t>(image);
@@ -1442,10 +1431,12 @@
         loseCurrent(cur_c);
 
         if (ctx != EGL_NO_CONTEXT) {
-            if (!c->dbg && gEGLDebugLevel > 0)
-                c->dbg = CreateDbgContext(c->version, c->cnx->hooks[c->version]);
             setGLHooksThreadSpecific(c->cnx->hooks[c->version]);
             setContext(ctx);
+            tls_t * const tls = getTLS();
+            if (!tls->dbg && gEGLDebugLevel > 0)
+                tls->dbg = CreateDbgContext(gEGLThreadLocalStorageKey, c->version,
+                                            c->cnx->hooks[c->version]);
             _c.acquire();
             _r.acquire();
             _d.acquire();
diff --git a/opengl/libs/GLES2_dbg/Android.mk b/opengl/libs/GLES2_dbg/Android.mk
index fc40799..853cce6 100644
--- a/opengl/libs/GLES2_dbg/Android.mk
+++ b/opengl/libs/GLES2_dbg/Android.mk
@@ -11,7 +11,7 @@
     src/server.cpp \
     src/vertex.cpp
 
-LOCAL_C_INCLUDES :=	\
+LOCAL_C_INCLUDES := \
     $(LOCAL_PATH) \
     $(LOCAL_PATH)/../ \
     external/stlport/stlport \
@@ -21,7 +21,8 @@
 
 #LOCAL_CFLAGS += -O0 -g -DDEBUG -UNDEBUG
 LOCAL_CFLAGS := -DGOOGLE_PROTOBUF_NO_RTTI
-
+LOCAL_STATIC_LIBRARIES := libprotobuf-cpp-2.3.0-lite liblzf
+LOCAL_SHARED_LIBRARIES := libcutils libutils libstlport
 ifeq ($(TARGET_ARCH),arm)
 	LOCAL_CFLAGS += -fstrict-aliasing
 endif
@@ -43,4 +44,4 @@
 LOCAL_MODULE:= libGLESv2_dbg
 LOCAL_MODULE_TAGS := optional
 
-include $(BUILD_STATIC_LIBRARY)
+include $(BUILD_SHARED_LIBRARY)
diff --git a/opengl/libs/GLES2_dbg/generate_api_cpp.py b/opengl/libs/GLES2_dbg/generate_api_cpp.py
index 66c110f..aeba213 100755
--- a/opengl/libs/GLES2_dbg/generate_api_cpp.py
+++ b/opengl/libs/GLES2_dbg/generate_api_cpp.py
@@ -26,36 +26,36 @@
         return line.replace(annotation, "*")
     else:
         return line
-    
+
 def generate_api(lines):
     externs = []
     i = 0
     # these have been hand written
-    skipFunctions = ["glReadPixels", "glDrawArrays", "glDrawElements"]
-    
+    skipFunctions = ["glDrawArrays", "glDrawElements"]
+
     # these have an EXTEND_Debug_* macro for getting data
-    extendFunctions = ["glCopyTexImage2D", "glCopyTexSubImage2D", "glShaderSource",
-"glTexImage2D", "glTexSubImage2D"]
-    
+    extendFunctions = ["glCopyTexImage2D", "glCopyTexSubImage2D", "glReadPixels",
+"glShaderSource", "glTexImage2D", "glTexSubImage2D"]
+
     # these also needs to be forwarded to DbgContext
     contextFunctions = ["glUseProgram", "glEnableVertexAttribArray", "glDisableVertexAttribArray", 
 "glVertexAttribPointer", "glBindBuffer", "glBufferData", "glBufferSubData", "glDeleteBuffers",]
-    
+
     for line in lines:
         if line.find("API_ENTRY(") >= 0: # a function prototype
             returnType = line[0: line.find(" API_ENTRY(")]
             functionName = line[line.find("(") + 1: line.find(")")] #extract GL function name
             parameterList = line[line.find(")(") + 2: line.find(") {")]
-            
+
             #if line.find("*") >= 0:
             #    extern = "%s Debug_%s(%s);" % (returnType, functionName, parameterList)
             #    externs.append(extern)
             #    continue
-            
+
             if functionName in skipFunctions:
                 sys.stderr.write("!\n! skipping function '%s'\n!\n" % (functionName))
                 continue
-                
+
             parameters = parameterList.split(',')
             paramIndex = 0
             if line.find("*") >= 0 and (line.find("*") < line.find(":") or line.find("*") > line.rfind(":")): # unannotated pointer
@@ -65,21 +65,21 @@
                     sys.stderr.write("%s should be hand written\n" % (extern))
                     print "// FIXME: this function has pointers, it should be hand written"
                     externs.append(extern)
-                
+
             print "%s Debug_%s(%s)\n{" % (returnType, functionName, RemoveAnnotation(parameterList))
             print "    glesv2debugger::Message msg;"
-    
+
             if parameterList == "void":
                 parameters = []
             arguments = ""
             paramNames = []
             inout = ""
             getData = ""
-            
+
             callerMembers = ""
             setCallerMembers = ""
             setMsgParameters = ""
-            
+
             for parameter in parameters:
                 const = parameter.find("const")
                 parameter = parameter.replace("const", "")
@@ -107,7 +107,7 @@
                         annotation = "strlen(%s)" % (paramName)
                     else:
                         count = int(annotation)
-            
+
                     setMsgParameters += "    msg.set_arg%d(ToInt(%s));\n" % (paramIndex, paramName)
                     if paramType.find("void") >= 0:
                         getData += "    msg.mutable_data()->assign(reinterpret_cast<const char *>(%s), %s * sizeof(char));" % (paramName, annotation)
@@ -127,7 +127,7 @@
                 paramIndex += 1
                 callerMembers += "        %s %s;\n" % (paramType, paramName)
                 setCallerMembers += "    caller.%s = %s;\n" % (paramName, paramName)
-            
+
             print "    struct : public FunctionCall {"
             print callerMembers
             print "        const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {"
@@ -141,6 +141,11 @@
             if inout in ["out", "inout"]:
                 print "            msg.set_time((systemTime(timeMode) - c0) * 1e-6f);"
                 print "        " + getData
+            if functionName in extendFunctions:
+                print "\
+#ifdef EXTEND_AFTER_CALL_Debug_%s\n\
+            EXTEND_AFTER_CALL_Debug_%s;\n\
+#endif" % (functionName, functionName)
             if functionName in contextFunctions:
                 print "            getDbgContextThreadSpecific()->%s(%s);" % (functionName, arguments)
             if returnType == "void":
@@ -157,7 +162,10 @@
             if inout in ["in", "inout"]:
                 print getData
             if functionName in extendFunctions:
-                print "    EXTEND_Debug_%s;" % (functionName) 
+                print "\
+#ifdef EXTEND_Debug_%s\n\
+    EXTEND_Debug_%s;\n\
+#endif" % (functionName, functionName)
             print "    int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_%s);"\
                 % (functionName)
             if returnType != "void":
@@ -166,8 +174,8 @@
                 else:
                     print "    return reinterpret_cast<%s>(ret);" % (returnType)
             print "}\n"
-                        
-            
+
+
     print "// FIXME: the following functions should be written by hand"
     for extern in externs:
         print extern
@@ -189,18 +197,23 @@
  ** See the License for the specific language governing permissions and
  ** limitations under the License.
  */
- 
+
 // auto generated by generate_api_cpp.py
 
+#include <utils/Debug.h>
+
 #include "src/header.h"
 #include "src/api.h"
 
-template<typename T> static int ToInt(const T & t) { STATIC_ASSERT(sizeof(T) == sizeof(int), bitcast); return (int &)t; }
-template<typename T> static T FromInt(const int & t) { STATIC_ASSERT(sizeof(T) == sizeof(int), bitcast); return (T &)t; }
-"""    
+template<typename T> static int ToInt(const T & t)
+{
+    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(sizeof(T) == sizeof(int));
+    return (int &)t;
+}
+"""
     lines = open("gl2_api_annotated.in").readlines()
     generate_api(lines)
     #lines = open("gl2ext_api.in").readlines()
     #generate_api(lines)
-            
+
 
diff --git a/opengl/libs/GLES2_dbg/generate_caller_cpp.py b/opengl/libs/GLES2_dbg/generate_caller_cpp.py
index eac2292..ee4208d 100755
--- a/opengl/libs/GLES2_dbg/generate_caller_cpp.py
+++ b/opengl/libs/GLES2_dbg/generate_caller_cpp.py
@@ -177,7 +177,6 @@
 {
     LOGD("GenerateCall function=%u", cmd.function());
     const int * ret = prevRet; // only some functions have return value
-    gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl;
     nsecs_t c0 = systemTime(timeMode);
     switch (cmd.function()) {""")
     
diff --git a/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py b/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py
index 466c447..914ea24 100755
--- a/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py
+++ b/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py
@@ -70,41 +70,43 @@
 """)
 
     i = 0;
-    
+
     lines = open("gl2_api_annotated.in").readlines()
     i = generate_gl_entries(output, lines, i)
     output.write("        // end of GL functions\n")
-    
+
     #lines = open("gl2ext_api.in").readlines()
     #i = generate_gl_entries(output, lines, i)
     #output.write("        // end of GL EXT functions\n")
-    
+
     lines = open("../EGL/egl_entries.in").readlines()
     i = generate_egl_entries(output, lines, i)
     output.write("        // end of GL EXT functions\n")
-    
+
     output.write("        ACK = %d;\n" % (i))
     i += 1
-    
+
     output.write("        NEG = %d;\n" % (i))
     i += 1
-    
+
     output.write("        CONTINUE = %d;\n" % (i))
     i += 1
-    
+
     output.write("        SKIP = %d;\n" % (i))
     i += 1
-    
+
     output.write("        SETPROP = %d;\n" % (i))
     i += 1
-    
+
     output.write("""    }
     required Function function = 2 [default = NEG]; // type/function of message
     enum Type
     {
         BeforeCall = 0;
         AfterCall = 1;
-        Response = 2; // currently used for misc messages
+        AfterGeneratedCall = 2;
+        Response = 3; // currently used for misc messages
+        CompleteCall = 4; // BeforeCall and AfterCall merged
     }
     required Type type = 3;
     required bool expect_response = 4;
@@ -125,16 +127,21 @@
         ReferencedImage = 0; // for image sourced from ReadPixels
         NonreferencedImage = 1; // for image sourced from ReadPixels
     };
-    optional DataType data_type = 23; // most data types can be inferred from function
-    optional int32 pixel_format = 24; // used for image data if format and type 
-    optional int32 pixel_type = 25;   //     cannot be determined from arg 
-    
+    // most data types can be inferred from function
+    optional DataType data_type = 23;
+    // these are used for image data when they cannot be determined from args
+    optional int32 pixel_format = 24;
+    optional int32 pixel_type = 25;
+    optional int32 image_width = 26;
+    optional int32 image_height = 27;
+
     optional float time = 11; // duration of previous GL call (ms)
     enum Prop
     {
-        Capture = 0; // arg0 = true | false
+        CaptureDraw = 0; // arg0 = number of glDrawArrays/Elements to glReadPixels
         TimeMode = 1; // arg0 = SYSTEM_TIME_* in utils/Timers.h
         ExpectResponse = 2; // arg0 = enum Function, arg1 = true/false
+        CaptureSwap = 3; // arg0 = number of eglSwapBuffers to glReadPixels
     };
     optional Prop prop = 21; // used with SETPROP, value in arg0
     optional float clock = 22; // wall clock in seconds
@@ -142,6 +149,6 @@
 """)
 
     output.close()
-    
+
     os.system("aprotoc --cpp_out=src --java_out=../../../../../development/tools/glesv2debugger/src debugger_message.proto")
     os.system('mv -f "src/debugger_message.pb.cc" "src/debugger_message.pb.cpp"')
diff --git a/opengl/libs/GLES2_dbg/src/api.cpp b/opengl/libs/GLES2_dbg/src/api.cpp
index 130ca7e..c483547 100644
--- a/opengl/libs/GLES2_dbg/src/api.cpp
+++ b/opengl/libs/GLES2_dbg/src/api.cpp
@@ -16,11 +16,16 @@
  
 // auto generated by generate_api_cpp.py
 
+#include <utils/Debug.h>
+
 #include "src/header.h"
 #include "src/api.h"
 
-template<typename T> static int ToInt(const T & t) { STATIC_ASSERT(sizeof(T) == sizeof(int), bitcast); return (int &)t; }
-template<typename T> static T FromInt(const int & t) { STATIC_ASSERT(sizeof(T) == sizeof(int), bitcast); return (T &)t; }
+template<typename T> static int ToInt(const T & t)
+{
+    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(sizeof(T) == sizeof(int));
+    return (int &)t;
+}
 
 void Debug_glActiveTexture(GLenum texture)
 {
@@ -592,6 +597,9 @@
 
         const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
             _c->glCopyTexImage2D(target, level, internalformat, x, y, width, height, border);
+#ifdef EXTEND_AFTER_CALL_Debug_glCopyTexImage2D
+            EXTEND_AFTER_CALL_Debug_glCopyTexImage2D;
+#endif
             return 0;
         }
     } caller;
@@ -613,7 +621,9 @@
     msg.set_arg6(height);
     msg.set_arg7(border);
 
+#ifdef EXTEND_Debug_glCopyTexImage2D
     EXTEND_Debug_glCopyTexImage2D;
+#endif
     int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glCopyTexImage2D);
 }
 
@@ -632,6 +642,9 @@
 
         const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
             _c->glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
+#ifdef EXTEND_AFTER_CALL_Debug_glCopyTexSubImage2D
+            EXTEND_AFTER_CALL_Debug_glCopyTexSubImage2D;
+#endif
             return 0;
         }
     } caller;
@@ -653,7 +666,9 @@
     msg.set_arg6(width);
     msg.set_arg7(height);
 
+#ifdef EXTEND_Debug_glCopyTexSubImage2D
     EXTEND_Debug_glCopyTexSubImage2D;
+#endif
     int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glCopyTexSubImage2D);
 }
 
@@ -2164,6 +2179,49 @@
     int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glPolygonOffset);
 }
 
+void Debug_glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels)
+{
+    glesv2debugger::Message msg;
+    struct : public FunctionCall {
+        GLint x;
+        GLint y;
+        GLsizei width;
+        GLsizei height;
+        GLenum format;
+        GLenum type;
+        GLvoid* pixels;
+
+        const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
+            _c->glReadPixels(x, y, width, height, format, type, pixels);
+#ifdef EXTEND_AFTER_CALL_Debug_glReadPixels
+            EXTEND_AFTER_CALL_Debug_glReadPixels;
+#endif
+            return 0;
+        }
+    } caller;
+    caller.x = x;
+    caller.y = y;
+    caller.width = width;
+    caller.height = height;
+    caller.format = format;
+    caller.type = type;
+    caller.pixels = pixels;
+
+    msg.set_arg0(x);
+    msg.set_arg1(y);
+    msg.set_arg2(width);
+    msg.set_arg3(height);
+    msg.set_arg4(format);
+    msg.set_arg5(type);
+    msg.set_arg6(ToInt(pixels));
+
+    // FIXME: check for pointer usage
+#ifdef EXTEND_Debug_glReadPixels
+    EXTEND_Debug_glReadPixels;
+#endif
+    int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glReadPixels);
+}
+
 void Debug_glReleaseShaderCompiler(void)
 {
     glesv2debugger::Message msg;
@@ -2297,6 +2355,9 @@
 
         const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
             _c->glShaderSource(shader, count, string, length);
+#ifdef EXTEND_AFTER_CALL_Debug_glShaderSource
+            EXTEND_AFTER_CALL_Debug_glShaderSource;
+#endif
             return 0;
         }
     } caller;
@@ -2311,7 +2372,9 @@
     msg.set_arg3(ToInt(length));
 
     // FIXME: check for pointer usage
+#ifdef EXTEND_Debug_glShaderSource
     EXTEND_Debug_glShaderSource;
+#endif
     int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glShaderSource);
 }
 
@@ -2472,6 +2535,9 @@
 
         const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
             _c->glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
+#ifdef EXTEND_AFTER_CALL_Debug_glTexImage2D
+            EXTEND_AFTER_CALL_Debug_glTexImage2D;
+#endif
             return 0;
         }
     } caller;
@@ -2496,7 +2562,9 @@
     msg.set_arg8(ToInt(pixels));
 
     // FIXME: check for pointer usage
+#ifdef EXTEND_Debug_glTexImage2D
     EXTEND_Debug_glTexImage2D;
+#endif
     int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glTexImage2D);
 }
 
@@ -2616,6 +2684,9 @@
 
         const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
             _c->glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
+#ifdef EXTEND_AFTER_CALL_Debug_glTexSubImage2D
+            EXTEND_AFTER_CALL_Debug_glTexSubImage2D;
+#endif
             return 0;
         }
     } caller;
@@ -2640,7 +2711,9 @@
     msg.set_arg8(ToInt(pixels));
 
     // FIXME: check for pointer usage
+#ifdef EXTEND_Debug_glTexSubImage2D
     EXTEND_Debug_glTexSubImage2D;
+#endif
     int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glTexSubImage2D);
 }
 
diff --git a/opengl/libs/GLES2_dbg/src/api.h b/opengl/libs/GLES2_dbg/src/api.h
index b9fc341..0b227bc 100644
--- a/opengl/libs/GLES2_dbg/src/api.h
+++ b/opengl/libs/GLES2_dbg/src/api.h
@@ -16,19 +16,29 @@
 
 #define EXTEND_Debug_glCopyTexImage2D \
     DbgContext * const dbg = getDbgContextThreadSpecific(); \
-    GLint readFormat, readType; \
-    dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat); \
-    dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType); \
-    unsigned readSize = GetBytesPerPixel(readFormat, readType) * width * height; \
-    void * readData = dbg->GetReadPixelsBuffer(readSize); \
-    dbg->hooks->gl.glReadPixels(x, y, width, height, readFormat, readType, readData); \
+    void * readData = dbg->GetReadPixelsBuffer(4 * width * height); \
+    /* pick easy format for client to convert */ \
+    dbg->hooks->gl.glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, readData); \
     dbg->CompressReadPixelBuffer(msg.mutable_data()); \
     msg.set_data_type(msg.ReferencedImage); \
-    msg.set_pixel_format(readFormat); \
-    msg.set_pixel_type(readType);
+    msg.set_pixel_format(GL_RGBA); \
+    msg.set_pixel_type(GL_UNSIGNED_BYTE);
 
 #define EXTEND_Debug_glCopyTexSubImage2D EXTEND_Debug_glCopyTexImage2D
 
+#define EXTEND_AFTER_CALL_Debug_glReadPixels \
+    { \
+        DbgContext * const dbg = getDbgContextThreadSpecific(); \
+        if (dbg->IsReadPixelBuffer(pixels)) { \
+            dbg->CompressReadPixelBuffer(msg.mutable_data()); \
+            msg.set_data_type(msg.ReferencedImage); \
+        } else { \
+            const unsigned int size = width * height * GetBytesPerPixel(format, type); \
+            dbg->Compress(pixels, size, msg.mutable_data()); \
+            msg.set_data_type(msg.NonreferencedImage); \
+        } \
+    }
+
 #define EXTEND_Debug_glShaderSource \
     std::string * const data = msg.mutable_data(); \
     for (unsigned i = 0; i < count; i++) \
diff --git a/opengl/libs/GLES2_dbg/src/caller.cpp b/opengl/libs/GLES2_dbg/src/caller.cpp
index 9992f05..6b72751 100644
--- a/opengl/libs/GLES2_dbg/src/caller.cpp
+++ b/opengl/libs/GLES2_dbg/src/caller.cpp
@@ -105,7 +105,6 @@
 {
     LOGD("GenerateCall function=%u", cmd.function());
     const int * ret = prevRet; // only some functions have return value
-    gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl;
     nsecs_t c0 = systemTime(timeMode);
     switch (cmd.function()) {    case glesv2debugger::Message_Function_glActiveTexture:
         dbg->hooks->gl.glActiveTexture(
@@ -772,7 +771,7 @@
     msg.set_time((systemTime(timeMode) - c0) * 1e-6f);
     msg.set_context_id(reinterpret_cast<int>(dbg));
     msg.set_function(cmd.function());
-    msg.set_type(glesv2debugger::Message_Type_AfterCall);
+    msg.set_type(glesv2debugger::Message_Type_AfterGeneratedCall);
     return ret;
 }
 
diff --git a/opengl/libs/GLES2_dbg/src/caller.h b/opengl/libs/GLES2_dbg/src/caller.h
index 5447757..e8111b3 100644
--- a/opengl/libs/GLES2_dbg/src/caller.h
+++ b/opengl/libs/GLES2_dbg/src/caller.h
@@ -138,7 +138,9 @@
         const glesv2debugger::Message & cmd,
         glesv2debugger::Message & msg, const int * const prevRet)
 {
-    assert(0);
+    GLint params = -1;
+    dbg->hooks->gl.glGetProgramiv(cmd.arg0(), cmd.arg1(), &params);
+    msg.mutable_data()->append(reinterpret_cast<char *>(&params), sizeof(params));
     return prevRet;
 }
 
@@ -146,7 +148,10 @@
         const glesv2debugger::Message & cmd,
         glesv2debugger::Message & msg, const int * const prevRet)
 {
-    assert(0);
+    const GLsizei bufSize = static_cast<GLsizei>(dbg->GetBufferSize());
+    GLsizei length = -1;
+    dbg->hooks->gl.glGetProgramInfoLog(cmd.arg0(), bufSize, &length, dbg->GetBuffer());
+    msg.mutable_data()->append(dbg->GetBuffer(), length);
     return prevRet;
 }
 
@@ -162,7 +167,9 @@
                                         const glesv2debugger::Message & cmd,
                                         glesv2debugger::Message & msg, const int * const prevRet)
 {
-    assert(0);
+    GLint params = -1;
+    dbg->hooks->gl.glGetShaderiv(cmd.arg0(), cmd.arg1(), &params);
+    msg.mutable_data()->append(reinterpret_cast<char *>(&params), sizeof(params));
     return prevRet;
 }
 
@@ -170,7 +177,10 @@
         const glesv2debugger::Message & cmd,
         glesv2debugger::Message & msg, const int * const prevRet)
 {
-    assert(0);
+    const GLsizei bufSize = static_cast<GLsizei>(dbg->GetBufferSize());
+    GLsizei length = -1;
+    dbg->hooks->gl.glGetShaderInfoLog(cmd.arg0(), bufSize, &length, dbg->GetBuffer());
+    msg.mutable_data()->append(dbg->GetBuffer(), length);
     return prevRet;
 }
 
diff --git a/opengl/libs/GLES2_dbg/src/dbgcontext.cpp b/opengl/libs/GLES2_dbg/src/dbgcontext.cpp
index cc7336c..fe93874 100644
--- a/opengl/libs/GLES2_dbg/src/dbgcontext.cpp
+++ b/opengl/libs/GLES2_dbg/src/dbgcontext.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "header.h"
+#include "egl_tls.h"
 
 extern "C"
 {
@@ -24,11 +25,23 @@
 namespace android
 {
 
+static pthread_key_t sEGLThreadLocalStorageKey = -1;
+
+DbgContext * getDbgContextThreadSpecific()
+{
+    tls_t* tls = (tls_t*)pthread_getspecific(sEGLThreadLocalStorageKey);
+    return tls->dbg;
+}
+
 DbgContext::DbgContext(const unsigned version, const gl_hooks_t * const hooks,
-                       const unsigned MAX_VERTEX_ATTRIBS)
+                       const unsigned MAX_VERTEX_ATTRIBS, const GLenum readFormat,
+                       const GLenum readType)
         : lzf_buf(NULL), lzf_readIndex(0), lzf_refSize(0), lzf_refBufSize(0)
         , version(version), hooks(hooks)
         , MAX_VERTEX_ATTRIBS(MAX_VERTEX_ATTRIBS)
+        , readFormat(readFormat), readType(readType)
+        , readBytesPerPixel(GetBytesPerPixel(readFormat, readType))
+        , captureSwap(0), captureDraw(0)
         , vertexAttribs(new VertexAttrib[MAX_VERTEX_ATTRIBS])
         , hasNonVBOAttribs(false), indexBuffers(NULL), indexBuffer(NULL)
         , program(0), maxAttrib(0)
@@ -47,13 +60,18 @@
     free(lzf_ref[1]);
 }
 
-DbgContext * CreateDbgContext(const unsigned version, const gl_hooks_t * const hooks)
+DbgContext * CreateDbgContext(const pthread_key_t EGLThreadLocalStorageKey,
+                              const unsigned version, const gl_hooks_t * const hooks)
 {
+    sEGLThreadLocalStorageKey = EGLThreadLocalStorageKey;
     assert(version < 2);
     assert(GL_NO_ERROR == hooks->gl.glGetError());
     GLint MAX_VERTEX_ATTRIBS = 0;
     hooks->gl.glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &MAX_VERTEX_ATTRIBS);
-    return new DbgContext(version, hooks, MAX_VERTEX_ATTRIBS);
+    GLint readFormat, readType;
+    hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat);
+    hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType);
+    return new DbgContext(version, hooks, MAX_VERTEX_ATTRIBS, readFormat, readType);
 }
 
 void DestroyDbgContext(DbgContext * const dbg)
@@ -113,6 +131,7 @@
 {
     if (!lzf_buf)
         lzf_buf = (char *)malloc(LZF_CHUNK_SIZE);
+    assert(lzf_buf);
     const uint32_t totalDecompSize = in_len;
     outStr->append((const char *)&totalDecompSize, sizeof(totalDecompSize));
     for (unsigned int i = 0; i < in_len; i += LZF_CHUNK_SIZE) {
@@ -135,8 +154,10 @@
     if (lzf_refBufSize < size + 8) {
         lzf_refBufSize = size + 8;
         lzf_ref[0] = (unsigned *)realloc(lzf_ref[0], lzf_refBufSize);
+        assert(lzf_ref[0]);
         memset(lzf_ref[0], 0, lzf_refBufSize);
         lzf_ref[1] = (unsigned *)realloc(lzf_ref[1], lzf_refBufSize);
+        assert(lzf_ref[1]);
         memset(lzf_ref[1], 0, lzf_refBufSize);
     }
     if (lzf_refSize != size) // need to clear unused ref to maintain consistency
@@ -151,6 +172,7 @@
 
 void DbgContext::CompressReadPixelBuffer(std::string * const outStr)
 {
+    assert(lzf_ref[0] && lzf_ref[1]);
     unsigned * const ref = lzf_ref[lzf_readIndex ^ 1];
     unsigned * const src = lzf_ref[lzf_readIndex];
     for (unsigned i = 0; i < lzf_refSize / sizeof(*ref) + 1; i++)
@@ -158,13 +180,34 @@
     Compress(ref, lzf_refSize, outStr);
 }
 
+char * DbgContext::GetBuffer()
+{
+    if (!lzf_buf)
+        lzf_buf = (char *)malloc(LZF_CHUNK_SIZE);
+    assert(lzf_buf);
+    return lzf_buf;
+}
+
+unsigned int DbgContext::GetBufferSize()
+{
+    if (!lzf_buf)
+        lzf_buf = (char *)malloc(LZF_CHUNK_SIZE);
+    assert(lzf_buf);
+    if (lzf_buf)
+        return LZF_CHUNK_SIZE;
+    else
+        return 0;
+}
+
 void DbgContext::glUseProgram(GLuint program)
 {
     while (GLenum error = hooks->gl.glGetError())
-        LOGD("DbgContext::glUseProgram: before glGetError() = 0x%.4X", error);
-
+        LOGD("DbgContext::glUseProgram(%u): before glGetError() = 0x%.4X",
+             program, error);
     this->program = program;
-
+    maxAttrib = 0;
+    if (program == 0)
+        return;
     GLint activeAttributes = 0;
     hooks->gl.glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &activeAttributes);
     maxAttrib = 0;
@@ -202,9 +245,9 @@
             maxAttrib = slot;
     }
     delete name;
-
     while (GLenum error = hooks->gl.glGetError())
-        LOGD("DbgContext::glUseProgram: after glGetError() = 0x%.4X", error);
+        LOGD("DbgContext::glUseProgram(%u): after glGetError() = 0x%.4X",
+             program, error);
 }
 
 static bool HasNonVBOAttribs(const DbgContext * const ctx)
@@ -254,14 +297,16 @@
 
 void DbgContext::glEnableVertexAttribArray(GLuint index)
 {
-    assert(index < MAX_VERTEX_ATTRIBS);
+    if (index >= MAX_VERTEX_ATTRIBS)
+        return;
     vertexAttribs[index].enabled = true;
     hasNonVBOAttribs = HasNonVBOAttribs(this);
 }
 
 void DbgContext::glDisableVertexAttribArray(GLuint index)
 {
-    assert(index < MAX_VERTEX_ATTRIBS);
+    if (index >= MAX_VERTEX_ATTRIBS)
+        return;
     vertexAttribs[index].enabled = false;
     hasNonVBOAttribs = HasNonVBOAttribs(this);
 }
diff --git a/opengl/libs/GLES2_dbg/src/debugger_message.pb.cpp b/opengl/libs/GLES2_dbg/src/debugger_message.pb.cpp
index 046c954..40478c3 100644
--- a/opengl/libs/GLES2_dbg/src/debugger_message.pb.cpp
+++ b/opengl/libs/GLES2_dbg/src/debugger_message.pb.cpp
@@ -436,6 +436,8 @@
     case 0:
     case 1:
     case 2:
+    case 3:
+    case 4:
       return true;
     default:
       return false;
@@ -445,7 +447,9 @@
 #ifndef _MSC_VER
 const Message_Type Message::BeforeCall;
 const Message_Type Message::AfterCall;
+const Message_Type Message::AfterGeneratedCall;
 const Message_Type Message::Response;
+const Message_Type Message::CompleteCall;
 const Message_Type Message::Type_MIN;
 const Message_Type Message::Type_MAX;
 const int Message::Type_ARRAYSIZE;
@@ -472,6 +476,7 @@
     case 0:
     case 1:
     case 2:
+    case 3:
       return true;
     default:
       return false;
@@ -479,9 +484,10 @@
 }
 
 #ifndef _MSC_VER
-const Message_Prop Message::Capture;
+const Message_Prop Message::CaptureDraw;
 const Message_Prop Message::TimeMode;
 const Message_Prop Message::ExpectResponse;
+const Message_Prop Message::CaptureSwap;
 const Message_Prop Message::Prop_MIN;
 const Message_Prop Message::Prop_MAX;
 const int Message::Prop_ARRAYSIZE;
@@ -506,6 +512,8 @@
 const int Message::kDataTypeFieldNumber;
 const int Message::kPixelFormatFieldNumber;
 const int Message::kPixelTypeFieldNumber;
+const int Message::kImageWidthFieldNumber;
+const int Message::kImageHeightFieldNumber;
 const int Message::kTimeFieldNumber;
 const int Message::kPropFieldNumber;
 const int Message::kClockFieldNumber;
@@ -545,6 +553,8 @@
   data_type_ = 0;
   pixel_format_ = 0;
   pixel_type_ = 0;
+  image_width_ = 0;
+  image_height_ = 0;
   time_ = 0;
   prop_ = 0;
   clock_ = 0;
@@ -606,6 +616,8 @@
   if (_has_bits_[16 / 32] & (0xffu << (16 % 32))) {
     pixel_format_ = 0;
     pixel_type_ = 0;
+    image_width_ = 0;
+    image_height_ = 0;
     time_ = 0;
     prop_ = 0;
     clock_ = 0;
@@ -790,7 +802,7 @@
           DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
                    float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
                  input, &time_)));
-          _set_bit(18);
+          _set_bit(20);
         } else {
           goto handle_uninterpreted;
         }
@@ -905,7 +917,7 @@
           DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
                    float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
                  input, &clock_)));
-          _set_bit(20);
+          _set_bit(22);
         } else {
           goto handle_uninterpreted;
         }
@@ -960,6 +972,38 @@
         } else {
           goto handle_uninterpreted;
         }
+        if (input->ExpectTag(208)) goto parse_image_width;
+        break;
+      }
+
+      // optional int32 image_width = 26;
+      case 26: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_image_width:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+                 input, &image_width_)));
+          _set_bit(18);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(216)) goto parse_image_height;
+        break;
+      }
+
+      // optional int32 image_height = 27;
+      case 27: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_image_height:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+                 input, &image_height_)));
+          _set_bit(19);
+        } else {
+          goto handle_uninterpreted;
+        }
         if (input->ExpectAtEnd()) return true;
         break;
       }
@@ -1035,7 +1079,7 @@
   }
   
   // optional float time = 11;
-  if (_has_bit(18)) {
+  if (_has_bit(20)) {
     ::google::protobuf::internal::WireFormatLite::WriteFloat(11, this->time(), output);
   }
   
@@ -1065,13 +1109,13 @@
   }
   
   // optional .com.android.glesv2debugger.Message.Prop prop = 21;
-  if (_has_bit(19)) {
+  if (_has_bit(21)) {
     ::google::protobuf::internal::WireFormatLite::WriteEnum(
       21, this->prop(), output);
   }
   
   // optional float clock = 22;
-  if (_has_bit(20)) {
+  if (_has_bit(22)) {
     ::google::protobuf::internal::WireFormatLite::WriteFloat(22, this->clock(), output);
   }
   
@@ -1091,6 +1135,16 @@
     ::google::protobuf::internal::WireFormatLite::WriteInt32(25, this->pixel_type(), output);
   }
   
+  // optional int32 image_width = 26;
+  if (_has_bit(18)) {
+    ::google::protobuf::internal::WireFormatLite::WriteInt32(26, this->image_width(), output);
+  }
+
+  // optional int32 image_height = 27;
+  if (_has_bit(19)) {
+    ::google::protobuf::internal::WireFormatLite::WriteInt32(27, this->image_height(), output);
+  }
+
 }
 
 int Message::ByteSize() const {
@@ -1222,6 +1276,20 @@
           this->pixel_type());
     }
     
+    // optional int32 image_width = 26;
+    if (has_image_width()) {
+      total_size += 2 +
+        ::google::protobuf::internal::WireFormatLite::Int32Size(
+          this->image_width());
+    }
+
+    // optional int32 image_height = 27;
+    if (has_image_height()) {
+      total_size += 2 +
+        ::google::protobuf::internal::WireFormatLite::Int32Size(
+          this->image_height());
+    }
+
     // optional float time = 11;
     if (has_time()) {
       total_size += 1 + 4;
@@ -1312,12 +1380,18 @@
       set_pixel_type(from.pixel_type());
     }
     if (from._has_bit(18)) {
-      set_time(from.time());
+      set_image_width(from.image_width());
     }
     if (from._has_bit(19)) {
-      set_prop(from.prop());
+      set_image_height(from.image_height());
     }
     if (from._has_bit(20)) {
+      set_time(from.time());
+    }
+    if (from._has_bit(21)) {
+      set_prop(from.prop());
+    }
+    if (from._has_bit(22)) {
       set_clock(from.clock());
     }
   }
@@ -1355,6 +1429,8 @@
     std::swap(data_type_, other->data_type_);
     std::swap(pixel_format_, other->pixel_format_);
     std::swap(pixel_type_, other->pixel_type_);
+    std::swap(image_width_, other->image_width_);
+    std::swap(image_height_, other->image_height_);
     std::swap(time_, other->time_);
     std::swap(prop_, other->prop_);
     std::swap(clock_, other->clock_);
diff --git a/opengl/libs/GLES2_dbg/src/debugger_message.pb.h b/opengl/libs/GLES2_dbg/src/debugger_message.pb.h
index b2ec5a0..4ccfebb 100644
--- a/opengl/libs/GLES2_dbg/src/debugger_message.pb.h
+++ b/opengl/libs/GLES2_dbg/src/debugger_message.pb.h
@@ -236,11 +236,13 @@
 enum Message_Type {
   Message_Type_BeforeCall = 0,
   Message_Type_AfterCall = 1,
-  Message_Type_Response = 2
+  Message_Type_AfterGeneratedCall = 2,
+  Message_Type_Response = 3,
+  Message_Type_CompleteCall = 4
 };
 bool Message_Type_IsValid(int value);
 const Message_Type Message_Type_Type_MIN = Message_Type_BeforeCall;
-const Message_Type Message_Type_Type_MAX = Message_Type_Response;
+const Message_Type Message_Type_Type_MAX = Message_Type_CompleteCall;
 const int Message_Type_Type_ARRAYSIZE = Message_Type_Type_MAX + 1;
 
 enum Message_DataType {
@@ -253,13 +255,14 @@
 const int Message_DataType_DataType_ARRAYSIZE = Message_DataType_DataType_MAX + 1;
 
 enum Message_Prop {
-  Message_Prop_Capture = 0,
+  Message_Prop_CaptureDraw = 0,
   Message_Prop_TimeMode = 1,
-  Message_Prop_ExpectResponse = 2
+  Message_Prop_ExpectResponse = 2,
+  Message_Prop_CaptureSwap = 3
 };
 bool Message_Prop_IsValid(int value);
-const Message_Prop Message_Prop_Prop_MIN = Message_Prop_Capture;
-const Message_Prop Message_Prop_Prop_MAX = Message_Prop_ExpectResponse;
+const Message_Prop Message_Prop_Prop_MIN = Message_Prop_CaptureDraw;
+const Message_Prop Message_Prop_Prop_MAX = Message_Prop_CaptureSwap;
 const int Message_Prop_Prop_ARRAYSIZE = Message_Prop_Prop_MAX + 1;
 
 // ===================================================================
@@ -510,7 +513,9 @@
   typedef Message_Type Type;
   static const Type BeforeCall = Message_Type_BeforeCall;
   static const Type AfterCall = Message_Type_AfterCall;
+  static const Type AfterGeneratedCall = Message_Type_AfterGeneratedCall;
   static const Type Response = Message_Type_Response;
+  static const Type CompleteCall = Message_Type_CompleteCall;
   static inline bool Type_IsValid(int value) {
     return Message_Type_IsValid(value);
   }
@@ -535,9 +540,10 @@
     Message_DataType_DataType_ARRAYSIZE;
   
   typedef Message_Prop Prop;
-  static const Prop Capture = Message_Prop_Capture;
+  static const Prop CaptureDraw = Message_Prop_CaptureDraw;
   static const Prop TimeMode = Message_Prop_TimeMode;
   static const Prop ExpectResponse = Message_Prop_ExpectResponse;
+  static const Prop CaptureSwap = Message_Prop_CaptureSwap;
   static inline bool Prop_IsValid(int value) {
     return Message_Prop_IsValid(value);
   }
@@ -679,6 +685,20 @@
   inline ::google::protobuf::int32 pixel_type() const;
   inline void set_pixel_type(::google::protobuf::int32 value);
   
+  // optional int32 image_width = 26;
+  inline bool has_image_width() const;
+  inline void clear_image_width();
+  static const int kImageWidthFieldNumber = 26;
+  inline ::google::protobuf::int32 image_width() const;
+  inline void set_image_width(::google::protobuf::int32 value);
+
+  // optional int32 image_height = 27;
+  inline bool has_image_height() const;
+  inline void clear_image_height();
+  static const int kImageHeightFieldNumber = 27;
+  inline ::google::protobuf::int32 image_height() const;
+  inline void set_image_height(::google::protobuf::int32 value);
+
   // optional float time = 11;
   inline bool has_time() const;
   inline void clear_time();
@@ -723,6 +743,8 @@
   int data_type_;
   ::google::protobuf::int32 pixel_format_;
   ::google::protobuf::int32 pixel_type_;
+  ::google::protobuf::int32 image_width_;
+  ::google::protobuf::int32 image_height_;
   float time_;
   int prop_;
   float clock_;
@@ -730,7 +752,7 @@
   friend void protobuf_AssignDesc_debugger_5fmessage_2eproto();
   friend void protobuf_ShutdownFile_debugger_5fmessage_2eproto();
   
-  ::google::protobuf::uint32 _has_bits_[(21 + 31) / 32];
+  ::google::protobuf::uint32 _has_bits_[(23 + 31) / 32];
   
   // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
   inline bool _has_bit(int index) const {
@@ -1070,52 +1092,84 @@
   pixel_type_ = value;
 }
 
+// optional int32 image_width = 26;
+inline bool Message::has_image_width() const {
+  return _has_bit(18);
+}
+inline void Message::clear_image_width() {
+  image_width_ = 0;
+  _clear_bit(18);
+}
+inline ::google::protobuf::int32 Message::image_width() const {
+  return image_width_;
+}
+inline void Message::set_image_width(::google::protobuf::int32 value) {
+  _set_bit(18);
+  image_width_ = value;
+}
+
+// optional int32 image_height = 27;
+inline bool Message::has_image_height() const {
+  return _has_bit(19);
+}
+inline void Message::clear_image_height() {
+  image_height_ = 0;
+  _clear_bit(19);
+}
+inline ::google::protobuf::int32 Message::image_height() const {
+  return image_height_;
+}
+inline void Message::set_image_height(::google::protobuf::int32 value) {
+  _set_bit(19);
+  image_height_ = value;
+}
+
 // optional float time = 11;
 inline bool Message::has_time() const {
-  return _has_bit(18);
+  return _has_bit(20);
 }
 inline void Message::clear_time() {
   time_ = 0;
-  _clear_bit(18);
+  _clear_bit(20);
 }
 inline float Message::time() const {
   return time_;
 }
 inline void Message::set_time(float value) {
-  _set_bit(18);
+  _set_bit(20);
   time_ = value;
 }
 
 // optional .com.android.glesv2debugger.Message.Prop prop = 21;
 inline bool Message::has_prop() const {
-  return _has_bit(19);
+  return _has_bit(21);
 }
 inline void Message::clear_prop() {
   prop_ = 0;
-  _clear_bit(19);
+  _clear_bit(21);
 }
 inline ::com::android::glesv2debugger::Message_Prop Message::prop() const {
   return static_cast< ::com::android::glesv2debugger::Message_Prop >(prop_);
 }
 inline void Message::set_prop(::com::android::glesv2debugger::Message_Prop value) {
   GOOGLE_DCHECK(::com::android::glesv2debugger::Message_Prop_IsValid(value));
-  _set_bit(19);
+  _set_bit(21);
   prop_ = value;
 }
 
 // optional float clock = 22;
 inline bool Message::has_clock() const {
-  return _has_bit(20);
+  return _has_bit(22);
 }
 inline void Message::clear_clock() {
   clock_ = 0;
-  _clear_bit(20);
+  _clear_bit(22);
 }
 inline float Message::clock() const {
   return clock_;
 }
 inline void Message::set_clock(float value) {
-  _set_bit(20);
+  _set_bit(22);
   clock_ = value;
 }
 
diff --git a/opengl/libs/GLES2_dbg/src/egl.cpp b/opengl/libs/GLES2_dbg/src/egl.cpp
index 3a20e21..eb28d06 100644
--- a/opengl/libs/GLES2_dbg/src/egl.cpp
+++ b/opengl/libs/GLES2_dbg/src/egl.cpp
@@ -18,6 +18,7 @@
 
 EGLBoolean Debug_eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
 {
+    DbgContext * const dbg = getDbgContextThreadSpecific();
     glesv2debugger::Message msg;
     struct : public FunctionCall {
         EGLDisplay dpy;
@@ -33,7 +34,21 @@
 
     msg.set_arg0(reinterpret_cast<int>(dpy));
     msg.set_arg1(reinterpret_cast<int>(draw));
-
+    if (dbg->captureSwap > 0) {
+        dbg->captureSwap--;
+        int viewport[4] = {};
+        dbg->hooks->gl.glGetIntegerv(GL_VIEWPORT, viewport);
+        void * pixels = dbg->GetReadPixelsBuffer(viewport[2] * viewport[3] *
+                        dbg->readBytesPerPixel);
+        dbg->hooks->gl.glReadPixels(viewport[0], viewport[1], viewport[2],
+                                    viewport[3], dbg->readFormat, dbg->readType, pixels);
+        dbg->CompressReadPixelBuffer(msg.mutable_data());
+        msg.set_data_type(msg.ReferencedImage);
+        msg.set_pixel_format(dbg->readFormat);
+        msg.set_pixel_type(dbg->readType);
+        msg.set_image_width(viewport[2]);
+        msg.set_image_height(viewport[3]);
+    }
     int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_eglSwapBuffers);
     return static_cast<EGLBoolean>(reinterpret_cast<int>(ret));
 }
diff --git a/opengl/libs/GLES2_dbg/src/header.h b/opengl/libs/GLES2_dbg/src/header.h
index 9218da5..c9e6c41 100644
--- a/opengl/libs/GLES2_dbg/src/header.h
+++ b/opengl/libs/GLES2_dbg/src/header.h
@@ -14,6 +14,9 @@
  ** limitations under the License.
  */
 
+#ifndef ANDROID_GLES2_DBG_HEADER_H
+#define ANDROID_GLES2_DBG_HEADER_H
+
 #include <stdlib.h>
 #include <ctype.h>
 #include <string.h>
@@ -24,9 +27,7 @@
 
 #include <cutils/log.h>
 #include <utils/Timers.h>
-#include <../../../libcore/include/StaticAssert.h>
 
-#define EGL_TRACE 1
 #include "hooks.h"
 
 #include "glesv2dbg.h"
@@ -39,8 +40,6 @@
 using namespace android;
 using namespace com::android;
 
-#define API_ENTRY(_api) Debug_##_api
-
 #ifndef __location__
 #define __HIERALLOC_STRING_0__(s)   #s
 #define __HIERALLOC_STRING_1__(s)   __HIERALLOC_STRING_0__(s)
@@ -76,7 +75,7 @@
 struct DbgContext {
 private:
     static const unsigned int LZF_CHUNK_SIZE = 256 * 1024;
-    char * lzf_buf; // malloc / free; for lzf chunk compression
+    char * lzf_buf; // malloc / free; for lzf chunk compression and other uses
 
     // used as buffer and reference frame for ReadPixels; malloc/free
     unsigned * lzf_ref [2];
@@ -84,9 +83,14 @@
     unsigned lzf_refSize, lzf_refBufSize; // bytes
 
 public:
-    const unsigned version; // 0 is GLES1, 1 is GLES2
+    const unsigned int version; // 0 is GLES1, 1 is GLES2
     const gl_hooks_t * const hooks;
-    const unsigned MAX_VERTEX_ATTRIBS;
+    const unsigned int MAX_VERTEX_ATTRIBS;
+    const GLenum readFormat, readType; // implementation supported glReadPixels
+    const unsigned int readBytesPerPixel;
+
+    unsigned int captureSwap; // number of eglSwapBuffers to glReadPixels
+    unsigned int captureDraw; // number of glDrawArrays/Elements to glReadPixels
 
     GLFunctionBitfield expectResponse;
 
@@ -119,7 +123,8 @@
     unsigned maxAttrib; // number of slots used by program
 
     DbgContext(const unsigned version, const gl_hooks_t * const hooks,
-               const unsigned MAX_VERTEX_ATTRIBS);
+               const unsigned MAX_VERTEX_ATTRIBS, const GLenum readFormat,
+               const GLenum readType);
     ~DbgContext();
 
     void Fetch(const unsigned index, std::string * const data) const;
@@ -129,6 +134,8 @@
         return ptr == lzf_ref[lzf_readIndex];
     }
     void CompressReadPixelBuffer(std::string * const outStr);
+    char * GetBuffer(); // allocates lzf_buf if NULL
+    unsigned int GetBufferSize(); // allocates lzf_buf if NULL
 
     void glUseProgram(GLuint program);
     void glEnableVertexAttribArray(GLuint index);
@@ -141,9 +148,7 @@
     void glDeleteBuffers(GLsizei n, const GLuint *buffers);
 };
 
-
 DbgContext * getDbgContextThreadSpecific();
-#define DBGCONTEXT(ctx) DbgContext * const ctx = getDbgContextThreadSpecific();
 
 struct FunctionCall {
     virtual const int * operator()(gl_hooks_t::gl_t const * const _c,
@@ -152,7 +157,6 @@
 };
 
 // move these into DbgContext as static
-extern bool capture;
 extern int timeMode; // SYSTEM_TIME_
 
 extern int clientSock, serverSock;
@@ -169,3 +173,5 @@
 const int * GenerateCall(DbgContext * const dbg, const glesv2debugger::Message & cmd,
                          glesv2debugger::Message & msg, const int * const prevRet);
 }; // namespace android {
+
+#endif // #ifndef ANDROID_GLES2_DBG_HEADER_H
diff --git a/opengl/libs/GLES2_dbg/src/server.cpp b/opengl/libs/GLES2_dbg/src/server.cpp
index 7039c84..f13d6cc 100644
--- a/opengl/libs/GLES2_dbg/src/server.cpp
+++ b/opengl/libs/GLES2_dbg/src/server.cpp
@@ -28,7 +28,8 @@
 {
 
 int serverSock = -1, clientSock = -1;
-
+FILE * file = NULL;
+unsigned int MAX_FILE_SIZE = 0;
 int timeMode = SYSTEM_TIME_THREAD;
 
 static void Die(const char * msg)
@@ -38,18 +39,25 @@
     exit(1);
 }
 
-void StartDebugServer(unsigned short port)
+void StartDebugServer(const unsigned short port, const bool forceUseFile,
+                      const unsigned int maxFileSize, const char * const filePath)
 {
+    MAX_FILE_SIZE = maxFileSize;
+
     LOGD("GLESv2_dbg: StartDebugServer");
-    if (serverSock >= 0)
+    if (serverSock >= 0 || file)
         return;
 
     LOGD("GLESv2_dbg: StartDebugServer create socket");
     struct sockaddr_in server = {}, client = {};
 
     /* Create the TCP socket */
-    if ((serverSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
-        Die("Failed to create socket");
+    if (forceUseFile || (serverSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
+        file = fopen(filePath, "wb");
+        if (!file)
+            Die("Failed to create socket and file");
+        else
+            return;
     }
     /* Construct the server sockaddr_in structure */
     server.sin_family = AF_INET;                  /* Internet/IP */
@@ -92,13 +100,17 @@
         close(serverSock);
         serverSock = -1;
     }
-
+    if (file) {
+        fclose(file);
+        file = NULL;
+    }
 }
 
 void Receive(glesv2debugger::Message & cmd)
 {
+    if (clientSock < 0)
+        return;
     unsigned len = 0;
-
     int received = recv(clientSock, &len, 4, MSG_WAITALL);
     if (received < 0)
         Die("Failed to receive response length");
@@ -106,7 +118,6 @@
         LOGD("received %dB: %.8X", received, len);
         Die("Received length mismatch, expected 4");
     }
-    len = ntohl(len);
     static void * buffer = NULL;
     static unsigned bufferSize = 0;
     if (bufferSize < len) {
@@ -125,6 +136,8 @@
 
 bool TryReceive(glesv2debugger::Message & cmd)
 {
+    if (clientSock < 0)
+        return false;
     fd_set readSet;
     FD_ZERO(&readSet);
     FD_SET(clientSock, &readSet);
@@ -153,7 +166,19 @@
         assert(msg.has_context_id() && msg.context_id() != 0);
     static std::string str;
     msg.SerializeToString(&str);
-    uint32_t len = htonl(str.length());
+    const uint32_t len = str.length();
+    if (clientSock < 0) {
+        if (file) {
+            fwrite(&len, sizeof(len), 1, file);
+            fwrite(str.data(), len, 1, file);
+            if (ftell(file) >= MAX_FILE_SIZE) {
+                fclose(file);
+                Die("MAX_FILE_SIZE reached");
+            }
+        }
+        pthread_mutex_unlock(&mutex);
+        return 0;
+    }
     int sent = -1;
     sent = send(clientSock, &len, sizeof(len), 0);
     if (sent != sizeof(len)) {
@@ -169,14 +194,13 @@
     }
 
     // try to receive commands even though not expecting response,
-    // since client can send SETPROP commands anytime
+    //  since client can send SETPROP and other commands anytime
     if (!msg.expect_response()) {
         if (TryReceive(cmd)) {
-            LOGD("Send: TryReceived");
             if (glesv2debugger::Message_Function_SETPROP == cmd.function())
-                LOGD("Send: received SETPROP");
+                LOGD("Send: TryReceived SETPROP");
             else
-                LOGD("Send: received something else");
+                LOGD("Send: TryReceived %u", cmd.function());
         }
     } else
         Receive(cmd);
@@ -188,9 +212,9 @@
 void SetProp(DbgContext * const dbg, const glesv2debugger::Message & cmd)
 {
     switch (cmd.prop()) {
-    case glesv2debugger::Message_Prop_Capture:
-        LOGD("SetProp Message_Prop_Capture %d", cmd.arg0());
-        capture = cmd.arg0();
+    case glesv2debugger::Message_Prop_CaptureDraw:
+        LOGD("SetProp Message_Prop_CaptureDraw %d", cmd.arg0());
+        dbg->captureDraw = cmd.arg0();
         break;
     case glesv2debugger::Message_Prop_TimeMode:
         LOGD("SetProp Message_Prop_TimeMode %d", cmd.arg0());
@@ -200,6 +224,10 @@
         LOGD("SetProp Message_Prop_ExpectResponse %d=%d", cmd.arg0(), cmd.arg1());
         dbg->expectResponse.Bit((glesv2debugger::Message_Function)cmd.arg0(), cmd.arg1());
         break;
+    case glesv2debugger::Message_Prop_CaptureSwap:
+        LOGD("SetProp CaptureSwap %d", cmd.arg0());
+        dbg->captureSwap = cmd.arg0();
+        break;
     default:
         assert(0);
     }
@@ -213,12 +241,16 @@
     glesv2debugger::Message cmd;
     msg.set_context_id(reinterpret_cast<int>(dbg));
     msg.set_type(glesv2debugger::Message_Type_BeforeCall);
-    const bool expectResponse = dbg->expectResponse.Bit(function);
+    bool expectResponse = dbg->expectResponse.Bit(function);
     msg.set_expect_response(expectResponse);
     msg.set_function(function);
-    if (!expectResponse)
-        cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+
+    // when not exectResponse, set cmd to CONTINUE then SKIP
+    cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+    cmd.set_expect_response(false);
+    glesv2debugger::Message_Function oldCmd = cmd.function();
     Send(msg, cmd);
+    expectResponse = cmd.expect_response();
     while (true) {
         msg.Clear();
         nsecs_t c0 = systemTime(timeMode);
@@ -233,22 +265,34 @@
             msg.set_function(function);
             msg.set_type(glesv2debugger::Message_Type_AfterCall);
             msg.set_expect_response(expectResponse);
-            if (!expectResponse)
+            if (!expectResponse) {
                 cmd.set_function(glesv2debugger::Message_Function_SKIP);
+                cmd.set_expect_response(false);
+            }
+            oldCmd = cmd.function();
             Send(msg, cmd);
+            expectResponse = cmd.expect_response();
             break;
         case glesv2debugger::Message_Function_SKIP:
             return const_cast<int *>(ret);
         case glesv2debugger::Message_Function_SETPROP:
             SetProp(dbg, cmd);
-            Receive(cmd);
+            expectResponse = cmd.expect_response();
+            if (!expectResponse) // SETPROP is "out of band"
+                cmd.set_function(oldCmd);
+            else
+                Receive(cmd);
             break;
         default:
             ret = GenerateCall(dbg, cmd, msg, ret);
             msg.set_expect_response(expectResponse);
-            if (!expectResponse)
+            if (!expectResponse) {
                 cmd.set_function(cmd.SKIP);
+                cmd.set_expect_response(expectResponse);
+            }
+            oldCmd = cmd.function();
             Send(msg, cmd);
+            expectResponse = cmd.expect_response();
             break;
         }
     }
diff --git a/opengl/libs/GLES2_dbg/src/vertex.cpp b/opengl/libs/GLES2_dbg/src/vertex.cpp
index 471e5ad..7edc050 100644
--- a/opengl/libs/GLES2_dbg/src/vertex.cpp
+++ b/opengl/libs/GLES2_dbg/src/vertex.cpp
@@ -21,74 +21,13 @@
 bool capture; // capture after each glDraw*
 }
 
-void Debug_glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels)
-{
-    DbgContext * const dbg = getDbgContextThreadSpecific();
-    glesv2debugger::Message msg, cmd;
-    msg.set_context_id(reinterpret_cast<int>(dbg));
-    msg.set_type(glesv2debugger::Message_Type_BeforeCall);
-    const bool expectResponse = dbg->expectResponse.Bit(glesv2debugger::Message_Function_glReadPixels);
-    msg.set_expect_response(expectResponse);
-    msg.set_function(glesv2debugger::Message_Function_glReadPixels);
-    msg.set_arg0(x);
-    msg.set_arg1(y);
-    msg.set_arg2(width);
-    msg.set_arg3(height);
-    msg.set_arg4(format);
-    msg.set_arg5(type);
-    msg.set_arg6(reinterpret_cast<int>(pixels));
-
-    const unsigned size = width * height * GetBytesPerPixel(format, type);
-    if (!expectResponse)
-        cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
-    Send(msg, cmd);
-    float t = 0;
-    while (true) {
-        msg.Clear();
-        nsecs_t c0 = systemTime(timeMode);
-        switch (cmd.function()) {
-        case glesv2debugger::Message_Function_CONTINUE:
-            dbg->hooks->gl.glReadPixels(x, y, width, height, format, type, pixels);
-            msg.set_time((systemTime(timeMode) - c0) * 1e-6f);
-            msg.set_context_id(reinterpret_cast<int>(dbg));
-            msg.set_function(glesv2debugger::Message_Function_glReadPixels);
-            msg.set_type(glesv2debugger::Message_Type_AfterCall);
-            msg.set_expect_response(expectResponse);
-            if (dbg->IsReadPixelBuffer(pixels)) {
-                dbg->CompressReadPixelBuffer(msg.mutable_data());
-                msg.set_data_type(msg.ReferencedImage);
-            } else {
-                dbg->Compress(pixels, size, msg.mutable_data());
-                msg.set_data_type(msg.NonreferencedImage);
-            }
-            if (!expectResponse)
-                cmd.set_function(glesv2debugger::Message_Function_SKIP);
-            Send(msg, cmd);
-            break;
-        case glesv2debugger::Message_Function_SKIP:
-            return;
-        case glesv2debugger::Message_Function_SETPROP:
-            SetProp(dbg, cmd);
-            Receive(cmd);
-            break;
-        default:
-            GenerateCall(dbg, cmd, msg, NULL);
-            msg.set_expect_response(expectResponse);
-            if (!expectResponse)
-                cmd.set_function(cmd.SKIP);
-            Send(msg, cmd);
-            break;
-        }
-    }
-}
-
 void Debug_glDrawArrays(GLenum mode, GLint first, GLsizei count)
 {
     DbgContext * const dbg = getDbgContextThreadSpecific();
     glesv2debugger::Message msg, cmd;
     msg.set_context_id(reinterpret_cast<int>(dbg));
     msg.set_type(glesv2debugger::Message_Type_BeforeCall);
-    const bool expectResponse = dbg->expectResponse.Bit(glesv2debugger::Message_Function_glDrawArrays);
+    bool expectResponse = dbg->expectResponse.Bit(glesv2debugger::Message_Function_glDrawArrays);
     msg.set_expect_response(expectResponse);
     msg.set_function(glesv2debugger::Message_Function_glDrawArrays);
     msg.set_arg0(mode);
@@ -103,11 +42,14 @@
     }
 
     void * pixels = NULL;
-    GLint readFormat = 0, readType = 0;
     int viewport[4] = {};
-    if (!expectResponse)
+    if (!expectResponse) {
         cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+        cmd.set_expect_response(false);
+    }
+    glesv2debugger::Message_Function oldCmd = cmd.function();
     Send(msg, cmd);
+    expectResponse = cmd.expect_response();
     while (true) {
         msg.Clear();
         nsecs_t c0 = systemTime(timeMode);
@@ -121,31 +63,47 @@
             msg.set_expect_response(expectResponse);
             if (!expectResponse)
                 cmd.set_function(glesv2debugger::Message_Function_SKIP);
+            if (!expectResponse) {
+                cmd.set_function(glesv2debugger::Message_Function_SKIP);
+                cmd.set_expect_response(false);
+            }
+            oldCmd = cmd.function();
             Send(msg, cmd);
-            if (capture) {
+            expectResponse = cmd.expect_response();
+            // TODO: pack glReadPixels data with vertex data instead of
+            //  relying on sperate call for transport, this would allow
+            //  auto generated message loop using EXTEND_Debug macro
+            if (dbg->captureDraw > 0) {
+                dbg->captureDraw--;
                 dbg->hooks->gl.glGetIntegerv(GL_VIEWPORT, viewport);
-                dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat);
-                dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType);
 //                LOGD("glDrawArrays CAPTURE: x=%d y=%d width=%d height=%d format=0x%.4X type=0x%.4X",
 //                     viewport[0], viewport[1], viewport[2], viewport[3], readFormat, readType);
                 pixels = dbg->GetReadPixelsBuffer(viewport[2] * viewport[3] *
-                                                  GetBytesPerPixel(readFormat, readType));
+                                                  dbg->readBytesPerPixel);
                 Debug_glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3],
-                                   readFormat, readType, pixels);
+                                   dbg->readFormat, dbg->readType, pixels);
             }
             break;
         case glesv2debugger::Message_Function_SKIP:
             return;
         case glesv2debugger::Message_Function_SETPROP:
             SetProp(dbg, cmd);
-            Receive(cmd);
+            expectResponse = cmd.expect_response();
+            if (!expectResponse) // SETPROP is "out of band"
+                cmd.set_function(oldCmd);
+            else
+                Receive(cmd);
             break;
         default:
             GenerateCall(dbg, cmd, msg, NULL);
             msg.set_expect_response(expectResponse);
-            if (!expectResponse)
+            if (!expectResponse) {
                 cmd.set_function(cmd.SKIP);
+                cmd.set_expect_response(expectResponse);
+            }
+            oldCmd = cmd.function();
             Send(msg, cmd);
+            expectResponse = cmd.expect_response();
             break;
         }
     }
@@ -169,7 +127,7 @@
     glesv2debugger::Message msg, cmd;
     msg.set_context_id(reinterpret_cast<int>(dbg));
     msg.set_type(glesv2debugger::Message_Type_BeforeCall);
-    const bool expectResponse = dbg->expectResponse.Bit(glesv2debugger::Message_Function_glDrawElements);
+    bool expectResponse = dbg->expectResponse.Bit(glesv2debugger::Message_Function_glDrawElements);
     msg.set_expect_response(expectResponse);
     msg.set_function(glesv2debugger::Message_Function_glDrawElements);
     msg.set_arg0(mode);
@@ -195,11 +153,14 @@
         assert(0);
 
     void * pixels = NULL;
-    GLint readFormat = 0, readType = 0;
     int viewport[4] = {};
-    if (!expectResponse)
+    if (!expectResponse) {
         cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+        cmd.set_expect_response(false);
+    }
+    glesv2debugger::Message_Function oldCmd = cmd.function();
     Send(msg, cmd);
+    expectResponse = cmd.expect_response();
     while (true) {
         msg.Clear();
         nsecs_t c0 = systemTime(timeMode);
@@ -213,31 +174,45 @@
             msg.set_expect_response(expectResponse);
             if (!expectResponse)
                 cmd.set_function(glesv2debugger::Message_Function_SKIP);
+            if (!expectResponse) {
+                cmd.set_function(glesv2debugger::Message_Function_SKIP);
+                cmd.set_expect_response(false);
+            }
+            oldCmd = cmd.function();
             Send(msg, cmd);
-            if (capture) {
+            expectResponse = cmd.expect_response();
+            // TODO: pack glReadPixels data with vertex data instead of
+            //  relying on sperate call for transport, this would allow
+            //  auto generated message loop using EXTEND_Debug macro
+            if (dbg->captureDraw > 0) {
+                dbg->captureDraw--;
                 dbg->hooks->gl.glGetIntegerv(GL_VIEWPORT, viewport);
-                dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat);
-                dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType);
-//                LOGD("glDrawArrays CAPTURE: x=%d y=%d width=%d height=%d format=0x%.4X type=0x%.4X",
-//                     viewport[0], viewport[1], viewport[2], viewport[3], readFormat, readType);
                 pixels = dbg->GetReadPixelsBuffer(viewport[2] * viewport[3] *
-                                                  GetBytesPerPixel(readFormat, readType));
+                                                  dbg->readBytesPerPixel);
                 Debug_glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3],
-                                   readFormat, readType, pixels);
+                                   dbg->readFormat, dbg->readType, pixels);
             }
             break;
         case glesv2debugger::Message_Function_SKIP:
             return;
         case glesv2debugger::Message_Function_SETPROP:
             SetProp(dbg, cmd);
-            Receive(cmd);
+            expectResponse = cmd.expect_response();
+            if (!expectResponse) // SETPROP is "out of band"
+                cmd.set_function(oldCmd);
+            else
+                Receive(cmd);
             break;
         default:
             GenerateCall(dbg, cmd, msg, NULL);
             msg.set_expect_response(expectResponse);
-            if (!expectResponse)
+            if (!expectResponse) {
                 cmd.set_function(cmd.SKIP);
+                cmd.set_expect_response(expectResponse);
+            }
+            oldCmd = cmd.function();
             Send(msg, cmd);
+            expectResponse = cmd.expect_response();
             break;
         }
     }
diff --git a/opengl/libs/egl_tls.h b/opengl/libs/egl_tls.h
new file mode 100644
index 0000000..087989a
--- /dev/null
+++ b/opengl/libs/egl_tls.h
@@ -0,0 +1,40 @@
+/*
+ ** Copyright 2011, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ **     http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#ifndef ANDROID_EGL_TLS_H
+#define ANDROID_EGL_TLS_H
+
+#include <EGL/egl.h>
+
+#include "glesv2dbg.h"
+
+namespace android
+{
+struct tls_t {
+    tls_t() : error(EGL_SUCCESS), ctx(0), logCallWithNoContext(EGL_TRUE), dbg(0) { }
+    ~tls_t() {
+        if (dbg)
+            DestroyDbgContext(dbg);
+    }
+
+    EGLint      error;
+    EGLContext  ctx;
+    EGLBoolean  logCallWithNoContext;
+    DbgContext* dbg;
+};
+}
+
+#endif
diff --git a/opengl/libs/glesv2dbg.h b/opengl/libs/glesv2dbg.h
index 8029dce..ee2c011 100644
--- a/opengl/libs/glesv2dbg.h
+++ b/opengl/libs/glesv2dbg.h
@@ -13,20 +13,27 @@
  ** See the License for the specific language governing permissions and
  ** limitations under the License.
  */
- 
+
 #ifndef _GLESV2_DBG_H_
 #define _GLESV2_DBG_H_
 
+#include <pthread.h>
+
 namespace android
 {
-    struct DbgContext;
-    
-    DbgContext * CreateDbgContext(const unsigned version, const gl_hooks_t * const hooks);
-    void DestroyDbgContext(DbgContext * const dbg);
-    
-    void StartDebugServer(unsigned short port); // create and bind socket if haven't already
-    void StopDebugServer(); // close socket if open
-    
+struct DbgContext;
+
+DbgContext * CreateDbgContext(const pthread_key_t EGLThreadLocalStorageKey,
+                              const unsigned version, const gl_hooks_t * const hooks);
+
+void DestroyDbgContext(DbgContext * const dbg);
+
+// create and bind socket if haven't already, if failed to create socket or
+//  forceUseFile, then open /data/local/tmp/dump.gles2dbg, exit when size reached
+void StartDebugServer(const unsigned short port, const bool forceUseFile,
+                      const unsigned int maxFileSize, const char * const filePath);
+void StopDebugServer(); // close socket if open
+
 }; // namespace android
 
 #endif // #ifndef _GLESV2_DBG_H_
diff --git a/opengl/tools/glgen/gen b/opengl/tools/glgen/gen
index 6c1a231..31f4190 100755
--- a/opengl/tools/glgen/gen
+++ b/opengl/tools/glgen/gen
@@ -21,7 +21,7 @@
 echo "package android.graphics;" > out/android/graphics/Canvas.java
 echo "public interface Canvas {}" >> out/android/graphics/Canvas.java
 
-echo "package android.app; import android.content.pm.IPackageManager; public class ActivityThread { public static final ActivityThread currentActivityThread() { return null; } public static final String currentPackageName(){ return null; } public static IPackageManager getPackageManager() { return null;} }" > out/android/app/ActivityThread.java
+echo "package android.app; import android.content.pm.IPackageManager; public class AppGlobals { public static IPackageManager getPackageManager() { return null;} }" > out/android/app/AppGlobals.java
 # echo "package android.content; import android.content.pm.PackageManager; public interface Context { public PackageManager getPackageManager(); }" > out/android/content/Context.java
 echo "package android.content.pm; public class ApplicationInfo {public int targetSdkVersion;}" > out/android/content/pm/ApplicationInfo.java
 echo "package android.content.pm; public interface IPackageManager {ApplicationInfo getApplicationInfo(java.lang.String packageName, int flags) throws android.os.RemoteException;}" > out/android/content/pm/IPackageManager.java
diff --git a/opengl/tools/glgen/src/JniCodeEmitter.java b/opengl/tools/glgen/src/JniCodeEmitter.java
index 9d8c5a0..9fa2b74 100644
--- a/opengl/tools/glgen/src/JniCodeEmitter.java
+++ b/opengl/tools/glgen/src/JniCodeEmitter.java
@@ -58,7 +58,7 @@
         } else if (baseType.equals("void")) {
             // nothing.
         } else {
-            throw new RuntimeException("Uknown primitive basetype " + baseType);
+            throw new RuntimeException("Unknown primitive basetype " + baseType);
         }
         return jniName;
     }
@@ -200,15 +200,9 @@
                 if (emitExceptionCheck) {
                     out.println(iii + indent + "_exception = 1;");
                 }
-                out.println(iii + indent +
-                            (mUseCPlusPlus ? "_env" : "(*_env)") +
-                            "->ThrowNew(" +
-                            (mUseCPlusPlus ? "" : "_env, ") +
-                            "IAEClass, " +
-                            "\"" +
-                            (isBuffer ?
-                             "remaining()" : "length - " + offset) +
-                            " < needed\");");
+                out.println(iii + indent + "jniThrowException(_env, " +
+                        "\"java/lang/IllegalArgumentException\", " +
+                        "\"" + (isBuffer ? "remaining()" : "length - " + offset) + " < needed\");");
                 out.println(iii + indent + "goto exit;");
                 needsExit = true;
                 out.println(iii + "}");
@@ -302,7 +296,7 @@
         }
         return false;
     }
-    
+
     String isRequiresFunc(CFunc cfunc) {
         String[] checks = mChecker.getChecks(cfunc.getName());
         int index = 1;
@@ -329,109 +323,94 @@
         }
         return null;
     }
-    
+
     void emitNativeBoundsChecks(CFunc cfunc, String cname, PrintStream out,
             boolean isBuffer, boolean emitExceptionCheck, String offset, String remaining, String iii) {
 
-                String[] checks = mChecker.getChecks(cfunc.getName());
+        String[] checks = mChecker.getChecks(cfunc.getName());
 
-                boolean lastWasIfcheck = false;
+        boolean lastWasIfcheck = false;
 
-                int index = 1;
-                if (checks != null) {
-                    while (index < checks.length) {
-                        if (checks[index].startsWith("check")) {
-                            if (lastWasIfcheck) {
-                                printIfcheckPostamble(out, isBuffer, emitExceptionCheck,
-                                                      offset, remaining, iii);
-                            }
-                            lastWasIfcheck = false;
-                            if (cname != null && !cname.equals(checks[index + 1])) {
-                                index += 3;
-                                continue;
-                            }
-                            out.println(iii + "if (" + remaining + " < " +
-                                        checks[index + 2] +
-                                        ") {");
-                            if (emitExceptionCheck) {
-                                out.println(iii + indent + "_exception = 1;");
-                            }
-                    String exceptionClassName = "IAEClass";
+        int index = 1;
+        if (checks != null) {
+            while (index < checks.length) {
+                if (checks[index].startsWith("check")) {
+                    if (lastWasIfcheck) {
+                        printIfcheckPostamble(out, isBuffer, emitExceptionCheck,
+                                offset, remaining, iii);
+                    }
+                    lastWasIfcheck = false;
+                    if (cname != null && !cname.equals(checks[index + 1])) {
+                        index += 3;
+                        continue;
+                    }
+                    out.println(iii + "if (" + remaining + " < " + checks[index + 2] + ") {");
+                    if (emitExceptionCheck) {
+                        out.println(iii + indent + "_exception = 1;");
+                    }
+                    String exceptionClassName = "java/lang/IllegalArgumentException";
                     // If the "check" keyword was of the form
                     // "check_<class name>", use the class name in the
                     // exception to be thrown
                     int underscore = checks[index].indexOf('_');
                     if (underscore >= 0) {
-                    exceptionClassName = checks[index].substring(underscore + 1) + "Class";
-                    }
-                            out.println(iii + indent +
-                                        (mUseCPlusPlus ? "_env" : "(*_env)") +
-                                        "->ThrowNew(" +
-                                        (mUseCPlusPlus ? "" : "_env, ") +
-                        exceptionClassName + ", " +
-                                        "\"" +
-                                        (isBuffer ?
-                                         "remaining()" : "length - " + offset) +
-                                        " < " + checks[index + 2] +
-                                        "\");");
-
-                            out.println(iii + indent + "goto exit;");
-                            needsExit = true;
-                            out.println(iii + "}");
-
-                            index += 3;
-                        } else if (checks[index].equals("ifcheck")) {
-                            String[] matches = checks[index + 4].split(",");
-
-                            if (!lastWasIfcheck) {
-                                out.println(iii + "int _needed;");
-                                out.println(iii +
-                                            "switch (" +
-                                            checks[index + 3] +
-                                            ") {");
-                            }
-
-                            for (int i = 0; i < matches.length; i++) {
-                                out.println("#if defined(" + matches[i] + ")");
-                                out.println(iii +
-                                            "    case " +
-                                            matches[i] +
-                                            ":");
-                                out.println("#endif // defined(" + matches[i] + ")");
-                            }
-                            out.println(iii +
-                                        "        _needed = " +
-                                        checks[index + 2] +
-                                        ";");
-                            out.println(iii +
-                                        "        break;");
-
-                            lastWasIfcheck = true;
-                            index += 5;
-                        } else if (checks[index].equals("return")) {
-                            // ignore
-                            index += 2;
-                        } else if (checks[index].equals("unsupported")) {
-                            // ignore
-                            index += 1;
-                        } else if (checks[index].equals("requires")) {
-                            // ignore
-                            index += 2;
-                        } else if (checks[index].equals("nullAllowed")) {
-                            // ignore
-                            index += 1;
+                        String abbr = checks[index].substring(underscore + 1);
+                        if (abbr.equals("AIOOBE")) {
+                            exceptionClassName = "java/lang/ArrayIndexOutOfBoundsException";
                         } else {
-                            System.out.println("Error: unknown keyword \"" +
-                                               checks[index] + "\"");
-                            System.exit(0);
+                            throw new RuntimeException("unknown exception abbreviation: " + abbr);
                         }
                     }
-                }
+                    out.println(iii + indent + "jniThrowException(_env, " +
+                            "\"" + exceptionClassName + "\", " +
+                            "\"" + (isBuffer ? "remaining()" : "length - " + offset) + " < " + checks[index + 2] + "\");");
 
-                if (lastWasIfcheck) {
-                    printIfcheckPostamble(out, isBuffer, emitExceptionCheck, iii);
+                    out.println(iii + indent + "goto exit;");
+                    needsExit = true;
+                    out.println(iii + "}");
+
+                    index += 3;
+                } else if (checks[index].equals("ifcheck")) {
+                    String[] matches = checks[index + 4].split(",");
+
+                    if (!lastWasIfcheck) {
+                        out.println(iii + "int _needed;");
+                        out.println(iii + "switch (" + checks[index + 3] + ") {");
+                    }
+
+                    for (int i = 0; i < matches.length; i++) {
+                        out.println("#if defined(" + matches[i] + ")");
+                        out.println(iii + "    case " + matches[i] + ":");
+                        out.println("#endif // defined(" + matches[i] + ")");
+                    }
+                    out.println(iii + "        _needed = " + checks[index + 2] + ";");
+                    out.println(iii + "        break;");
+
+                    lastWasIfcheck = true;
+                    index += 5;
+                } else if (checks[index].equals("return")) {
+                    // ignore
+                    index += 2;
+                } else if (checks[index].equals("unsupported")) {
+                    // ignore
+                    index += 1;
+                } else if (checks[index].equals("requires")) {
+                    // ignore
+                    index += 2;
+                } else if (checks[index].equals("nullAllowed")) {
+                    // ignore
+                    index += 1;
+                } else {
+                    System.out.println("Error: unknown keyword \"" + checks[index] + "\"");
+                    System.exit(0);
                 }
             }
+        }
+
+        if (lastWasIfcheck) {
+            printIfcheckPostamble(out, isBuffer, emitExceptionCheck, iii);
+        }
+    }
 
     boolean hasNonConstArg(JFunc jfunc, CFunc cfunc, List<Integer> nonPrimitiveArgs) {
         if (nonPrimitiveArgs.size() > 0) {
@@ -817,7 +796,7 @@
         boolean isUnsupported = isUnsupportedFunc(cfunc);
         if (isUnsupported) {
             out.println(indent +
-                        "_env->ThrowNew(UOEClass,");
+                        "jniThrowException(_env, \"java/lang/UnsupportedOperationException\",");
             out.println(indent +
                         "    \"" + cfunc.getName() + "\");");
             if (!isVoid) {
@@ -828,13 +807,13 @@
             out.println();
             return;
         }
-        
+
         String requiresExtension = isRequiresFunc(cfunc);
         if (requiresExtension != null) {
             out.println(indent +
                         "if (! supportsExtension(_env, _this, have_" + requiresExtension + "ID)) {");
             out.println(indent + indent +
-                        "_env->ThrowNew(UOEClass,");
+                        "jniThrowException(_env, \"java/lang/UnsupportedOperationException\",");
             out.println(indent + indent +
                         "    \"" + cfunc.getName() + "\");");
             if (isVoid) {
@@ -945,7 +924,8 @@
                 CType type = cfunc.getArgType(jfunc.getArgCIndex(idx));
                 String decl = type.getDeclaration();
                 out.println(indent + "if (!" + cname + ") {");
-                out.println(indent + "    _env->ThrowNew(IAEClass, \"" + cname + " == null\");");
+                out.println(indent + "    jniThrowException(_env, " +
+                        "\"java/lang/IllegalArgumentException\", \"" + cname + " == null\");");
                 out.println(indent + "    goto exit;");
                 needsExit = true;
                 out.println(indent + "}");
@@ -978,13 +958,9 @@
                     if (emitExceptionCheck) {
                         out.println(indent + indent + "_exception = 1;");
                     }
-                    out.println(indent + "    " +
-                                (mUseCPlusPlus ? "_env" : "(*_env)") +
-                                "->ThrowNew(" +
-                                (mUseCPlusPlus ? "" : "_env, ") +
-                                "IAEClass, " +
-                                "\"" + cname +
-                                " == null\");");
+                    out.println(indent + "    jniThrowException(_env, " +
+                            "\"java/lang/IllegalArgumentException\", " +
+                            "\"" + cname + " == null\");");
                     out.println(indent + "    goto exit;");
                     needsExit = true;
                     out.println(indent + "}");
@@ -993,12 +969,8 @@
                     if (emitExceptionCheck) {
                         out.println(indent + indent + "_exception = 1;");
                     }
-                    out.println(indent + "    " +
-                                (mUseCPlusPlus ? "_env" : "(*_env)") +
-                                "->ThrowNew(" +
-                                (mUseCPlusPlus ? "" : "_env, ") +
-                                "IAEClass, " +
-                                "\"" + offset + " < 0\");");
+                    out.println(indent + "    jniThrowException(_env, " +
+                            "\"java/lang/IllegalArgumentException\", \"" + offset + " < 0\");");
                     out.println(indent + "    goto exit;");
                     needsExit = true;
                     out.println(indent + "}");
diff --git a/opengl/tools/glgen/stubs/gles11/GLES10ExtcHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES10ExtcHeader.cpp
index 294d1ce..5d418d7 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES10ExtcHeader.cpp
+++ b/opengl/tools/glgen/stubs/gles11/GLES10ExtcHeader.cpp
@@ -1,21 +1,23 @@
 **
 ** 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 
+** 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 
+**     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 
+** 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.
 */
 
 // This source file is automatically generated
 
+#include "jni.h"
+#include "JNIHelp.h"
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/misc.h>
 
@@ -27,10 +29,6 @@
 
 static jclass nioAccessClass;
 static jclass bufferClass;
-static jclass OOMEClass;
-static jclass UOEClass;
-static jclass IAEClass;
-static jclass AIOOBEClass;
 static jmethodID getBasePointerID;
 static jmethodID getBaseArrayID;
 static jmethodID getBaseArrayOffsetID;
@@ -41,7 +39,7 @@
 /* Cache method IDs each time the class is loaded. */
 
 static void
-nativeClassInitBuffer(JNIEnv *_env)
+nativeClassInit(JNIEnv *_env, jclass glImplClass)
 {
     jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
     nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
@@ -63,26 +61,6 @@
 }
 
 
-static void
-nativeClassInit(JNIEnv *_env, jclass glImplClass)
-{
-    nativeClassInitBuffer(_env);
-
-    jclass IAEClassLocal =
-        _env->FindClass("java/lang/IllegalArgumentException");
-    jclass OOMEClassLocal =
-         _env->FindClass("java/lang/OutOfMemoryError");
-    jclass UOEClassLocal =
-         _env->FindClass("java/lang/UnsupportedOperationException");
-    jclass AIOOBEClassLocal =
-         _env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
-
-    IAEClass = (jclass) _env->NewGlobalRef(IAEClassLocal);
-    OOMEClass = (jclass) _env->NewGlobalRef(OOMEClassLocal);
-    UOEClass = (jclass) _env->NewGlobalRef(UOEClassLocal);
-    AIOOBEClass = (jclass) _env->NewGlobalRef(AIOOBEClassLocal);
-}
-
 static void *
 getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
 {
@@ -103,13 +81,13 @@
         *array = NULL;
         return (void *) (jint) pointer;
     }
-    
+
     *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
             getBaseArrayID, buffer);
     offset = _env->CallStaticIntMethod(nioAccessClass,
             getBaseArrayOffsetID, buffer);
     data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0);
-    
+
     return (void *) ((char *) data + offset);
 }
 
@@ -122,4 +100,3 @@
 }
 
 // --------------------------------------------------------------------------
-
diff --git a/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp
index e1c09f4..35a3c33 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp
+++ b/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp
@@ -1,21 +1,23 @@
 **
 ** 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 
+** 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 
+**     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 
+** 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.
 */
 
 // This source file is automatically generated
 
+#include "jni.h"
+#include "JNIHelp.h"
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/misc.h>
 
@@ -40,10 +42,6 @@
 
 static jclass nioAccessClass;
 static jclass bufferClass;
-static jclass OOMEClass;
-static jclass UOEClass;
-static jclass IAEClass;
-static jclass AIOOBEClass;
 static jmethodID getBasePointerID;
 static jmethodID getBaseArrayID;
 static jmethodID getBaseArrayOffsetID;
@@ -54,7 +52,7 @@
 /* Cache method IDs each time the class is loaded. */
 
 static void
-nativeClassInitBuffer(JNIEnv *_env)
+nativeClassInit(JNIEnv *_env, jclass glImplClass)
 {
     jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
     nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
@@ -75,26 +73,6 @@
         _env->GetFieldID(bufferClass, "_elementSizeShift", "I");
 }
 
-static void
-nativeClassInit(JNIEnv *_env, jclass glImplClass)
-{
-    nativeClassInitBuffer(_env);
-
-    jclass IAEClassLocal =
-        _env->FindClass("java/lang/IllegalArgumentException");
-    jclass OOMEClassLocal =
-         _env->FindClass("java/lang/OutOfMemoryError");
-    jclass UOEClassLocal =
-         _env->FindClass("java/lang/UnsupportedOperationException");
-    jclass AIOOBEClassLocal =
-         _env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
-
-    IAEClass = (jclass) _env->NewGlobalRef(IAEClassLocal);
-    OOMEClass = (jclass) _env->NewGlobalRef(OOMEClassLocal);
-    UOEClass = (jclass) _env->NewGlobalRef(UOEClassLocal);
-    AIOOBEClass = (jclass) _env->NewGlobalRef(AIOOBEClassLocal);
-}
-
 static void *
 getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
 {
@@ -115,13 +93,13 @@
         *array = NULL;
         return (void *) (jint) pointer;
     }
-    
+
     *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
             getBaseArrayID, buffer);
     offset = _env->CallStaticIntMethod(nioAccessClass,
             getBaseArrayOffsetID, buffer);
     data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0);
-    
+
     return (void *) ((char *) data + offset);
 }
 
@@ -140,7 +118,8 @@
         jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
         buf += position << elementSizeShift;
     } else {
-        _env->ThrowNew(IAEClass, "Must use a native order direct Buffer");
+        jniThrowException(_env, "java/lang/IllegalArgumentException",
+                          "Must use a native order direct Buffer");
     }
     return (void*) buf;
 }
@@ -153,4 +132,3 @@
 }
 
 // --------------------------------------------------------------------------
-
diff --git a/opengl/tools/glgen/stubs/gles11/GLES11ExtcHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES11ExtcHeader.cpp
index 2548b32..9b29a44 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES11ExtcHeader.cpp
+++ b/opengl/tools/glgen/stubs/gles11/GLES11ExtcHeader.cpp
@@ -1,21 +1,23 @@
 **
 ** 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 
+** 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 
+**     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 
+** 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.
 */
 
 // This source file is automatically generated
 
+#include "jni.h"
+#include "JNIHelp.h"
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/misc.h>
 
@@ -36,10 +38,6 @@
 
 static jclass nioAccessClass;
 static jclass bufferClass;
-static jclass OOMEClass;
-static jclass UOEClass;
-static jclass IAEClass;
-static jclass AIOOBEClass;
 static jmethodID getBasePointerID;
 static jmethodID getBaseArrayID;
 static jmethodID getBaseArrayOffsetID;
@@ -50,7 +48,7 @@
 /* Cache method IDs each time the class is loaded. */
 
 static void
-nativeClassInitBuffer(JNIEnv *_env)
+nativeClassInit(JNIEnv *_env, jclass glImplClass)
 {
     jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
     nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
@@ -72,26 +70,6 @@
 }
 
 
-static void
-nativeClassInit(JNIEnv *_env, jclass glImplClass)
-{
-    nativeClassInitBuffer(_env);
-
-    jclass IAEClassLocal =
-        _env->FindClass("java/lang/IllegalArgumentException");
-    jclass OOMEClassLocal =
-         _env->FindClass("java/lang/OutOfMemoryError");
-    jclass UOEClassLocal =
-         _env->FindClass("java/lang/UnsupportedOperationException");
-    jclass AIOOBEClassLocal =
-         _env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
-
-    IAEClass = (jclass) _env->NewGlobalRef(IAEClassLocal);
-    OOMEClass = (jclass) _env->NewGlobalRef(OOMEClassLocal);
-    UOEClass = (jclass) _env->NewGlobalRef(UOEClassLocal);
-    AIOOBEClass = (jclass) _env->NewGlobalRef(AIOOBEClassLocal);
-}
-
 static void *
 getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
 {
@@ -112,13 +90,13 @@
         *array = NULL;
         return (void *) (jint) pointer;
     }
-    
+
     *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
             getBaseArrayID, buffer);
     offset = _env->CallStaticIntMethod(nioAccessClass,
             getBaseArrayOffsetID, buffer);
     data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0);
-    
+
     return (void *) ((char *) data + offset);
 }
 
@@ -138,9 +116,9 @@
         jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
         buf += position << elementSizeShift;
     } else {
-        _env->ThrowNew(IAEClass, "Must use a native order direct Buffer");
+        jniThrowException(_env, "java/lang/IllegalArgumentException",
+                          "Must use a native order direct Buffer");
     }
     return (void*) buf;
 }
 // --------------------------------------------------------------------------
-
diff --git a/opengl/tools/glgen/stubs/gles11/GLES11cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES11cHeader.cpp
index 4c297f7..823079f 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES11cHeader.cpp
+++ b/opengl/tools/glgen/stubs/gles11/GLES11cHeader.cpp
@@ -1,21 +1,23 @@
 **
 ** 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 
+** 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 
+**     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 
+** 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.
 */
 
 // This source file is automatically generated
 
+#include "jni.h"
+#include "JNIHelp.h"
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/misc.h>
 
@@ -34,10 +36,6 @@
 
 static jclass nioAccessClass;
 static jclass bufferClass;
-static jclass OOMEClass;
-static jclass UOEClass;
-static jclass IAEClass;
-static jclass AIOOBEClass;
 static jmethodID getBasePointerID;
 static jmethodID getBaseArrayID;
 static jmethodID getBaseArrayOffsetID;
@@ -48,7 +46,7 @@
 /* Cache method IDs each time the class is loaded. */
 
 static void
-nativeClassInitBuffer(JNIEnv *_env)
+nativeClassInit(JNIEnv *_env, jclass glImplClass)
 {
     jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
     nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
@@ -70,26 +68,6 @@
 }
 
 
-static void
-nativeClassInit(JNIEnv *_env, jclass glImplClass)
-{
-    nativeClassInitBuffer(_env);
-
-    jclass IAEClassLocal =
-        _env->FindClass("java/lang/IllegalArgumentException");
-    jclass OOMEClassLocal =
-         _env->FindClass("java/lang/OutOfMemoryError");
-    jclass UOEClassLocal =
-         _env->FindClass("java/lang/UnsupportedOperationException");
-    jclass AIOOBEClassLocal =
-         _env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
-
-    IAEClass = (jclass) _env->NewGlobalRef(IAEClassLocal);
-    OOMEClass = (jclass) _env->NewGlobalRef(OOMEClassLocal);
-    UOEClass = (jclass) _env->NewGlobalRef(UOEClassLocal);
-    AIOOBEClass = (jclass) _env->NewGlobalRef(AIOOBEClassLocal);
-}
-
 static void *
 getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
 {
@@ -110,13 +88,13 @@
         *array = NULL;
         return (void *) (jint) pointer;
     }
-    
+
     *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
             getBaseArrayID, buffer);
     offset = _env->CallStaticIntMethod(nioAccessClass,
             getBaseArrayOffsetID, buffer);
     data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0);
-    
+
     return (void *) ((char *) data + offset);
 }
 
@@ -136,10 +114,10 @@
         jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
         buf += position << elementSizeShift;
     } else {
-        _env->ThrowNew(IAEClass, "Must use a native order direct Buffer");
+        jniThrowException(_env, "java/lang/IllegalArgumentException",
+                          "Must use a native order direct Buffer");
     }
     return (void*) buf;
 }
 
 // --------------------------------------------------------------------------
-
diff --git a/opengl/tools/glgen/stubs/gles11/GLES20cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES20cHeader.cpp
index e451e9a..13a2577 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES20cHeader.cpp
+++ b/opengl/tools/glgen/stubs/gles11/GLES20cHeader.cpp
@@ -1,21 +1,23 @@
 **
 ** 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 
+** 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 
+**     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 
+** 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.
 */
 
 // This source file is automatically generated
 
+#include "jni.h"
+#include "JNIHelp.h"
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/misc.h>
 
@@ -27,10 +29,6 @@
 
 static jclass nioAccessClass;
 static jclass bufferClass;
-static jclass OOMEClass;
-static jclass UOEClass;
-static jclass IAEClass;
-static jclass AIOOBEClass;
 static jmethodID getBasePointerID;
 static jmethodID getBaseArrayID;
 static jmethodID getBaseArrayOffsetID;
@@ -41,7 +39,7 @@
 /* Cache method IDs each time the class is loaded. */
 
 static void
-nativeClassInitBuffer(JNIEnv *_env)
+nativeClassInit(JNIEnv *_env, jclass glImplClass)
 {
     jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
     nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
@@ -63,26 +61,6 @@
 }
 
 
-static void
-nativeClassInit(JNIEnv *_env, jclass glImplClass)
-{
-    nativeClassInitBuffer(_env);
-
-    jclass IAEClassLocal =
-        _env->FindClass("java/lang/IllegalArgumentException");
-    jclass OOMEClassLocal =
-         _env->FindClass("java/lang/OutOfMemoryError");
-    jclass UOEClassLocal =
-         _env->FindClass("java/lang/UnsupportedOperationException");
-    jclass AIOOBEClassLocal =
-         _env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
-
-    IAEClass = (jclass) _env->NewGlobalRef(IAEClassLocal);
-    OOMEClass = (jclass) _env->NewGlobalRef(OOMEClassLocal);
-    UOEClass = (jclass) _env->NewGlobalRef(UOEClassLocal);
-    AIOOBEClass = (jclass) _env->NewGlobalRef(AIOOBEClassLocal);
-}
-
 static void *
 getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
 {
@@ -103,13 +81,13 @@
         *array = NULL;
         return (void *) (jint) pointer;
     }
-    
+
     *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
             getBaseArrayID, buffer);
     offset = _env->CallStaticIntMethod(nioAccessClass,
             getBaseArrayOffsetID, buffer);
     data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0);
-    
+
     return (void *) ((char *) data + offset);
 }
 
@@ -129,7 +107,8 @@
         jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
         buf += position << elementSizeShift;
     } else {
-        _env->ThrowNew(IAEClass, "Must use a native order direct Buffer");
+        jniThrowException(_env, "java/lang/IllegalArgumentException",
+                          "Must use a native order direct Buffer");
     }
     return (void*) buf;
 }
@@ -147,4 +126,3 @@
 }
 
 // --------------------------------------------------------------------------
-
diff --git a/opengl/tools/glgen/stubs/gles11/glGetProgramInfoLog.cpp b/opengl/tools/glgen/stubs/gles11/glGetProgramInfoLog.cpp
index d92f515..ce6ab24 100644
--- a/opengl/tools/glgen/stubs/gles11/glGetProgramInfoLog.cpp
+++ b/opengl/tools/glgen/stubs/gles11/glGetProgramInfoLog.cpp
@@ -1,27 +1,19 @@
-#include <string.h>
+#include <stdlib.h>
 
 /* void glGetProgramInfoLog ( GLuint shader, GLsizei maxLength, GLsizei* length, GLchar* infoLog ) */
-static
-jstring
-android_glGetProgramInfoLog (JNIEnv *_env, jobject _this, jint shader) {
+static jstring android_glGetProgramInfoLog(JNIEnv *_env, jobject, jint shader) {
     GLint infoLen = 0;
-    jstring _result = 0;
-    char* buf = 0;
     glGetProgramiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
-    if (infoLen) {
-        char* buf = (char*) malloc(infoLen);
-        if (buf == 0) {
-            _env->ThrowNew(IAEClass, "out of memory");
-            goto exit;
-        }
-        glGetProgramInfoLog(shader, infoLen, NULL, buf);
-        _result = _env->NewStringUTF(buf);
-    } else {
-        _result = _env->NewStringUTF("");
+    if (!infoLen) {
+        return _env->NewStringUTF("");
     }
-exit:
-    if (buf) {
-            free(buf);
+    char* buf = (char*) malloc(infoLen);
+    if (buf == NULL) {
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "out of memory");
+        return NULL;
     }
-    return _result;
-}
\ No newline at end of file
+    glGetProgramInfoLog(shader, infoLen, NULL, buf);
+    jstring result = _env->NewStringUTF(buf);
+    free(buf);
+    return result;
+}
diff --git a/opengl/tools/glgen/stubs/gles11/glGetShaderInfoLog.cpp b/opengl/tools/glgen/stubs/gles11/glGetShaderInfoLog.cpp
index 5441d66..dd656b6 100644
--- a/opengl/tools/glgen/stubs/gles11/glGetShaderInfoLog.cpp
+++ b/opengl/tools/glgen/stubs/gles11/glGetShaderInfoLog.cpp
@@ -1,27 +1,19 @@
-#include <string.h>
+#include <stdlib.h>
 
 /* void glGetShaderInfoLog ( GLuint shader, GLsizei maxLength, GLsizei* length, GLchar* infoLog ) */
-static
-jstring
-android_glGetShaderInfoLog (JNIEnv *_env, jobject _this, jint shader) {
+static jstring android_glGetShaderInfoLog(JNIEnv *_env, jobject, jint shader) {
     GLint infoLen = 0;
-    jstring _result = 0;
-    char* buf = 0;
     glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
-    if (infoLen) {
-        char* buf = (char*) malloc(infoLen);
-        if (buf == 0) {
-            _env->ThrowNew(IAEClass, "out of memory");
-            goto exit;
-        }
-        glGetShaderInfoLog(shader, infoLen, NULL, buf);
-        _result = _env->NewStringUTF(buf);
-    } else {
-        _result = _env->NewStringUTF("");
+    if (!infoLen) {
+        return _env->NewStringUTF("");
     }
-exit:
-    if (buf) {
-            free(buf);
+    char* buf = (char*) malloc(infoLen);
+    if (buf == NULL) {
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "out of memory");
+        return NULL;
     }
-    return _result;
-}
\ No newline at end of file
+    glGetShaderInfoLog(shader, infoLen, NULL, buf);
+    jstring result = _env->NewStringUTF(buf);
+    free(buf);
+    return result;
+}
diff --git a/opengl/tools/glgen/stubs/gles11/glGetString.cpp b/opengl/tools/glgen/stubs/gles11/glGetString.cpp
index a400859..239fe4a 100644
--- a/opengl/tools/glgen/stubs/gles11/glGetString.cpp
+++ b/opengl/tools/glgen/stubs/gles11/glGetString.cpp
@@ -1,11 +1,5 @@
-#include <string.h>

-

 /* const GLubyte * glGetString ( GLenum name ) */

-static

-jstring

-android_glGetString

-  (JNIEnv *_env, jobject _this, jint name) {

-    const char * chars = (const char *)glGetString((GLenum)name);

-    jstring output = _env->NewStringUTF(chars);

-    return output;

+static jstring android_glGetString(JNIEnv* _env, jobject, jint name) {

+    const char* chars = (const char*) glGetString((GLenum) name);

+    return _env->NewStringUTF(chars);

 }

diff --git a/opengl/tools/glgen/stubs/gles11/glShaderSource.cpp b/opengl/tools/glgen/stubs/gles11/glShaderSource.cpp
index c274108..125fd0f 100644
--- a/opengl/tools/glgen/stubs/gles11/glShaderSource.cpp
+++ b/opengl/tools/glgen/stubs/gles11/glShaderSource.cpp
@@ -6,7 +6,7 @@
     (JNIEnv *_env, jobject _this, jint shader, jstring string) {
 
     if (!string) {
-        _env->ThrowNew(IAEClass, "string == null");
+        jniThrowException(_env, "java/lang/IllegalArgumentException", "string == null");
         return;
     }
 
diff --git a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
index c2464b0..f7315ee 100644
--- a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
+++ b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
@@ -1,21 +1,23 @@
 **
 ** Copyright 2006, 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 
+** 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 
+**     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 
+** 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.
 */
 
 // This source file is automatically generated
 
+#include "jni.h"
+#include "JNIHelp.h"
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/misc.h>
 
@@ -63,10 +65,6 @@
 
 static jclass nioAccessClass;
 static jclass bufferClass;
-static jclass OOMEClass;
-static jclass UOEClass;
-static jclass IAEClass;
-static jclass AIOOBEClass;
 static jclass G11ImplClass;
 static jmethodID getBasePointerID;
 static jmethodID getBaseArrayID;
@@ -84,7 +82,7 @@
 /* Cache method IDs each time the class is loaded. */
 
 static void
-nativeClassInitBuffer(JNIEnv *_env)
+nativeClassInit(JNIEnv *_env, jclass glImplClass)
 {
     jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
     nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
@@ -114,26 +112,6 @@
         _env->GetFieldID(bufferClass, "_elementSizeShift", "I");
 }
 
-static void
-nativeClassInit(JNIEnv *_env, jclass glImplClass)
-{
-    nativeClassInitBuffer(_env);
-
-    jclass IAEClassLocal =
-        _env->FindClass("java/lang/IllegalArgumentException");
-    jclass OOMEClassLocal =
-         _env->FindClass("java/lang/OutOfMemoryError");
-    jclass UOEClassLocal =
-         _env->FindClass("java/lang/UnsupportedOperationException");
-    jclass AIOOBEClassLocal =
-         _env->FindClass("java/lang/ArrayIndexOutOfBoundsException");
-
-    IAEClass = (jclass) _env->NewGlobalRef(IAEClassLocal);
-    OOMEClass = (jclass) _env->NewGlobalRef(OOMEClassLocal);
-    UOEClass = (jclass) _env->NewGlobalRef(UOEClassLocal);
-    AIOOBEClass = (jclass) _env->NewGlobalRef(AIOOBEClassLocal);
-}
-
 static void *
 getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining)
 {
@@ -154,7 +132,7 @@
         *array = NULL;
         return (void *) (jint) pointer;
     }
-    
+
     *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
             getBaseArrayID, buffer);
     if (*array == NULL) {
@@ -163,7 +141,7 @@
     offset = _env->CallStaticIntMethod(nioAccessClass,
             getBaseArrayOffsetID, buffer);
     data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0);
-    
+
     return (void *) ((char *) data + offset);
 }
 
@@ -207,7 +185,8 @@
                 releasePointer(_env, array, buf, 0);
             }
         } else {
-            _env->ThrowNew(IAEClass, "Must use a native order direct Buffer");
+            jniThrowException(_env, "java/lang/IllegalArgumentException",
+                              "Must use a native order direct Buffer");
         }
     }
     return buf;
@@ -250,7 +229,7 @@
         }
     }
 }
-    
+
 static bool
 checkForExtension(const GLubyte* pExtensions, const GLubyte* pExtension) {
     for (;*pExtensions != '\0'; pExtensions = nextExtension(pExtensions)) {
@@ -279,4 +258,3 @@
 }
 
 // --------------------------------------------------------------------------
-
diff --git a/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl b/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl
index 3727106..cd730aa 100644
--- a/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl
+++ b/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl
@@ -18,7 +18,7 @@
 
 package com.google.android.gles_jni;
 
-import android.app.ActivityThread;
+import android.app.AppGlobals;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.os.Build;
@@ -64,7 +64,7 @@
     private static boolean allowIndirectBuffers(String appName) {
         boolean result = false;
         int version = 0;
-        IPackageManager pm = ActivityThread.getPackageManager();
+        IPackageManager pm = AppGlobals.getPackageManager();
         try {
             ApplicationInfo applicationInfo = pm.getApplicationInfo(appName, 0);
             if (applicationInfo != null) {
diff --git a/opengl/tools/glgen/stubs/jsr239/glGetString.cpp b/opengl/tools/glgen/stubs/jsr239/glGetString.cpp
index a400859..cd6e3f3 100644
--- a/opengl/tools/glgen/stubs/jsr239/glGetString.cpp
+++ b/opengl/tools/glgen/stubs/jsr239/glGetString.cpp
@@ -1,11 +1,5 @@
-#include <string.h>

-

 /* const GLubyte * glGetString ( GLenum name ) */

-static

-jstring

-android_glGetString

-  (JNIEnv *_env, jobject _this, jint name) {

-    const char * chars = (const char *)glGetString((GLenum)name);

-    jstring output = _env->NewStringUTF(chars);

-    return output;

+static jstring android_glGetString(JNIEnv *_env, jobject, jint name) {

+    const char* chars = (const char*) glGetString((GLenum) name);

+    return _env->NewStringUTF(chars);

 }

diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
index 8c6eefb..7d4629e 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
@@ -410,7 +410,7 @@
 
     void updateRunningTasks() {
         mRunningTaskList = mActivityManager.getRunningTasks(MAX_TASKS,
-                ActivityManager.TASKS_GET_THUMBNAILS, mThumbnailReceiver);
+                0, mThumbnailReceiver);
         if (DBG) Log.v(TAG, "Portrait: " + mPortraitMode);
         for (RunningTaskInfo r : mRunningTaskList) {
             if (r.thumbnail != null) {
@@ -441,8 +441,7 @@
         final ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
 
         final List<ActivityManager.RecentTaskInfo> recentTasks =
-                am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE
-                        | ActivityManager.TASKS_GET_THUMBNAILS);
+                am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
 
         ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
                     .resolveActivityInfo(pm, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
index a39bef8..cfb4975 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
@@ -429,8 +429,7 @@
                 mContext.getSystemService(Context.ACTIVITY_SERVICE);
 
         final List<ActivityManager.RecentTaskInfo> recentTasks =
-                am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE
-                        | ActivityManager.TASKS_GET_THUMBNAILS);
+                am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
 
         ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
                     .resolveActivityInfo(pm, 0);
diff --git a/packages/TtsService/Android.mk b/packages/TtsService/Android.mk
deleted file mode 100644
index a1a3b9f..0000000
--- a/packages/TtsService/Android.mk
+++ /dev/null
@@ -1,15 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files) \
-
-LOCAL_PACKAGE_NAME := TtsService
-LOCAL_CERTIFICATE := platform
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/TtsService/AndroidManifest.xml b/packages/TtsService/AndroidManifest.xml
deleted file mode 100755
index 46e0ad1..0000000
--- a/packages/TtsService/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.tts">
-    <application android:label="TTS Service"
-        android:icon="@drawable/ic_launcher_text_to_speech">
-        <service android:enabled="true"
-                 android:name=".TtsService"
-                 android:label="TTS Service">
-            <intent-filter>
-                <action android:name="android.intent.action.START_TTS_SERVICE"/>
-                <category android:name="android.intent.category.TTS"/>
-            </intent-filter>
-        </service>
-    </application>
-    <uses-permission android:name="android.permission.INTERNET"/>
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-</manifest>
diff --git a/packages/TtsService/MODULE_LICENSE_APACHE2 b/packages/TtsService/MODULE_LICENSE_APACHE2
deleted file mode 100644
index e69de29..0000000
--- a/packages/TtsService/MODULE_LICENSE_APACHE2
+++ /dev/null
diff --git a/packages/TtsService/NOTICE b/packages/TtsService/NOTICE
deleted file mode 100644
index 64aaa8d..0000000
--- a/packages/TtsService/NOTICE
+++ /dev/null
@@ -1,190 +0,0 @@
-
-   Copyright (c) 2009, The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
diff --git a/packages/TtsService/jni/Android.mk b/packages/TtsService/jni/Android.mk
deleted file mode 100755
index 5dc0c30..0000000
--- a/packages/TtsService/jni/Android.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-	android_tts_SynthProxy.cpp
-
-LOCAL_C_INCLUDES += \
-	frameworks/base/native/include \
-	$(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
-
-include $(BUILD_SHARED_LIBRARY)
-
diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp
deleted file mode 100644
index e00fa85..0000000
--- a/packages/TtsService/jni/android_tts_SynthProxy.cpp
+++ /dev/null
@@ -1,1071 +0,0 @@
-/*
- * Copyright (C) 2009-2010 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 "SynthProxyJNI"
-
-#include <utils/Log.h>
-#include <nativehelper/jni.h>
-#include <nativehelper/JNIHelp.h>
-#include <android_runtime/AndroidRuntime.h>
-#include <android/tts.h>
-#include <media/AudioTrack.h>
-#include <math.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  2048
-#define DEFAULT_TTS_STREAM_TYPE AudioSystem::MUSIC
-#define DEFAULT_VOLUME          1.0f
-
-// EQ + BOOST parameters
-#define FILTER_LOWSHELF_ATTENUATION -18.0f // in dB
-#define FILTER_TRANSITION_FREQ 1100.0f     // in Hz
-#define FILTER_SHELF_SLOPE 1.0f            // Q
-#define FILTER_GAIN 5.5f // linear gain
-
-#define USAGEMODE_PLAY_IMMEDIATELY 0
-#define USAGEMODE_WRITE_TO_FILE    1
-
-#define SYNTHPLAYSTATE_IS_STOPPED 0
-#define SYNTHPLAYSTATE_IS_PLAYING 1
-
-using namespace android;
-
-// ----------------------------------------------------------------------------
-struct fields_t {
-    jfieldID    synthProxyFieldJniData;
-    jmethodID   synthProxyMethodPost;
-};
-
-// structure to hold the data that is used each time the TTS engine has synthesized more data
-struct afterSynthData_t {
-    jint jniStorage;
-    int  usageMode;
-    FILE* outputFile;
-    AudioSystem::stream_type streamType;
-};
-
-// ----------------------------------------------------------------------------
-// EQ data
-double amp;
-double w;
-double sinw;
-double cosw;
-double beta;
-double a0, a1, a2, b0, b1, b2;
-double m_fa, m_fb, m_fc, m_fd, m_fe;
-double x0;  // x[n]
-double x1;  // x[n-1]
-double x2;  // x[n-2]
-double out0;// y[n]
-double out1;// y[n-1]
-double out2;// y[n-2]
-
-static float fFilterLowshelfAttenuation = FILTER_LOWSHELF_ATTENUATION;
-static float fFilterTransitionFreq = FILTER_TRANSITION_FREQ;
-static float fFilterShelfSlope = FILTER_SHELF_SLOPE;
-static float fFilterGain = FILTER_GAIN;
-static bool  bUseFilter = false;
-
-void initializeEQ() {
-
-    amp = float(pow(10.0, fFilterLowshelfAttenuation / 40.0));
-    w = 2.0 * M_PI * (fFilterTransitionFreq / DEFAULT_TTS_RATE);
-    sinw = float(sin(w));
-    cosw = float(cos(w));
-    beta = float(sqrt(amp)/fFilterShelfSlope);
-
-    // initialize low-shelf parameters
-    b0 = amp * ((amp+1.0F) - ((amp-1.0F)*cosw) + (beta*sinw));
-    b1 = 2.0F * amp * ((amp-1.0F) - ((amp+1.0F)*cosw));
-    b2 = amp * ((amp+1.0F) - ((amp-1.0F)*cosw) - (beta*sinw));
-    a0 = (amp+1.0F) + ((amp-1.0F)*cosw) + (beta*sinw);
-    a1 = 2.0F * ((amp-1.0F) + ((amp+1.0F)*cosw));
-    a2 = -((amp+1.0F) + ((amp-1.0F)*cosw) - (beta*sinw));
-
-    m_fa = fFilterGain * b0/a0;
-    m_fb = fFilterGain * b1/a0;
-    m_fc = fFilterGain * b2/a0;
-    m_fd = a1/a0;
-    m_fe = a2/a0;
-}
-
-void initializeFilter() {
-    x0 = 0.0f;
-    x1 = 0.0f;
-    x2 = 0.0f;
-    out0 = 0.0f;
-    out1 = 0.0f;
-    out2 = 0.0f;
-}
-
-void applyFilter(int16_t* buffer, size_t sampleCount) {
-
-    for (size_t i=0 ; i<sampleCount ; i++) {
-
-        x0 = (double) buffer[i];
-
-        out0 = (m_fa*x0) + (m_fb*x1) + (m_fc*x2) + (m_fd*out1) + (m_fe*out2);
-
-        x2 = x1;
-        x1 = x0;
-
-        out2 = out1;
-        out1 = out0;
-
-        if (out0 > 32767.0f) {
-            buffer[i] = 32767;
-        } else if (out0 < -32768.0f) {
-            buffer[i] = -32768;
-        } else {
-            buffer[i] = (int16_t) out0;
-        }
-    }
-}
-
-
-// ----------------------------------------------------------------------------
-static fields_t javaTTSFields;
-
-// TODO move to synth member once we have multiple simultaneous engines running
-static Mutex engineMutex;
-
-// ----------------------------------------------------------------------------
-class SynthProxyJniStorage {
-    public :
-        jobject                   tts_ref;
-        android_tts_engine_t*     mEngine;
-        void*                     mEngineLibHandle;
-        AudioTrack*               mAudioOut;
-        int8_t                    mPlayState;
-        Mutex                     mPlayLock;
-        AudioSystem::stream_type  mStreamType;
-        uint32_t                  mSampleRate;
-        uint32_t                  mAudFormat;
-        int                       mNbChannels;
-        int8_t *                  mBuffer;
-        size_t                    mBufferSize;
-        float                     mVolume[2];
-
-        SynthProxyJniStorage() {
-            tts_ref = NULL;
-            mEngine = NULL;
-            mEngineLibHandle = NULL;
-            mAudioOut = NULL;
-            mPlayState =  SYNTHPLAYSTATE_IS_STOPPED;
-            mStreamType = DEFAULT_TTS_STREAM_TYPE;
-            mSampleRate = DEFAULT_TTS_RATE;
-            mAudFormat  = DEFAULT_TTS_FORMAT;
-            mNbChannels = DEFAULT_TTS_NB_CHANNELS;
-            mBufferSize = DEFAULT_TTS_BUFFERSIZE;
-            mBuffer = new int8_t[mBufferSize];
-            memset(mBuffer, 0, mBufferSize);
-            mVolume[AudioTrack::LEFT] = DEFAULT_VOLUME;
-            mVolume[AudioTrack::RIGHT] = DEFAULT_VOLUME;
-        }
-
-        ~SynthProxyJniStorage() {
-            //LOGV("entering ~SynthProxyJniStorage()");
-            killAudio();
-            if (mEngine) {
-                mEngine->funcs->shutdown(mEngine);
-                mEngine = NULL;
-            }
-            if (mEngineLibHandle) {
-                //LOGV("~SynthProxyJniStorage(): before close library");
-                int res = dlclose(mEngineLibHandle);
-                LOGE_IF( res != 0, "~SynthProxyJniStorage(): dlclose returned %d", res);
-            }
-            delete mBuffer;
-        }
-
-        void killAudio() {
-            if (mAudioOut) {
-                mAudioOut->stop();
-                delete mAudioOut;
-                mAudioOut = NULL;
-            }
-        }
-
-        void createAudioOut(AudioSystem::stream_type streamType, uint32_t rate,
-                AudioSystem::audio_format format, int channel) {
-            mSampleRate = rate;
-            mAudFormat  = format;
-            mNbChannels = channel;
-            mStreamType = streamType;
-
-            // retrieve system properties to ensure successful creation of the
-            // AudioTrack object for playback
-            int afSampleRate;
-            if (AudioSystem::getOutputSamplingRate(&afSampleRate, mStreamType) != NO_ERROR) {
-                afSampleRate = 44100;
-            }
-            int afFrameCount;
-            if (AudioSystem::getOutputFrameCount(&afFrameCount, mStreamType) != NO_ERROR) {
-                afFrameCount = 2048;
-            }
-            uint32_t afLatency;
-            if (AudioSystem::getOutputLatency(&afLatency, mStreamType) != NO_ERROR) {
-                afLatency = 500;
-            }
-            uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate);
-            if (minBufCount < 2) minBufCount = 2;
-            int minFrameCount = (afFrameCount * rate * minBufCount)/afSampleRate;
-
-            mPlayLock.lock();
-            mAudioOut = new AudioTrack(mStreamType, rate, format,
-                    (channel == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO,
-                    minFrameCount > 4096 ? minFrameCount : 4096,
-                    0, 0, 0, 0); // not using an AudioTrack callback
-
-            if (mAudioOut->initCheck() != NO_ERROR) {
-              LOGE("createAudioOut(): AudioTrack error");
-              delete mAudioOut;
-              mAudioOut = NULL;
-            } else {
-              //LOGI("AudioTrack OK");
-              mAudioOut->setVolume(mVolume[AudioTrack::LEFT], mVolume[AudioTrack::RIGHT]);
-              LOGV("AudioTrack ready");
-            }
-            mPlayLock.unlock();
-        }
-};
-
-
-// ----------------------------------------------------------------------------
-void prepAudioTrack(SynthProxyJniStorage* pJniData, AudioSystem::stream_type streamType,
-        uint32_t rate, AudioSystem::audio_format format, int channel) {
-    // Don't bother creating a new audiotrack object if the current
-    // object is already initialized with the same audio parameters.
-    if ( pJniData->mAudioOut &&
-         (rate == pJniData->mSampleRate) &&
-         (format == pJniData->mAudFormat) &&
-         (channel == pJniData->mNbChannels) &&
-         (streamType == pJniData->mStreamType) ){
-        return;
-    }
-    if (pJniData->mAudioOut){
-        pJniData->killAudio();
-    }
-    pJniData->createAudioOut(streamType, rate, format, channel);
-}
-
-
-// ----------------------------------------------------------------------------
-/*
- * Callback from TTS engine.
- * Directly speaks using AudioTrack or write to file
- */
-extern "C" android_tts_callback_status_t
-__ttsSynthDoneCB(void ** pUserdata, uint32_t rate,
-               android_tts_audio_format_t format, int channel,
-               int8_t **pWav, size_t *pBufferSize,
-               android_tts_synth_status_t status)
-{
-    //LOGV("ttsSynthDoneCallback: %d bytes", bufferSize);
-    AudioSystem::audio_format  encoding;
-
-    if (*pUserdata == NULL){
-        LOGE("userdata == NULL");
-        return ANDROID_TTS_CALLBACK_HALT;
-    }
-    switch (format) {
-    case ANDROID_TTS_AUDIO_FORMAT_PCM_8_BIT:
-        encoding = AudioSystem::PCM_8_BIT;
-        break;
-    case ANDROID_TTS_AUDIO_FORMAT_PCM_16_BIT:
-        encoding = AudioSystem::PCM_16_BIT;
-        break;
-    default:
-        LOGE("Can't play, bad format");
-        return ANDROID_TTS_CALLBACK_HALT;
-    }
-    afterSynthData_t* pForAfter = (afterSynthData_t*) *pUserdata;
-    SynthProxyJniStorage* pJniData = (SynthProxyJniStorage*)(pForAfter->jniStorage);
-
-    if (pForAfter->usageMode == USAGEMODE_PLAY_IMMEDIATELY){
-        //LOGV("Direct speech");
-
-        if (*pWav == NULL) {
-            delete pForAfter;
-            pForAfter = NULL;
-            LOGV("Null: speech has completed");
-            return ANDROID_TTS_CALLBACK_HALT;
-        }
-
-        if (*pBufferSize > 0) {
-            prepAudioTrack(pJniData, pForAfter->streamType, rate, encoding, channel);
-            if (pJniData->mAudioOut) {
-                pJniData->mPlayLock.lock();
-                if(pJniData->mAudioOut->stopped()
-                        && (pJniData->mPlayState == SYNTHPLAYSTATE_IS_PLAYING)) {
-                    pJniData->mAudioOut->start();
-                }
-                pJniData->mPlayLock.unlock();
-                if (bUseFilter) {
-                    applyFilter((int16_t*)*pWav, *pBufferSize/2);
-                }
-                pJniData->mAudioOut->write(*pWav, *pBufferSize);
-                memset(*pWav, 0, *pBufferSize);
-                //LOGV("AudioTrack wrote: %d bytes", bufferSize);
-            } else {
-                LOGE("Can't play, null audiotrack");
-                delete pForAfter;
-                pForAfter = NULL;
-                return ANDROID_TTS_CALLBACK_HALT;
-            }
-        }
-    } else  if (pForAfter->usageMode == USAGEMODE_WRITE_TO_FILE) {
-        //LOGV("Save to file");
-        if (*pWav == NULL) {
-            delete pForAfter;
-            LOGV("Null: speech has completed");
-            return ANDROID_TTS_CALLBACK_HALT;
-        }
-        if (*pBufferSize > 0){
-            if (bUseFilter) {
-                applyFilter((int16_t*)*pWav, *pBufferSize/2);
-            }
-            fwrite(*pWav, 1, *pBufferSize, pForAfter->outputFile);
-            memset(*pWav, 0, *pBufferSize);
-        }
-    }
-    // Future update:
-    //      For sync points in the speech, call back into the SynthProxy class through the
-    //      javaTTSFields.synthProxyMethodPost methode to notify
-    //      playback has completed if the synthesis is done or if a marker has been reached.
-
-    if (status == ANDROID_TTS_SYNTH_DONE) {
-        // this struct was allocated in the original android_tts_SynthProxy_speak call,
-        // all processing matching this call is now done.
-        LOGV("Speech synthesis done.");
-        if (pForAfter->usageMode == USAGEMODE_PLAY_IMMEDIATELY) {
-            // only delete for direct playback. When writing to a file, we still have work to do
-            // in android_tts_SynthProxy_synthesizeToFile. The struct will be deleted there.
-            delete pForAfter;
-            pForAfter = NULL;
-        }
-        return ANDROID_TTS_CALLBACK_HALT;
-    }
-
-    // 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.
-    *pBufferSize = pJniData->mBufferSize;
-
-    return ANDROID_TTS_CALLBACK_CONTINUE;
-}
-
-
-// ----------------------------------------------------------------------------
-static int
-android_tts_SynthProxy_setLowShelf(JNIEnv *env, jobject thiz, jboolean applyFilter,
-        jfloat filterGain, jfloat attenuationInDb, jfloat freqInHz, jfloat slope)
-{
-    int result = ANDROID_TTS_SUCCESS;
-
-    bUseFilter = applyFilter;
-    if (applyFilter) {
-        fFilterLowshelfAttenuation = attenuationInDb;
-        fFilterTransitionFreq = freqInHz;
-        fFilterShelfSlope = slope;
-        fFilterGain = filterGain;
-
-        if (fFilterShelfSlope != 0.0f) {
-            initializeEQ();
-        } else {
-            LOGE("Invalid slope, can't be null");
-            result = ANDROID_TTS_FAILURE;
-        }
-    }
-
-    return result;
-}
-
-// ----------------------------------------------------------------------------
-static int
-android_tts_SynthProxy_native_setup(JNIEnv *env, jobject thiz,
-        jobject weak_this, jstring nativeSoLib, jstring engConfig)
-{
-    int result = ANDROID_TTS_FAILURE;
-
-    bUseFilter = false;
-
-    SynthProxyJniStorage* pJniStorage = new SynthProxyJniStorage();
-
-    prepAudioTrack(pJniStorage,
-            DEFAULT_TTS_STREAM_TYPE, DEFAULT_TTS_RATE, DEFAULT_TTS_FORMAT, DEFAULT_TTS_NB_CHANNELS);
-
-    const char *nativeSoLibNativeString =  env->GetStringUTFChars(nativeSoLib, 0);
-    const char *engConfigString = env->GetStringUTFChars(engConfig, 0);
-
-    void *engine_lib_handle = dlopen(nativeSoLibNativeString,
-            RTLD_NOW | RTLD_LOCAL);
-    if (engine_lib_handle == NULL) {
-       LOGE("android_tts_SynthProxy_native_setup(): engine_lib_handle == NULL");
-    } else {
-        android_tts_engine_t * (*get_TtsEngine)() =
-            reinterpret_cast<android_tts_engine_t* (*)()>(dlsym(engine_lib_handle, "android_getTtsEngine"));
-
-        // Support obsolete/legacy binary modules
-        if (get_TtsEngine == NULL) {
-            get_TtsEngine =
-                reinterpret_cast<android_tts_engine_t* (*)()>(dlsym(engine_lib_handle, "getTtsEngine"));
-        }
-
-        pJniStorage->mEngine = (*get_TtsEngine)();
-        pJniStorage->mEngineLibHandle = engine_lib_handle;
-
-        android_tts_engine_t *engine = pJniStorage->mEngine;
-        if (engine) {
-            Mutex::Autolock l(engineMutex);
-            engine->funcs->init(
-                engine,
-                __ttsSynthDoneCB,
-                engConfigString);
-        }
-
-        result = ANDROID_TTS_SUCCESS;
-    }
-
-    // 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);
-    env->ReleaseStringUTFChars(engConfig, engConfigString);
-
-    return result;
-}
-
-
-static void
-android_tts_SynthProxy_native_finalize(JNIEnv *env, jobject thiz, jint jniData)
-{
-    //LOGV("entering android_tts_SynthProxy_finalize()");
-    if (jniData == 0) {
-        //LOGE("android_tts_SynthProxy_native_finalize(): invalid JNI data");
-        return;
-    }
-
-    Mutex::Autolock l(engineMutex);
-
-    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-    env->DeleteGlobalRef(pSynthData->tts_ref);
-    delete pSynthData;
-
-    env->SetIntField(thiz, javaTTSFields.synthProxyFieldJniData, 0);
-}
-
-
-static void
-android_tts_SynthProxy_shutdown(JNIEnv *env, jobject thiz, jint jniData)
-{
-    //LOGV("entering android_tts_SynthProxy_shutdown()");
-
-    // do everything a call to finalize would
-    android_tts_SynthProxy_native_finalize(env, thiz, jniData);
-}
-
-
-static int
-android_tts_SynthProxy_isLanguageAvailable(JNIEnv *env, jobject thiz, jint jniData,
-        jstring language, jstring country, jstring variant)
-{
-    int result = ANDROID_TTS_LANG_NOT_SUPPORTED;
-
-    if (jniData == 0) {
-        LOGE("android_tts_SynthProxy_isLanguageAvailable(): invalid JNI data");
-        return result;
-    }
-
-    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-    const char *langNativeString = env->GetStringUTFChars(language, 0);
-    const char *countryNativeString = env->GetStringUTFChars(country, 0);
-    const char *variantNativeString = env->GetStringUTFChars(variant, 0);
-
-    android_tts_engine_t *engine = pSynthData->mEngine;
-
-    if (engine) {
-        result = engine->funcs->isLanguageAvailable(engine,langNativeString,
-                countryNativeString, variantNativeString);
-    }
-    env->ReleaseStringUTFChars(language, langNativeString);
-    env->ReleaseStringUTFChars(country, countryNativeString);
-    env->ReleaseStringUTFChars(variant, variantNativeString);
-    return result;
-}
-
-static int
-android_tts_SynthProxy_setConfig(JNIEnv *env, jobject thiz, jint jniData, jstring engineConfig)
-{
-    int result = ANDROID_TTS_FAILURE;
-
-    if (jniData == 0) {
-        LOGE("android_tts_SynthProxy_setConfig(): invalid JNI data");
-        return result;
-    }
-
-    Mutex::Autolock l(engineMutex);
-
-    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-    const char *engineConfigNativeString = env->GetStringUTFChars(engineConfig, 0);
-    android_tts_engine_t *engine = pSynthData->mEngine;
-
-    if (engine) {
-        result = engine->funcs->setProperty(engine,ANDROID_TTS_ENGINE_PROPERTY_CONFIG,
-                engineConfigNativeString, strlen(engineConfigNativeString));
-    }
-    env->ReleaseStringUTFChars(engineConfig, engineConfigNativeString);
-
-    return result;
-}
-
-static int
-android_tts_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jint jniData,
-        jstring language, jstring country, jstring variant)
-{
-    int result = ANDROID_TTS_LANG_NOT_SUPPORTED;
-
-    if (jniData == 0) {
-        LOGE("android_tts_SynthProxy_setLanguage(): invalid JNI data");
-        return result;
-    }
-
-    Mutex::Autolock l(engineMutex);
-
-    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-    const char *langNativeString = env->GetStringUTFChars(language, 0);
-    const char *countryNativeString = env->GetStringUTFChars(country, 0);
-    const char *variantNativeString = env->GetStringUTFChars(variant, 0);
-    android_tts_engine_t *engine = pSynthData->mEngine;
-
-    if (engine) {
-        result = engine->funcs->setLanguage(engine, langNativeString,
-                countryNativeString, variantNativeString);
-    }
-    env->ReleaseStringUTFChars(language, langNativeString);
-    env->ReleaseStringUTFChars(country, countryNativeString);
-    env->ReleaseStringUTFChars(variant, variantNativeString);
-    return result;
-}
-
-
-static int
-android_tts_SynthProxy_loadLanguage(JNIEnv *env, jobject thiz, jint jniData,
-        jstring language, jstring country, jstring variant)
-{
-    int result = ANDROID_TTS_LANG_NOT_SUPPORTED;
-
-    if (jniData == 0) {
-        LOGE("android_tts_SynthProxy_loadLanguage(): invalid JNI data");
-        return result;
-    }
-
-    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-    const char *langNativeString = env->GetStringUTFChars(language, 0);
-    const char *countryNativeString = env->GetStringUTFChars(country, 0);
-    const char *variantNativeString = env->GetStringUTFChars(variant, 0);
-    android_tts_engine_t *engine = pSynthData->mEngine;
-
-    if (engine) {
-        result = engine->funcs->loadLanguage(engine, langNativeString,
-                countryNativeString, variantNativeString);
-    }
-    env->ReleaseStringUTFChars(language, langNativeString);
-    env->ReleaseStringUTFChars(country, countryNativeString);
-    env->ReleaseStringUTFChars(variant, variantNativeString);
-
-    return result;
-}
-
-
-static int
-android_tts_SynthProxy_setSpeechRate(JNIEnv *env, jobject thiz, jint jniData,
-        jint speechRate)
-{
-    int result = ANDROID_TTS_FAILURE;
-
-    if (jniData == 0) {
-        LOGE("android_tts_SynthProxy_setSpeechRate(): invalid JNI data");
-        return result;
-    }
-
-    int bufSize = 12;
-    char buffer [bufSize];
-    sprintf(buffer, "%d", speechRate);
-
-    Mutex::Autolock l(engineMutex);
-
-    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-    //LOGI("setting speech rate to %d", speechRate);
-    android_tts_engine_t *engine = pSynthData->mEngine;
-
-    if (engine) {
-        result = engine->funcs->setProperty(engine, "rate", buffer, bufSize);
-    }
-
-    return result;
-}
-
-
-static int
-android_tts_SynthProxy_setPitch(JNIEnv *env, jobject thiz, jint jniData,
-        jint pitch)
-{
-    int result = ANDROID_TTS_FAILURE;
-
-    if (jniData == 0) {
-        LOGE("android_tts_SynthProxy_setPitch(): invalid JNI data");
-        return result;
-    }
-
-    Mutex::Autolock l(engineMutex);
-
-    int bufSize = 12;
-    char buffer [bufSize];
-    sprintf(buffer, "%d", pitch);
-
-    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-    //LOGI("setting pitch to %d", pitch);
-    android_tts_engine_t *engine = pSynthData->mEngine;
-
-    if (engine) {
-        result = engine->funcs->setProperty(engine, "pitch", buffer, bufSize);
-    }
-
-    return result;
-}
-
-
-static int
-android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData,
-        jstring textJavaString, jstring filenameJavaString)
-{
-    int result = ANDROID_TTS_FAILURE;
-
-    if (jniData == 0) {
-        LOGE("android_tts_SynthProxy_synthesizeToFile(): invalid JNI data");
-        return result;
-    }
-
-    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-    if (!pSynthData->mEngine) {
-        LOGE("android_tts_SynthProxy_synthesizeToFile(): invalid engine handle");
-        return result;
-    }
-
-    initializeFilter();
-
-    Mutex::Autolock l(engineMutex);
-
-    // Retrieve audio parameters before writing the file header
-    AudioSystem::audio_format encoding;
-    uint32_t rate = DEFAULT_TTS_RATE;
-    int channels = DEFAULT_TTS_NB_CHANNELS;
-    android_tts_engine_t *engine = pSynthData->mEngine;
-    android_tts_audio_format_t  format = ANDROID_TTS_AUDIO_FORMAT_DEFAULT;
-
-    engine->funcs->setAudioFormat(engine, &format, &rate, &channels);
-
-    switch (format) {
-    case ANDROID_TTS_AUDIO_FORMAT_PCM_16_BIT:
-        encoding = AudioSystem::PCM_16_BIT;
-        break;
-    case ANDROID_TTS_AUDIO_FORMAT_PCM_8_BIT:
-        encoding = AudioSystem::PCM_8_BIT;
-        break;
-    default:
-        LOGE("android_tts_SynthProxy_synthesizeToFile(): engine uses invalid format");
-        return result;
-    }
-
-    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");
-
-    if (pForAfter->outputFile == NULL) {
-        LOGE("android_tts_SynthProxy_synthesizeToFile(): error creating output file");
-        delete pForAfter;
-        return result;
-    }
-
-    // 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;
-
-    memset(pSynthData->mBuffer, 0, pSynthData->mBufferSize);
-
-    result = engine->funcs->synthesizeText(engine, 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
-
-    int sampleSizeInByte = (encoding == AudioSystem::PCM_16_BIT ? 2 : 1);
-
-    ((unsigned short *)(&header[20]))[0] = 1;  // format
-    ((unsigned short *)(&header[22]))[0] = channels;  // channels
-    ((uint32_t *)(&header[24]))[0] = rate;  // samplerate
-    ((uint32_t *)(&header[28]))[0] = rate * sampleSizeInByte * channels;// byterate
-    ((unsigned short *)(&header[32]))[0] = sampleSizeInByte * channels;  // block align
-    ((unsigned short *)(&header[34]))[0] = sampleSizeInByte * 8;  // 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);
-
-    delete pForAfter;
-    pForAfter = NULL;
-
-    env->ReleaseStringUTFChars(textJavaString, textNativeString);
-    env->ReleaseStringUTFChars(filenameJavaString, filenameNativeString);
-
-    return result;
-}
-
-
-static int
-android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData,
-        jstring textJavaString, jint javaStreamType, jfloat volume, jfloat pan)
-{
-    int result = ANDROID_TTS_FAILURE;
-
-    if (jniData == 0) {
-        LOGE("android_tts_SynthProxy_speak(): invalid JNI data");
-        return result;
-    }
-
-    initializeFilter();
-
-    Mutex::Autolock l(engineMutex);
-
-    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-
-    {//scope for lock on mPlayLock
-        Mutex::Autolock _l(pSynthData->mPlayLock);
-
-        pSynthData->mPlayState = SYNTHPLAYSTATE_IS_PLAYING;
-
-        // clip volume and pan
-        float vol = (volume > 1.0f) ? 1.0f : (volume < 0.0f) ? 0.0f : volume;
-        float panning = (pan > 1.0f) ? 1.0f : (pan < -1.0f) ? -1.0f : pan;
-        // compute playback volume based on volume and pan, using balance rule, in order to avoid
-        // lowering volume when panning in center
-        pSynthData->mVolume[AudioTrack::LEFT] = vol;
-        pSynthData->mVolume[AudioTrack::RIGHT] = vol;
-        if (panning > 0.0f) {
-            pSynthData->mVolume[AudioTrack::LEFT] *= (1.0f - panning);
-        } else if (panning < 0.0f) {
-            pSynthData->mVolume[AudioTrack::RIGHT] *= (1.0f + panning);
-        }
-
-        // apply the volume if there is an output
-        if (NULL != pSynthData->mAudioOut) {
-            pSynthData->mAudioOut->setVolume(pSynthData->mVolume[AudioTrack::LEFT],
-                    pSynthData->mVolume[AudioTrack::RIGHT]);
-        }
-
-        //LOGV("android_tts_SynthProxy_speak() vol=%.3f pan=%.3f, mVolume=[%.1f %.1f]",
-        //        volume, pan,
-        //        pSynthData->mVolume[AudioTrack::LEFT], pSynthData->mVolume[AudioTrack::RIGHT]);
-    }
-
-    afterSynthData_t* pForAfter = new (afterSynthData_t);
-    pForAfter->jniStorage = jniData;
-    pForAfter->usageMode  = USAGEMODE_PLAY_IMMEDIATELY;
-    pForAfter->streamType = (AudioSystem::stream_type) javaStreamType;
-
-    if (pSynthData->mEngine) {
-        const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
-        memset(pSynthData->mBuffer, 0, pSynthData->mBufferSize);
-        android_tts_engine_t *engine = pSynthData->mEngine;
-
-        result = engine->funcs->synthesizeText(engine, textNativeString,
-                pSynthData->mBuffer, pSynthData->mBufferSize, (void *)pForAfter);
-        env->ReleaseStringUTFChars(textJavaString, textNativeString);
-    }
-
-    return result;
-}
-
-
-static int
-android_tts_SynthProxy_stop(JNIEnv *env, jobject thiz, jint jniData)
-{
-    int result = ANDROID_TTS_FAILURE;
-
-    if (jniData == 0) {
-        LOGE("android_tts_SynthProxy_stop(): invalid JNI data");
-        return result;
-    }
-
-    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-
-    pSynthData->mPlayLock.lock();
-    pSynthData->mPlayState = SYNTHPLAYSTATE_IS_STOPPED;
-    if (pSynthData->mAudioOut) {
-        pSynthData->mAudioOut->stop();
-    }
-    pSynthData->mPlayLock.unlock();
-
-    android_tts_engine_t *engine = pSynthData->mEngine;
-    if (engine) {
-        result = engine->funcs->stop(engine);
-    }
-
-    return result;
-}
-
-
-static int
-android_tts_SynthProxy_stopSync(JNIEnv *env, jobject thiz, jint jniData)
-{
-    int result = ANDROID_TTS_FAILURE;
-
-    if (jniData == 0) {
-        LOGE("android_tts_SynthProxy_stop(): invalid JNI data");
-        return result;
-    }
-
-    // perform a regular stop
-    result = android_tts_SynthProxy_stop(env, thiz, jniData);
-    // but wait on the engine having released the engine mutex which protects
-    // the synthesizer resources.
-    engineMutex.lock();
-    engineMutex.unlock();
-
-    return result;
-}
-
-
-static jobjectArray
-android_tts_SynthProxy_getLanguage(JNIEnv *env, jobject thiz, jint jniData)
-{
-    if (jniData == 0) {
-        LOGE("android_tts_SynthProxy_getLanguage(): invalid JNI data");
-        return NULL;
-    }
-
-    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-
-    if (pSynthData->mEngine) {
-        size_t bufSize = 100;
-        char lang[bufSize];
-        char country[bufSize];
-        char variant[bufSize];
-        memset(lang, 0, bufSize);
-        memset(country, 0, bufSize);
-        memset(variant, 0, bufSize);
-        jobjectArray retLocale = (jobjectArray)env->NewObjectArray(3,
-                env->FindClass("java/lang/String"), env->NewStringUTF(""));
-
-        android_tts_engine_t *engine = pSynthData->mEngine;
-        engine->funcs->getLanguage(engine, lang, country, variant);
-        env->SetObjectArrayElement(retLocale, 0, env->NewStringUTF(lang));
-        env->SetObjectArrayElement(retLocale, 1, env->NewStringUTF(country));
-        env->SetObjectArrayElement(retLocale, 2, env->NewStringUTF(variant));
-        return retLocale;
-    } else {
-        return NULL;
-    }
-}
-
-
-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
-    android_tts_engine_t *engine = pSynthData->mEngine;
-    if (engine) {
-        engine->funcs->getProperty(engine,"rate", buf, &bufSize);
-    }
-    return atoi(buf);
-}
-
-// Dalvik VM type signatures
-static JNINativeMethod gMethods[] = {
-    {   "native_stop",
-        "(I)I",
-        (void*)android_tts_SynthProxy_stop
-    },
-    {   "native_stopSync",
-        "(I)I",
-        (void*)android_tts_SynthProxy_stopSync
-    },
-    {   "native_speak",
-        "(ILjava/lang/String;IFF)I",
-        (void*)android_tts_SynthProxy_speak
-    },
-    {   "native_synthesizeToFile",
-        "(ILjava/lang/String;Ljava/lang/String;)I",
-        (void*)android_tts_SynthProxy_synthesizeToFile
-    },
-    {   "native_isLanguageAvailable",
-        "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
-        (void*)android_tts_SynthProxy_isLanguageAvailable
-    },
-    {   "native_setConfig",
-            "(ILjava/lang/String;)I",
-            (void*)android_tts_SynthProxy_setConfig
-    },
-    {   "native_setLanguage",
-        "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
-        (void*)android_tts_SynthProxy_setLanguage
-    },
-    {   "native_loadLanguage",
-        "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
-        (void*)android_tts_SynthProxy_loadLanguage
-    },
-    {   "native_setSpeechRate",
-        "(II)I",
-        (void*)android_tts_SynthProxy_setSpeechRate
-    },
-    {   "native_setPitch",
-        "(II)I",
-        (void*)android_tts_SynthProxy_setPitch
-    },
-    {   "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;Ljava/lang/String;)I",
-        (void*)android_tts_SynthProxy_native_setup
-    },
-    {   "native_setLowShelf",
-        "(ZFFFF)I",
-        (void*)android_tts_SynthProxy_setLowShelf
-    },
-    {   "native_finalize",
-        "(I)V",
-        (void*)android_tts_SynthProxy_native_finalize
-    }
-};
-
-#define SP_JNIDATA_FIELD_NAME                "mJniData"
-#define SP_POSTSPEECHSYNTHESIZED_METHOD_NAME "postNativeSpeechSynthesizedInJava"
-
-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.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/packages/TtsService/proguard.flags b/packages/TtsService/proguard.flags
deleted file mode 100644
index e8bee6b..0000000
--- a/packages/TtsService/proguard.flags
+++ /dev/null
@@ -1,5 +0,0 @@
--keep class android.tts.SynthProxy {
-  int mJniData;
-  # keep all declarations for native methods
-  <methods>;
-}
diff --git a/packages/TtsService/res/drawable-hdpi/ic_launcher_text_to_speech.png b/packages/TtsService/res/drawable-hdpi/ic_launcher_text_to_speech.png
deleted file mode 100644
index f075e0f..0000000
--- a/packages/TtsService/res/drawable-hdpi/ic_launcher_text_to_speech.png
+++ /dev/null
Binary files differ
diff --git a/packages/TtsService/res/drawable-mdpi/ic_launcher_text_to_speech.png b/packages/TtsService/res/drawable-mdpi/ic_launcher_text_to_speech.png
deleted file mode 100644
index cbae7de..0000000
--- a/packages/TtsService/res/drawable-mdpi/ic_launcher_text_to_speech.png
+++ /dev/null
Binary files differ
diff --git a/packages/TtsService/src/android/tts/SynthProxy.java b/packages/TtsService/src/android/tts/SynthProxy.java
deleted file mode 100755
index f5f5fcf..0000000
--- a/packages/TtsService/src/android/tts/SynthProxy.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * 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.media.AudioManager;
-import android.media.AudioSystem;
-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 {
-
-    // Default parameters of a filter to be applied when using the Pico engine.
-    // Such a huge filter gain is justified by how much energy in the low frequencies is "wasted" at
-    // the output of the synthesis. The low shelving filter removes it, leaving room for
-    // amplification.
-    private final static float PICO_FILTER_GAIN = 5.0f; // linear gain
-    private final static float PICO_FILTER_LOWSHELF_ATTENUATION = -18.0f; // in dB
-    private final static float PICO_FILTER_TRANSITION_FREQ = 1100.0f;     // in Hz
-    private final static float PICO_FILTER_SHELF_SLOPE = 1.0f;            // Q
-
-    //
-    // External API
-    //
-
-    /**
-     * Constructor; pass the location of the native TTS .so to use.
-     */
-    public SynthProxy(String nativeSoLib, String engineConfig) {
-        boolean applyFilter = nativeSoLib.toLowerCase().contains("pico");
-        Log.v(TtsService.SERVICE_TAG, "About to load "+ nativeSoLib + ", applyFilter="+applyFilter);
-        native_setup(new WeakReference<SynthProxy>(this), nativeSoLib, engineConfig);
-        native_setLowShelf(applyFilter, PICO_FILTER_GAIN, PICO_FILTER_LOWSHELF_ATTENUATION,
-                PICO_FILTER_TRANSITION_FREQ, PICO_FILTER_SHELF_SLOPE);
-    }
-
-    /**
-     * Stops and clears the AudioTrack.
-     */
-    public int stop() {
-        return native_stop(mJniData);
-    }
-
-    /**
-     * Synchronous stop of the synthesizer. This method returns when the synth
-     * has completed the stop procedure and doesn't use any of the resources it
-     * was using while synthesizing.
-     *
-     * @return {@link android.speech.tts.TextToSpeech.SUCCESS} or
-     *         {@link android.speech.tts.TextToSpeech.ERROR}
-     */
-    public int stopSync() {
-        return native_stopSync(mJniData);
-    }
-
-    /**
-     * Synthesize speech and speak it directly using AudioTrack.
-     */
-    public int speak(String text, int streamType, float volume, float pan) {
-        Log.i(TAG, "speak() on stream "+ streamType);
-        if ((streamType > -1) && (streamType < AudioSystem.getNumStreamTypes())) {
-            return native_speak(mJniData, text, streamType, volume, pan);
-        } else {
-            Log.e("SynthProxy", "Trying to speak with invalid stream type " + streamType);
-            return native_speak(mJniData, text, AudioManager.STREAM_MUSIC, volume, pan);
-        }
-    }
-
-    /**
-     * 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 int synthesizeToFile(String text, String filename) {
-        Log.i(TAG, "synthesizeToFile() to file "+ filename);
-        return native_synthesizeToFile(mJniData, text, filename);
-    }
-
-    /**
-     * Queries for language support.
-     * Return codes are defined in android.speech.tts.TextToSpeech
-     */
-    public int isLanguageAvailable(String language, String country, String variant) {
-        return native_isLanguageAvailable(mJniData, language, country, variant);
-    }
-
-    /**
-     * Updates the engine configuration.
-     */
-    public int setConfig(String engineConfig) {
-        return native_setConfig(mJniData, engineConfig);
-    }
-
-    /**
-     * Sets the language.
-     */
-    public int setLanguage(String language, String country, String variant) {
-        return native_setLanguage(mJniData, language, country, variant);
-    }
-
-    /**
-     * Loads the language: it's not set, but prepared for use later.
-     */
-    public int loadLanguage(String language, String country, String variant) {
-        return native_loadLanguage(mJniData, language, country, variant);
-    }
-
-    /**
-     * Sets the speech rate.
-     */
-    public final int setSpeechRate(int speechRate) {
-        return native_setSpeechRate(mJniData, speechRate);
-    }
-
-    /**
-     * Sets the pitch of the synthesized voice.
-     */
-    public final int setPitch(int pitch) {
-        return native_setPitch(mJniData, pitch);
-    }
-
-    /**
-     * Returns the currently set language, country and variant information.
-     */
-    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 int native_setup(Object weak_this, String nativeSoLib,
-            String engineConfig);
-
-    private native final int native_setLowShelf(boolean applyFilter, float filterGain,
-            float attenuationInDb, float freqInHz, float slope);
-
-    private native final void native_finalize(int jniData);
-
-    private native final int native_stop(int jniData);
-
-    private native final int native_stopSync(int jniData);
-
-    private native final int native_speak(int jniData, String text, int streamType, float volume,
-            float pan);
-
-    private native final int native_synthesizeToFile(int jniData, String text, String filename);
-
-    private native final int  native_isLanguageAvailable(int jniData, String language,
-            String country, String variant);
-
-    private native final int native_setLanguage(int jniData, String language, String country,
-            String variant);
-
-    private native final int native_loadLanguage(int jniData, String language, String country,
-            String variant);
-
-    private native final int native_setConfig(int jniData, String engineConfig);
-
-    private native final int native_setSpeechRate(int jniData, int speechRate);
-
-    private native final int native_setPitch(int jniData, int speechRate);
-
-    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/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
deleted file mode 100755
index c562327..0000000
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ /dev/null
@@ -1,1503 +0,0 @@
-/*
- * 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.app.Service;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.database.Cursor;
-import android.media.AudioManager;
-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.speech.tts.ITts.Stub;
-import android.speech.tts.ITtsCallback;
-import android.speech.tts.TextToSpeech;
-import android.util.Log;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.concurrent.TimeUnit;
-
-
-/**
- * @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 TEXT = 0;
-        public static final int EARCON = 1;
-        public static final int SILENCE = 2;
-        public static final int TEXT_TO_FILE = 3;
-        public String mText = "";
-        public ArrayList<String> mParams = null;
-        public int mType = TEXT;
-        public long mDuration = 0;
-        public String mFilename = null;
-        public String mCallingApp = "";
-
-        public SpeechItem(String source, String text, ArrayList<String> params, int itemType) {
-            mText = text;
-            mParams = params;
-            mType = itemType;
-            mCallingApp = source;
-        }
-
-        public SpeechItem(String source, long silenceTime, ArrayList<String> params) {
-            mDuration = silenceTime;
-            mParams = params;
-            mType = SILENCE;
-            mCallingApp = source;
-        }
-
-        public SpeechItem(String source, String text, ArrayList<String> params,
-                int itemType, String filename) {
-            mText = text;
-            mParams = params;
-            mType = itemType;
-            mFilename = filename;
-            mCallingApp = source;
-        }
-
-    }
-
-    /**
-     * 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;
-        }
-    }
-    // If the speech queue is locked for more than 5 seconds, something has gone
-    // very wrong with processSpeechQueue.
-    private static final int SPEECHQUEUELOCK_TIMEOUT = 5000;
-    private static final int MAX_SPEECH_ITEM_CHAR_LENGTH = 4000;
-    private static final int MAX_FILENAME_LENGTH = 250;
-    private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_MUSIC;
-    // TODO use TextToSpeech.DEFAULT_SYNTH once it is unhidden
-    private static final String DEFAULT_SYNTH = "com.svox.pico";
-    private static final String ACTION = "android.intent.action.START_TTS_SERVICE";
-    private static final String CATEGORY = "android.intent.category.TTS";
-    private static final String PKGNAME = "android.tts";
-    protected static final String SERVICE_TAG = "TtsService";
-
-    private final RemoteCallbackList<ITtsCallback> mCallbacks
-            = new RemoteCallbackList<ITtsCallback>();
-
-    private HashMap<String, ITtsCallback> mCallbacksMap;
-
-    private Boolean mIsSpeaking;
-    private Boolean mSynthBusy;
-    private ArrayList<SpeechItem> mSpeechQueue;
-    private HashMap<String, SoundResource> mEarcons;
-    private HashMap<String, SoundResource> mUtterances;
-    private MediaPlayer mPlayer;
-    private SpeechItem mCurrentSpeechItem;
-    private HashMap<SpeechItem, Boolean> mKillList; // Used to ensure that in-flight synth calls
-                                                    // are killed when stop is used.
-    private TtsService mSelf;
-
-    private ContentResolver mResolver;
-
-    // lock for the speech queue (mSpeechQueue) and the current speech item (mCurrentSpeechItem)
-    private final ReentrantLock speechQueueLock = new ReentrantLock();
-    private final ReentrantLock synthesizerLock = new ReentrantLock();
-
-    private static SynthProxy sNativeSynth = null;
-    private String currentSpeechEngineSOFile = "";
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        Log.v("TtsService", "TtsService.onCreate()");
-
-        mResolver = getContentResolver();
-
-        currentSpeechEngineSOFile = "";
-        setEngine(getDefaultEngine());
-
-        mSelf = this;
-        mIsSpeaking = false;
-        mSynthBusy = false;
-
-        mEarcons = new HashMap<String, SoundResource>();
-        mUtterances = new HashMap<String, SoundResource>();
-        mCallbacksMap = new HashMap<String, android.speech.tts.ITtsCallback>();
-
-        mSpeechQueue = new ArrayList<SpeechItem>();
-        mPlayer = null;
-        mCurrentSpeechItem = null;
-        mKillList = new HashMap<SpeechItem, Boolean>();
-
-        setDefaultSettings();
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-
-        killAllUtterances();
-
-        // Don't hog the media player
-        cleanUpPlayer();
-
-        if (sNativeSynth != null) {
-            sNativeSynth.shutdown();
-        }
-        sNativeSynth = null;
-
-        // Unregister all callbacks.
-        mCallbacks.kill();
-
-        Log.v(SERVICE_TAG, "onDestroy() completed");
-    }
-
-
-    private int setEngine(String enginePackageName) {
-        String soFilename = "";
-        if (isDefaultEnforced()) {
-            enginePackageName = getDefaultEngine();
-        }
-
-        // Make sure that the engine has been allowed by the user
-        if (!enginePackageName.equals(DEFAULT_SYNTH)) {
-            String[] enabledEngines = android.provider.Settings.Secure.getString(mResolver,
-                    android.provider.Settings.Secure.TTS_ENABLED_PLUGINS).split(" ");
-            boolean isEnabled = false;
-            for (int i=0; i<enabledEngines.length; i++) {
-                if (enabledEngines[i].equals(enginePackageName)) {
-                    isEnabled = true;
-                    break;
-                }
-            }
-            if (!isEnabled) {
-                // Do not use an engine that the user has not enabled; fall back
-                // to using the default synthesizer.
-                enginePackageName = DEFAULT_SYNTH;
-            }
-        }
-
-        // The SVOX TTS is an exception to how the TTS packaging scheme works
-        // because it is part of the system and not a 3rd party add-on; thus
-        // its binary is actually located under /system/lib/
-        if (enginePackageName.equals(DEFAULT_SYNTH)) {
-            soFilename = "/system/lib/libttspico.so";
-        } else {
-            // Find the package
-            Intent intent = new Intent("android.intent.action.START_TTS_ENGINE");
-            intent.setPackage(enginePackageName);
-            ResolveInfo[] enginesArray = new ResolveInfo[0];
-            PackageManager pm = getPackageManager();
-            List <ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
-            if ((resolveInfos == null) || resolveInfos.isEmpty()) {
-                Log.e(SERVICE_TAG, "Invalid TTS Engine Package: " + enginePackageName);
-                return TextToSpeech.ERROR;
-            }
-            enginesArray = resolveInfos.toArray(enginesArray);
-            // Generate the TTS .so filename from the package
-            ActivityInfo aInfo = enginesArray[0].activityInfo;
-            soFilename = aInfo.name.replace(aInfo.packageName + ".", "") + ".so";
-            soFilename = soFilename.toLowerCase();
-            soFilename = "/data/data/" + aInfo.packageName + "/lib/libtts" + soFilename;
-        }
-
-        if (currentSpeechEngineSOFile.equals(soFilename)) {
-            return TextToSpeech.SUCCESS;
-        }
-
-        File f = new File(soFilename);
-        if (!f.exists()) {
-            Log.e(SERVICE_TAG, "Invalid TTS Binary: " + soFilename);
-            return TextToSpeech.ERROR;
-        }
-
-        if (sNativeSynth != null) {
-            sNativeSynth.stopSync();
-            sNativeSynth.shutdown();
-            sNativeSynth = null;
-        }
-
-        // Load the engineConfig from the plugin if it has any special configuration
-        // to be loaded. By convention, if an engine wants the TTS framework to pass
-        // in any configuration, it must put it into its content provider which has the URI:
-        // content://<packageName>.providers.SettingsProvider
-        // That content provider must provide a Cursor which returns the String that
-        // is to be passed back to the native .so file for the plugin when getString(0) is
-        // called on it.
-        // Note that the TTS framework does not care what this String data is: it is something
-        // that comes from the engine plugin and is consumed only by the engine plugin itself.
-        String engineConfig = "";
-        Cursor c = getContentResolver().query(Uri.parse("content://" + enginePackageName
-                + ".providers.SettingsProvider"), null, null, null, null);
-        if (c != null){
-            c.moveToFirst();
-            engineConfig = c.getString(0);
-            c.close();
-        }
-        sNativeSynth = new SynthProxy(soFilename, engineConfig);
-        currentSpeechEngineSOFile = soFilename;
-        return TextToSpeech.SUCCESS;
-    }
-
-
-
-    private void setDefaultSettings() {
-        setLanguage("", this.getDefaultLanguage(), getDefaultCountry(), getDefaultLocVariant());
-
-        // speech rate
-        setSpeechRate("", getDefaultRate());
-    }
-
-
-    private boolean isDefaultEnforced() {
-        return (android.provider.Settings.Secure.getInt(mResolver,
-                    android.provider.Settings.Secure.TTS_USE_DEFAULTS,
-                    TextToSpeech.Engine.USE_DEFAULTS)
-                == 1 );
-    }
-
-    private String getDefaultEngine() {
-        String defaultEngine = android.provider.Settings.Secure.getString(mResolver,
-                android.provider.Settings.Secure.TTS_DEFAULT_SYNTH);
-        if (defaultEngine == null) {
-            return TextToSpeech.Engine.DEFAULT_SYNTH;
-        } else {
-            return defaultEngine;
-        }
-    }
-
-    private int getDefaultRate() {
-        return android.provider.Settings.Secure.getInt(mResolver,
-                android.provider.Settings.Secure.TTS_DEFAULT_RATE,
-                TextToSpeech.Engine.DEFAULT_RATE);
-    }
-
-    private int getDefaultPitch() {
-        // Pitch is not user settable; the default pitch is always 100.
-        return 100;
-    }
-
-    private String getDefaultLanguage() {
-        String defaultLang = android.provider.Settings.Secure.getString(mResolver,
-                android.provider.Settings.Secure.TTS_DEFAULT_LANG);
-        if (defaultLang == null) {
-            // no setting found, use the current Locale to determine the default language
-            return Locale.getDefault().getISO3Language();
-        } else {
-            return defaultLang;
-        }
-    }
-
-
-    private String getDefaultCountry() {
-        String defaultCountry = android.provider.Settings.Secure.getString(mResolver,
-                android.provider.Settings.Secure.TTS_DEFAULT_COUNTRY);
-        if (defaultCountry == null) {
-            // no setting found, use the current Locale to determine the default country
-            return Locale.getDefault().getISO3Country();
-        } else {
-            return defaultCountry;
-        }
-    }
-
-
-    private String getDefaultLocVariant() {
-        String defaultVar = android.provider.Settings.Secure.getString(mResolver,
-                android.provider.Settings.Secure.TTS_DEFAULT_VARIANT);
-        if (defaultVar == null) {
-            // no setting found, use the current Locale to determine the default variant
-            return Locale.getDefault().getVariant();
-        } else {
-            return defaultVar;
-        }
-    }
-
-
-    private int setSpeechRate(String callingApp, int rate) {
-        int res = TextToSpeech.ERROR;
-        try {
-            if (isDefaultEnforced()) {
-                res = sNativeSynth.setSpeechRate(getDefaultRate());
-            } else {
-                res = sNativeSynth.setSpeechRate(rate);
-            }
-        } catch (NullPointerException e) {
-            // synth will become null during onDestroy()
-            res = TextToSpeech.ERROR;
-        }
-        return res;
-    }
-
-
-    private int setPitch(String callingApp, int pitch) {
-        int res = TextToSpeech.ERROR;
-        try {
-            res = sNativeSynth.setPitch(pitch);
-        } catch (NullPointerException e) {
-            // synth will become null during onDestroy()
-            res = TextToSpeech.ERROR;
-        }
-        return res;
-    }
-
-
-    private int isLanguageAvailable(String lang, String country, String variant) {
-        int res = TextToSpeech.LANG_NOT_SUPPORTED;
-        try {
-            res = sNativeSynth.isLanguageAvailable(lang, country, variant);
-        } catch (NullPointerException e) {
-            // synth will become null during onDestroy()
-            res = TextToSpeech.LANG_NOT_SUPPORTED;
-        }
-        return res;
-    }
-
-
-    private String[] getLanguage() {
-        try {
-            return sNativeSynth.getLanguage();
-        } catch (Exception e) {
-            return null;
-        }
-    }
-
-
-    private int setLanguage(String callingApp, String lang, String country, String variant) {
-        Log.v(SERVICE_TAG, "TtsService.setLanguage(" + lang + ", " + country + ", " + variant + ")");
-        int res = TextToSpeech.ERROR;
-        try {
-            if (isDefaultEnforced()) {
-                res = sNativeSynth.setLanguage(getDefaultLanguage(), getDefaultCountry(),
-                        getDefaultLocVariant());
-            } else {
-                res = sNativeSynth.setLanguage(lang, country, variant);
-            }
-        } catch (NullPointerException e) {
-            // synth will become null during onDestroy()
-            res = TextToSpeech.ERROR;
-        }
-        return res;
-    }
-
-
-    /**
-     * 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 callingApp, 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 callingApp, 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 callingApp, 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 callingApp, 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
-     *            TextToSpeech.TTS_QUEUE_FLUSH for no queue (interrupts all previous utterances),
-     *            TextToSpeech.TTS_QUEUE_ADD for queued
-     * @param params
-     *            An ArrayList of parameters. This is not implemented for all
-     *            engines.
-     */
-    private int speak(String callingApp, String text, int queueMode, ArrayList<String> params) {
-        // Log.v(SERVICE_TAG, "TTS service received " + text);
-        if (queueMode == TextToSpeech.QUEUE_FLUSH) {
-            stop(callingApp);
-        } else if (queueMode == 2) {
-            stopAll(callingApp);
-        }
-        mSpeechQueue.add(new SpeechItem(callingApp, text, params, SpeechItem.TEXT));
-        if (!mIsSpeaking) {
-            processSpeechQueue();
-        }
-        return TextToSpeech.SUCCESS;
-    }
-
-    /**
-     * Plays the earcon using the specified queueing mode and parameters.
-     *
-     * @param earcon
-     *            The earcon that should be played
-     * @param queueMode
-     *            TextToSpeech.TTS_QUEUE_FLUSH for no queue (interrupts all previous utterances),
-     *            TextToSpeech.TTS_QUEUE_ADD for queued
-     * @param params
-     *            An ArrayList of parameters. This is not implemented for all
-     *            engines.
-     */
-    private int playEarcon(String callingApp, String earcon, int queueMode,
-            ArrayList<String> params) {
-        if (queueMode == TextToSpeech.QUEUE_FLUSH) {
-            stop(callingApp);
-        } else if (queueMode == 2) {
-            stopAll(callingApp);
-        }
-        mSpeechQueue.add(new SpeechItem(callingApp, earcon, params, SpeechItem.EARCON));
-        if (!mIsSpeaking) {
-            processSpeechQueue();
-        }
-        return TextToSpeech.SUCCESS;
-    }
-
-    /**
-     * Stops all speech output and removes any utterances still in the queue for the calling app.
-     */
-    private int stop(String callingApp) {
-        int result = TextToSpeech.ERROR;
-        boolean speechQueueAvailable = false;
-        try{
-            speechQueueAvailable =
-                    speechQueueLock.tryLock(SPEECHQUEUELOCK_TIMEOUT, TimeUnit.MILLISECONDS);
-            if (speechQueueAvailable) {
-                Log.i(SERVICE_TAG, "Stopping");
-                for (int i = mSpeechQueue.size() - 1; i > -1; i--){
-                    if (mSpeechQueue.get(i).mCallingApp.equals(callingApp)){
-                        mSpeechQueue.remove(i);
-                    }
-                }
-                if ((mCurrentSpeechItem != null) &&
-                     mCurrentSpeechItem.mCallingApp.equals(callingApp)) {
-                    try {
-                        result = sNativeSynth.stop();
-                    } catch (NullPointerException e1) {
-                        // synth will become null during onDestroy()
-                        result = TextToSpeech.ERROR;
-                    }
-                    mKillList.put(mCurrentSpeechItem, true);
-                    if (mPlayer != null) {
-                        try {
-                            mPlayer.stop();
-                        } catch (IllegalStateException e) {
-                            // Do nothing, the player is already stopped.
-                        }
-                    }
-                    mIsSpeaking = false;
-                    mCurrentSpeechItem = null;
-                } else {
-                    result = TextToSpeech.SUCCESS;
-                }
-                Log.i(SERVICE_TAG, "Stopped");
-            } else {
-                Log.e(SERVICE_TAG, "TTS stop(): queue locked longer than expected");
-                result = TextToSpeech.ERROR;
-            }
-        } catch (InterruptedException e) {
-          Log.e(SERVICE_TAG, "TTS stop: tryLock interrupted");
-          e.printStackTrace();
-        } finally {
-            // This check is needed because finally will always run; even if the
-            // method returns somewhere in the try block.
-            if (speechQueueAvailable) {
-                speechQueueLock.unlock();
-            }
-            return result;
-        }
-    }
-
-
-    /**
-     * Stops all speech output, both rendered to a file and directly spoken, and removes any
-     * utterances still in the queue globally. Files that were being written are deleted.
-     */
-    @SuppressWarnings("finally")
-    private int killAllUtterances() {
-        int result = TextToSpeech.ERROR;
-        boolean speechQueueAvailable = false;
-
-        try {
-            speechQueueAvailable = speechQueueLock.tryLock(SPEECHQUEUELOCK_TIMEOUT,
-                    TimeUnit.MILLISECONDS);
-            if (speechQueueAvailable) {
-                // remove every single entry in the speech queue
-                mSpeechQueue.clear();
-
-                // clear the current speech item
-                if (mCurrentSpeechItem != null) {
-                    result = sNativeSynth.stopSync();
-                    mKillList.put(mCurrentSpeechItem, true);
-                    mIsSpeaking = false;
-
-                    // was the engine writing to a file?
-                    if (mCurrentSpeechItem.mType == SpeechItem.TEXT_TO_FILE) {
-                        // delete the file that was being written
-                        if (mCurrentSpeechItem.mFilename != null) {
-                            File tempFile = new File(mCurrentSpeechItem.mFilename);
-                            Log.v(SERVICE_TAG, "Leaving behind " + mCurrentSpeechItem.mFilename);
-                            if (tempFile.exists()) {
-                                Log.v(SERVICE_TAG, "About to delete "
-                                        + mCurrentSpeechItem.mFilename);
-                                if (tempFile.delete()) {
-                                    Log.v(SERVICE_TAG, "file successfully deleted");
-                                }
-                            }
-                        }
-                    }
-
-                    mCurrentSpeechItem = null;
-                }
-            } else {
-                Log.e(SERVICE_TAG, "TTS killAllUtterances(): queue locked longer than expected");
-                result = TextToSpeech.ERROR;
-            }
-        } catch (InterruptedException e) {
-            Log.e(SERVICE_TAG, "TTS killAllUtterances(): tryLock interrupted");
-            result = TextToSpeech.ERROR;
-        } finally {
-            // This check is needed because finally will always run, even if the
-            // method returns somewhere in the try block.
-            if (speechQueueAvailable) {
-                speechQueueLock.unlock();
-            }
-            return result;
-        }
-    }
-
-
-    /**
-     * Stops all speech output and removes any utterances still in the queue globally, except
-     * those intended to be synthesized to file.
-     */
-    private int stopAll(String callingApp) {
-        int result = TextToSpeech.ERROR;
-        boolean speechQueueAvailable = false;
-        try{
-            speechQueueAvailable =
-                    speechQueueLock.tryLock(SPEECHQUEUELOCK_TIMEOUT, TimeUnit.MILLISECONDS);
-            if (speechQueueAvailable) {
-                for (int i = mSpeechQueue.size() - 1; i > -1; i--){
-                    if (mSpeechQueue.get(i).mType != SpeechItem.TEXT_TO_FILE){
-                        mSpeechQueue.remove(i);
-                    }
-                }
-                if ((mCurrentSpeechItem != null) &&
-                    ((mCurrentSpeechItem.mType != SpeechItem.TEXT_TO_FILE) ||
-                      mCurrentSpeechItem.mCallingApp.equals(callingApp))) {
-                    try {
-                        result = sNativeSynth.stop();
-                    } catch (NullPointerException e1) {
-                        // synth will become null during onDestroy()
-                        result = TextToSpeech.ERROR;
-                    }
-                    mKillList.put(mCurrentSpeechItem, true);
-                    if (mPlayer != null) {
-                        try {
-                            mPlayer.stop();
-                        } catch (IllegalStateException e) {
-                            // Do nothing, the player is already stopped.
-                        }
-                    }
-                    mIsSpeaking = false;
-                    mCurrentSpeechItem = null;
-                } else {
-                    result = TextToSpeech.SUCCESS;
-                }
-                Log.i(SERVICE_TAG, "Stopped all");
-            } else {
-                Log.e(SERVICE_TAG, "TTS stopAll(): queue locked longer than expected");
-                result = TextToSpeech.ERROR;
-            }
-        } catch (InterruptedException e) {
-          Log.e(SERVICE_TAG, "TTS stopAll: tryLock interrupted");
-          e.printStackTrace();
-        } finally {
-            // This check is needed because finally will always run; even if the
-            // method returns somewhere in the try block.
-            if (speechQueueAvailable) {
-                speechQueueLock.unlock();
-            }
-            return result;
-        }
-    }
-
-    public void onCompletion(MediaPlayer arg0) {
-        // mCurrentSpeechItem may become null if it is stopped at the same
-        // time it completes.
-        SpeechItem currentSpeechItemCopy = mCurrentSpeechItem;
-        if (currentSpeechItemCopy != null) {
-            String callingApp = currentSpeechItemCopy.mCallingApp;
-            ArrayList<String> params = currentSpeechItemCopy.mParams;
-            String utteranceId = "";
-            if (params != null) {
-                for (int i = 0; i < params.size() - 1; i = i + 2) {
-                    String param = params.get(i);
-                    if (param.equals(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID)) {
-                        utteranceId = params.get(i + 1);
-                    }
-                }
-            }
-            if (utteranceId.length() > 0) {
-                dispatchUtteranceCompletedCallback(utteranceId, callingApp);
-            }
-        }
-        processSpeechQueue();
-    }
-
-    private int playSilence(String callingApp, long duration, int queueMode,
-            ArrayList<String> params) {
-        if (queueMode == TextToSpeech.QUEUE_FLUSH) {
-            stop(callingApp);
-        }
-        mSpeechQueue.add(new SpeechItem(callingApp, duration, params));
-        if (!mIsSpeaking) {
-            processSpeechQueue();
-        }
-        return TextToSpeech.SUCCESS;
-    }
-
-    private void silence(final SpeechItem speechItem) {
-        class SilenceThread implements Runnable {
-            public void run() {
-                String utteranceId = "";
-                if (speechItem.mParams != null){
-                    for (int i = 0; i < speechItem.mParams.size() - 1; i = i + 2){
-                        String param = speechItem.mParams.get(i);
-                        if (param.equals(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID)){
-                            utteranceId = speechItem.mParams.get(i+1);
-                        }
-                    }
-                }
-                try {
-                    Thread.sleep(speechItem.mDuration);
-                } catch (InterruptedException e) {
-                    e.printStackTrace();
-                } finally {
-                    if (utteranceId.length() > 0){
-                        dispatchUtteranceCompletedCallback(utteranceId, speechItem.mCallingApp);
-                    }
-                    processSpeechQueue();
-                }
-            }
-        }
-        Thread slnc = (new Thread(new SilenceThread()));
-        slnc.setPriority(Thread.MIN_PRIORITY);
-        slnc.start();
-    }
-
-    private void speakInternalOnly(final SpeechItem speechItem) {
-        class SynthThread implements Runnable {
-            public void run() {
-                boolean synthAvailable = false;
-                String utteranceId = "";
-                try {
-                    synthAvailable = synthesizerLock.tryLock();
-                    if (!synthAvailable) {
-                        mSynthBusy = true;
-                        Thread.sleep(100);
-                        Thread synth = (new Thread(new SynthThread()));
-                        synth.start();
-                        mSynthBusy = false;
-                        return;
-                    }
-                    int streamType = DEFAULT_STREAM_TYPE;
-                    String language = "";
-                    String country = "";
-                    String variant = "";
-                    String speechRate = "";
-                    String engine = "";
-                    String pitch = "";
-                    float volume = TextToSpeech.Engine.DEFAULT_VOLUME;
-                    float pan = TextToSpeech.Engine.DEFAULT_PAN;
-                    if (speechItem.mParams != null){
-                        for (int i = 0; i < speechItem.mParams.size() - 1; i = i + 2){
-                            String param = speechItem.mParams.get(i);
-                            if (param != null) {
-                                if (param.equals(TextToSpeech.Engine.KEY_PARAM_RATE)) {
-                                    speechRate = speechItem.mParams.get(i+1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_LANGUAGE)){
-                                    language = speechItem.mParams.get(i+1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_COUNTRY)){
-                                    country = speechItem.mParams.get(i+1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_VARIANT)){
-                                    variant = speechItem.mParams.get(i+1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID)){
-                                    utteranceId = speechItem.mParams.get(i+1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_STREAM)) {
-                                    try {
-                                        streamType
-                                                = Integer.parseInt(speechItem.mParams.get(i + 1));
-                                    } catch (NumberFormatException e) {
-                                        streamType = DEFAULT_STREAM_TYPE;
-                                    }
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_ENGINE)) {
-                                    engine = speechItem.mParams.get(i + 1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_PITCH)) {
-                                    pitch = speechItem.mParams.get(i + 1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_VOLUME)) {
-                                    try {
-                                        volume = Float.parseFloat(speechItem.mParams.get(i + 1));
-                                    } catch (NumberFormatException e) {
-                                        volume = TextToSpeech.Engine.DEFAULT_VOLUME;
-                                    }
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_PAN)) {
-                                    try {
-                                        pan = Float.parseFloat(speechItem.mParams.get(i + 1));
-                                    } catch (NumberFormatException e) {
-                                        pan = TextToSpeech.Engine.DEFAULT_PAN;
-                                    }
-                                }
-                            }
-                        }
-                    }
-                    // Only do the synthesis if it has not been killed by a subsequent utterance.
-                    if (mKillList.get(speechItem) == null) {
-                        if (engine.length() > 0) {
-                            setEngine(engine);
-                        } else {
-                            setEngine(getDefaultEngine());
-                        }
-                        if (language.length() > 0){
-                            setLanguage("", language, country, variant);
-                        } else {
-                            setLanguage("", getDefaultLanguage(), getDefaultCountry(),
-                                    getDefaultLocVariant());
-                        }
-                        if (speechRate.length() > 0){
-                            setSpeechRate("", Integer.parseInt(speechRate));
-                        } else {
-                            setSpeechRate("", getDefaultRate());
-                        }
-                        if (pitch.length() > 0){
-                            setPitch("", Integer.parseInt(pitch));
-                        } else {
-                            setPitch("", getDefaultPitch());
-                        }
-                        try {
-                            sNativeSynth.speak(speechItem.mText, streamType, volume, pan);
-                        } catch (NullPointerException e) {
-                            // synth will become null during onDestroy()
-                            Log.v(SERVICE_TAG, " null synth, can't speak");
-                        }
-                    }
-                } catch (InterruptedException e) {
-                    Log.e(SERVICE_TAG, "TTS speakInternalOnly(): tryLock interrupted");
-                    e.printStackTrace();
-                } finally {
-                    // This check is needed because finally will always run;
-                    // even if the
-                    // method returns somewhere in the try block.
-                    if (utteranceId.length() > 0){
-                        dispatchUtteranceCompletedCallback(utteranceId, speechItem.mCallingApp);
-                    }
-                    if (synthAvailable) {
-                        synthesizerLock.unlock();
-                        processSpeechQueue();
-                    }
-                }
-            }
-        }
-        Thread synth = (new Thread(new SynthThread()));
-        synth.setPriority(Thread.MAX_PRIORITY);
-        synth.start();
-    }
-
-    private void synthToFileInternalOnly(final SpeechItem speechItem) {
-        class SynthThread implements Runnable {
-            public void run() {
-                boolean synthAvailable = false;
-                String utteranceId = "";
-                Log.i(SERVICE_TAG, "Synthesizing to " + speechItem.mFilename);
-                try {
-                    synthAvailable = synthesizerLock.tryLock();
-                    if (!synthAvailable) {
-                        synchronized (this) {
-                            mSynthBusy = true;
-                        }
-                        Thread.sleep(100);
-                        Thread synth = (new Thread(new SynthThread()));
-                        synth.start();
-                        synchronized (this) {
-                            mSynthBusy = false;
-                        }
-                        return;
-                    }
-                    String language = "";
-                    String country = "";
-                    String variant = "";
-                    String speechRate = "";
-                    String engine = "";
-                    String pitch = "";
-                    if (speechItem.mParams != null){
-                        for (int i = 0; i < speechItem.mParams.size() - 1; i = i + 2){
-                            String param = speechItem.mParams.get(i);
-                            if (param != null) {
-                                if (param.equals(TextToSpeech.Engine.KEY_PARAM_RATE)) {
-                                    speechRate = speechItem.mParams.get(i+1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_LANGUAGE)){
-                                    language = speechItem.mParams.get(i+1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_COUNTRY)){
-                                    country = speechItem.mParams.get(i+1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_VARIANT)){
-                                    variant = speechItem.mParams.get(i+1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID)){
-                                    utteranceId = speechItem.mParams.get(i+1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_ENGINE)) {
-                                    engine = speechItem.mParams.get(i + 1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_PITCH)) {
-                                    pitch = speechItem.mParams.get(i + 1);
-                                }
-                            }
-                        }
-                    }
-                    // Only do the synthesis if it has not been killed by a subsequent utterance.
-                    if (mKillList.get(speechItem) == null){
-                        if (engine.length() > 0) {
-                            setEngine(engine);
-                        } else {
-                            setEngine(getDefaultEngine());
-                        }
-                        if (language.length() > 0){
-                            setLanguage("", language, country, variant);
-                        } else {
-                            setLanguage("", getDefaultLanguage(), getDefaultCountry(),
-                                    getDefaultLocVariant());
-                        }
-                        if (speechRate.length() > 0){
-                            setSpeechRate("", Integer.parseInt(speechRate));
-                        } else {
-                            setSpeechRate("", getDefaultRate());
-                        }
-                        if (pitch.length() > 0){
-                            setPitch("", Integer.parseInt(pitch));
-                        } else {
-                            setPitch("", getDefaultPitch());
-                        }
-                        try {
-                            sNativeSynth.synthesizeToFile(speechItem.mText, speechItem.mFilename);
-                        } catch (NullPointerException e) {
-                            // synth will become null during onDestroy()
-                            Log.v(SERVICE_TAG, " null synth, can't synthesize to file");
-                        }
-                    }
-                } catch (InterruptedException e) {
-                    Log.e(SERVICE_TAG, "TTS synthToFileInternalOnly(): tryLock interrupted");
-                    e.printStackTrace();
-                } finally {
-                    // This check is needed because finally will always run;
-                    // even if the
-                    // method returns somewhere in the try block.
-                    if (utteranceId.length() > 0){
-                        dispatchUtteranceCompletedCallback(utteranceId, speechItem.mCallingApp);
-                    }
-                    if (synthAvailable) {
-                        synthesizerLock.unlock();
-                        processSpeechQueue();
-                    }
-                }
-            }
-        }
-        Thread synth = (new Thread(new SynthThread()));
-        synth.setPriority(Thread.MAX_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 broadcastTtsQueueProcessingCompleted(){
-        Intent i = new Intent(TextToSpeech.ACTION_TTS_QUEUE_PROCESSING_COMPLETED);
-        sendBroadcast(i);
-    }
-
-
-    private void dispatchUtteranceCompletedCallback(String utteranceId, String packageName) {
-        ITtsCallback cb = mCallbacksMap.get(packageName);
-        if (cb == null){
-            return;
-        }
-        Log.v(SERVICE_TAG, "TTS callback: dispatch started");
-        // Broadcast to all clients the new value.
-        final int N = mCallbacks.beginBroadcast();
-        try {
-            cb.utteranceCompleted(utteranceId);
-        } catch (RemoteException e) {
-            // The RemoteCallbackList will take care of removing
-            // the dead object for us.
-        }
-        mCallbacks.finishBroadcast();
-        Log.v(SERVICE_TAG, "TTS callback: dispatch completed to " + N);
-    }
-
-    private SpeechItem splitCurrentTextIfNeeded(SpeechItem currentSpeechItem){
-        if (currentSpeechItem.mText.length() < MAX_SPEECH_ITEM_CHAR_LENGTH){
-            return currentSpeechItem;
-        } else {
-            String callingApp = currentSpeechItem.mCallingApp;
-            ArrayList<SpeechItem> splitItems = new ArrayList<SpeechItem>();
-            int start = 0;
-            int end = start + MAX_SPEECH_ITEM_CHAR_LENGTH - 1;
-            String splitText;
-            SpeechItem splitItem;
-            while (end < currentSpeechItem.mText.length()){
-                splitText = currentSpeechItem.mText.substring(start, end);
-                splitItem = new SpeechItem(callingApp, splitText, null, SpeechItem.TEXT);
-                splitItems.add(splitItem);
-                start = end;
-                end = start + MAX_SPEECH_ITEM_CHAR_LENGTH - 1;
-            }
-            splitText = currentSpeechItem.mText.substring(start);
-            splitItem = new SpeechItem(callingApp, splitText, null, SpeechItem.TEXT);
-            splitItems.add(splitItem);
-            mSpeechQueue.remove(0);
-            for (int i = splitItems.size() - 1; i >= 0; i--){
-                mSpeechQueue.add(0, splitItems.get(i));
-            }
-            return mSpeechQueue.get(0);
-        }
-    }
-
-    private void processSpeechQueue() {
-        boolean speechQueueAvailable = false;
-        synchronized (this) {
-            if (mSynthBusy){
-                // There is already a synth thread waiting to run.
-                return;
-            }
-        }
-        try {
-            speechQueueAvailable =
-                    speechQueueLock.tryLock(SPEECHQUEUELOCK_TIMEOUT, TimeUnit.MILLISECONDS);
-            if (!speechQueueAvailable) {
-                Log.e(SERVICE_TAG, "processSpeechQueue - Speech queue is unavailable.");
-                return;
-            }
-            if (mSpeechQueue.size() < 1) {
-                mIsSpeaking = false;
-                mKillList.clear();
-                broadcastTtsQueueProcessingCompleted();
-                return;
-            }
-
-            mCurrentSpeechItem = mSpeechQueue.get(0);
-            mIsSpeaking = true;
-            SoundResource sr = getSoundResource(mCurrentSpeechItem);
-            // Synth speech as needed - synthesizer should call
-            // processSpeechQueue to continue running the queue
-            // Log.v(SERVICE_TAG, "TTS processing: " + mCurrentSpeechItem.mText);
-            if (sr == null) {
-                if (mCurrentSpeechItem.mType == SpeechItem.TEXT) {
-                    mCurrentSpeechItem = splitCurrentTextIfNeeded(mCurrentSpeechItem);
-                    speakInternalOnly(mCurrentSpeechItem);
-                } else if (mCurrentSpeechItem.mType == SpeechItem.TEXT_TO_FILE) {
-                    synthToFileInternalOnly(mCurrentSpeechItem);
-                } else {
-                    // This is either silence or an earcon that was missing
-                    silence(mCurrentSpeechItem);
-                }
-            } 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.setAudioStreamType(getStreamTypeFromParams(mCurrentSpeechItem.mParams));
-                    mPlayer.start();
-                } catch (IllegalStateException e) {
-                    mSpeechQueue.clear();
-                    mIsSpeaking = false;
-                    cleanUpPlayer();
-                    return;
-                }
-            }
-            if (mSpeechQueue.size() > 0) {
-                mSpeechQueue.remove(0);
-            }
-        } catch (InterruptedException e) {
-          Log.e(SERVICE_TAG, "TTS processSpeechQueue: tryLock interrupted");
-          e.printStackTrace();
-        } 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 int getStreamTypeFromParams(ArrayList<String> paramList) {
-        int streamType = DEFAULT_STREAM_TYPE;
-        if (paramList == null) {
-            return streamType;
-        }
-        for (int i = 0; i < paramList.size() - 1; i = i + 2) {
-            String param = paramList.get(i);
-            if ((param != null) && (param.equals(TextToSpeech.Engine.KEY_PARAM_STREAM))) {
-                try {
-                    streamType = Integer.parseInt(paramList.get(i + 1));
-                } catch (NumberFormatException e) {
-                    streamType = DEFAULT_STREAM_TYPE;
-                }
-            }
-        }
-        return streamType;
-    }
-
-    private void cleanUpPlayer() {
-        if (mPlayer != null) {
-            mPlayer.release();
-            mPlayer = null;
-        }
-    }
-
-    /**
-     * Synthesizes the given text to a file using the specified 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 can be started
-     */
-    private boolean synthesizeToFile(String callingApp, String text, ArrayList<String> params,
-            String filename) {
-        // Don't allow a filename that is too long
-        if (filename.length() > MAX_FILENAME_LENGTH) {
-            return false;
-        }
-        // Don't allow anything longer than the max text length; since this
-        // is synthing to a file, don't even bother splitting it.
-        if (text.length() >= MAX_SPEECH_ITEM_CHAR_LENGTH){
-            return false;
-        }
-        // Check that the output file can be created
-        try {
-            File tempFile = new File(filename);
-            if (tempFile.exists()) {
-                Log.v("TtsService", "File " + filename + " exists, deleting.");
-                tempFile.delete();
-            }
-            if (!tempFile.createNewFile()) {
-                Log.e("TtsService", "Unable to synthesize to file: can't create " + filename);
-                return false;
-            }
-            tempFile.delete();
-        } catch (IOException e) {
-            Log.e("TtsService", "Can't create " + filename + " due to exception " + e);
-            return false;
-        }
-        mSpeechQueue.add(new SpeechItem(callingApp, text, params, SpeechItem.TEXT_TO_FILE, filename));
-        if (!mIsSpeaking) {
-            processSpeechQueue();
-        }
-        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 android.speech.tts.ITts.Stub mBinder = new Stub() {
-
-        public int registerCallback(String packageName, ITtsCallback cb) {
-            if (cb != null) {
-                mCallbacks.register(cb);
-                mCallbacksMap.put(packageName, cb);
-                return TextToSpeech.SUCCESS;
-            }
-            return TextToSpeech.ERROR;
-        }
-
-        public int unregisterCallback(String packageName, ITtsCallback cb) {
-            if (cb != null) {
-                mCallbacksMap.remove(packageName);
-                mCallbacks.unregister(cb);
-                return TextToSpeech.SUCCESS;
-            }
-            return TextToSpeech.ERROR;
-        }
-
-        /**
-         * Speaks the given text using the specified queueing mode and
-         * parameters.
-         *
-         * @param text
-         *            The text that should be spoken
-         * @param queueMode
-         *            TextToSpeech.TTS_QUEUE_FLUSH for no queue (interrupts all previous utterances)
-         *            TextToSpeech.TTS_QUEUE_ADD for queued
-         * @param params
-         *            An ArrayList of parameters. The first element of this
-         *            array controls the type of voice to use.
-         */
-        public int speak(String callingApp, String text, int queueMode, String[] params) {
-            ArrayList<String> speakingParams = new ArrayList<String>();
-            if (params != null) {
-                speakingParams = new ArrayList<String>(Arrays.asList(params));
-            }
-            return mSelf.speak(callingApp, text, queueMode, speakingParams);
-        }
-
-        /**
-         * Plays the earcon using the specified queueing mode and parameters.
-         *
-         * @param earcon
-         *            The earcon that should be played
-         * @param queueMode
-         *            TextToSpeech.TTS_QUEUE_FLUSH for no queue (interrupts all previous utterances)
-         *            TextToSpeech.TTS_QUEUE_ADD for queued
-         * @param params
-         *            An ArrayList of parameters.
-         */
-        public int playEarcon(String callingApp, String earcon, int queueMode, String[] params) {
-            ArrayList<String> speakingParams = new ArrayList<String>();
-            if (params != null) {
-                speakingParams = new ArrayList<String>(Arrays.asList(params));
-            }
-            return mSelf.playEarcon(callingApp, 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
-         *            TextToSpeech.TTS_QUEUE_FLUSH for no queue (interrupts all previous utterances)
-         *            TextToSpeech.TTS_QUEUE_ADD for queued
-         * @param params
-         *            An ArrayList of parameters.
-         */
-        public int playSilence(String callingApp, long duration, int queueMode, String[] params) {
-            ArrayList<String> speakingParams = new ArrayList<String>();
-            if (params != null) {
-                speakingParams = new ArrayList<String>(Arrays.asList(params));
-            }
-            return mSelf.playSilence(callingApp, duration, queueMode, speakingParams);
-        }
-
-        /**
-         * Stops all speech output and removes any utterances still in the
-         * queue.
-         */
-        public int stop(String callingApp) {
-            return mSelf.stop(callingApp);
-        }
-
-        /**
-         * 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 callingApp, String text, String packageName, int resId) {
-            mSelf.addSpeech(callingApp, 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 callingApp, String text, String filename) {
-            mSelf.addSpeech(callingApp, 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 callingApp, String earcon, String packageName, int resId) {
-            mSelf.addEarcon(callingApp, 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 callingApp, String earcon, String filename) {
-            mSelf.addEarcon(callingApp, 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 int setSpeechRate(String callingApp, int speechRate) {
-            return mSelf.setSpeechRate(callingApp, speechRate);
-        }
-
-        /**
-         * Sets the pitch for the TTS. Note that this will only have an
-         * effect on synthesized speech; it will not affect pre-recorded speech.
-         *
-         * @param pitch
-         *            The pitch that should be used for the synthesized voice
-         */
-        public int setPitch(String callingApp, int pitch) {
-            return mSelf.setPitch(callingApp, pitch);
-        }
-
-        /**
-         * Returns the level of support for the specified language.
-         *
-         * @param lang  the three letter ISO language code.
-         * @param country  the three letter ISO country code.
-         * @param variant  the variant code associated with the country and language pair.
-         * @return one of TTS_LANG_NOT_SUPPORTED, TTS_LANG_MISSING_DATA, TTS_LANG_AVAILABLE,
-         *      TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE as defined in
-         *      android.speech.tts.TextToSpeech.
-         */
-        public int isLanguageAvailable(String lang, String country, String variant,
-                String[] params) {
-            for (int i = 0; i < params.length - 1; i = i + 2){
-                String param = params[i];
-                if (param != null) {
-                    if (param.equals(TextToSpeech.Engine.KEY_PARAM_ENGINE)) {
-                        mSelf.setEngine(params[i + 1]);
-                        break;
-                    }
-                }
-            }
-            return mSelf.isLanguageAvailable(lang, country, variant);
-        }
-
-        /**
-         * Returns the currently set language / country / variant strings representing the
-         * language used by the TTS engine.
-         * @return null is no language is set, or an array of 3 string containing respectively
-         *      the language, country and variant.
-         */
-        public String[] getLanguage() {
-            return mSelf.getLanguage();
-        }
-
-        /**
-         * Sets the speech rate for the TTS, which affects the synthesized voice.
-         *
-         * @param lang  the three letter ISO language code.
-         * @param country  the three letter ISO country code.
-         * @param variant  the variant code associated with the country and language pair.
-         */
-        public int setLanguage(String callingApp, String lang, String country, String variant) {
-            return mSelf.setLanguage(callingApp, lang, country, variant);
-        }
-
-        /**
-         * Synthesizes the given text to a file using the specified
-         * 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 callingApp, 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(callingApp, text, speakingParams, filename);
-        }
-
-        /**
-         * Sets the speech synthesis engine for the TTS by specifying its packagename
-         *
-         * @param packageName  the packageName of the speech synthesis engine (ie, "com.svox.pico")
-         *
-         * @return SUCCESS or ERROR as defined in android.speech.tts.TextToSpeech.
-         */
-        public int setEngineByPackageName(String packageName) {
-            return mSelf.setEngine(packageName);
-        }
-
-        /**
-         * Returns the packagename of the default speech synthesis engine.
-         *
-         * @return Packagename of the TTS engine that the user has chosen as their default.
-         */
-        public String getDefaultEngine() {
-            return mSelf.getDefaultEngine();
-        }
-
-        /**
-         * Returns whether or not the user is forcing their defaults to override the
-         * Text-To-Speech settings set by applications.
-         *
-         * @return Whether or not defaults are enforced.
-         */
-        public boolean areDefaultsEnforced() {
-            return mSelf.isDefaultEnforced();
-        }
-
-    };
-
-}
diff --git a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
index 5b80a93..ca56c4f 100644
--- a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
@@ -28,6 +28,7 @@
 import android.os.CountDownTimer;
 import android.os.SystemClock;
 import android.telephony.TelephonyManager;
+import android.text.InputType;
 import android.text.method.DigitsKeyListener;
 import android.text.method.TextKeyListener;
 import android.util.Log;
@@ -70,6 +71,7 @@
     private CountDownTimer mCountdownTimer;
 
     private StatusView mStatusView;
+    private final boolean mUseSystemIME = true; // TODO: Make configurable
 
     // To avoid accidental lockout due to events while the device in in the pocket, ignore
     // any passwords with length less than or equal to this length.
@@ -108,14 +110,23 @@
         mPasswordEntry.setOnEditorActionListener(this);
         mPasswordEntry.setOnClickListener(new OnClickListener() {
             public void onClick(View v) {
-                if (mIsAlpha && !isPhysicalKbShowing) {
-                    mKeyboardViewAlpha.setVisibility(
-                            mKeyboardViewAlpha.getVisibility() == View.VISIBLE
-                            ? View.GONE : View.VISIBLE);
-                    mCallback.pokeWakelock();
+                if (mIsAlpha && !isPhysicalKbShowing && !mUseSystemIME) {
+                    // Toggle visibility of alpha keyboard
+                    final boolean visible = mKeyboardViewAlpha.getVisibility() == View.VISIBLE;
+                    mKeyboardViewAlpha.setVisibility(visible ? View.GONE : View.VISIBLE);
                 }
+                mCallback.pokeWakelock();
             }
         });
+
+        // We don't currently use the IME for PIN mode, but this will make it work if we ever do...
+        if (!mIsAlpha) {
+            mPasswordEntry.setInputType(InputType.TYPE_CLASS_NUMBER
+                    | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
+        } else {
+            mPasswordEntry.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
+        }
+
         mEmergencyCallButton = (Button) findViewById(R.id.emergencyCall);
         mEmergencyCallButton.setOnClickListener(this);
         mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
@@ -176,7 +187,7 @@
 
     /** {@inheritDoc} */
     public boolean needsInput() {
-        return false;
+        return mUseSystemIME && mIsAlpha;
     }
 
     /** {@inheritDoc} */
@@ -295,7 +306,7 @@
 
     public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
         // Check if this was the result of hitting the enter key
-        if (actionId == EditorInfo.IME_NULL) {
+        if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE) {
             verifyPasswordAndUnlock();
             return true;
         }
diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp
index 5c3b43fc..dca795c 100644
--- a/services/audioflinger/AudioResampler.cpp
+++ b/services/audioflinger/AudioResampler.cpp
@@ -26,11 +26,15 @@
 #include "AudioResamplerSinc.h"
 #include "AudioResamplerCubic.h"
 
+#ifdef __arm__
+#include <machine/cpu-features.h>
+#endif
+
 namespace android {
 
-#ifdef __ARM_ARCH_5E__  // optimized asm option
+#ifdef __ARM_HAVE_HALFWORD_MULTIPLY // optimized asm option
     #define ASM_ARM_RESAMP1 // enable asm optimisation for ResamplerOrder1
-#endif // __ARM_ARCH_5E__
+#endif // __ARM_HAVE_HALFWORD_MULTIPLY
 // ----------------------------------------------------------------------------
 
 class AudioResamplerOrder1 : public AudioResampler {
diff --git a/services/input/Android.mk b/services/input/Android.mk
index 96431bc..f9f8623 100644
--- a/services/input/Android.mk
+++ b/services/input/Android.mk
@@ -22,7 +22,9 @@
     InputManager.cpp \
     InputReader.cpp \
     InputWindow.cpp \
-    PointerController.cpp
+    PointerController.cpp \
+    SpotController.cpp \
+    SpriteController.cpp
 
 LOCAL_SHARED_LIBRARIES := \
     libcutils \
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index cf9b13d..9ed1391 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -20,6 +20,7 @@
 #include "EventHub.h"
 #include "InputDispatcher.h"
 #include "PointerController.h"
+#include "SpotController.h"
 
 #include <ui/Input.h>
 #include <ui/DisplayInfo.h>
@@ -89,6 +90,9 @@
 
     /* Gets a pointer controller associated with the specified cursor device (ie. a mouse). */
     virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) = 0;
+
+    /* Gets a spot controller associated with the specified touch pad device. */
+    virtual sp<SpotControllerInterface> obtainSpotController(int32_t deviceId) = 0;
 };
 
 
diff --git a/services/input/PointerController.cpp b/services/input/PointerController.cpp
index a4ee295..15effb7 100644
--- a/services/input/PointerController.cpp
+++ b/services/input/PointerController.cpp
@@ -49,8 +49,11 @@
 static const float FADE_DECAY_PER_FRAME = float(FADE_FRAME_INTERVAL) / FADE_DURATION;
 
 
-PointerController::PointerController(const sp<Looper>& looper, int32_t pointerLayer) :
-        mLooper(looper), mPointerLayer(pointerLayer) {
+PointerController::PointerController(const sp<Looper>& looper,
+        const sp<SpriteController>& spriteController) :
+        mLooper(looper), mSpriteController(spriteController) {
+    mHandler = new WeakMessageHandler(this);
+
     AutoMutex _l(mLock);
 
     mLocked.displayWidth = -1;
@@ -61,34 +64,20 @@
     mLocked.pointerY = 0;
     mLocked.buttonState = 0;
 
-    mLocked.iconBitmap = NULL;
-    mLocked.iconHotSpotX = 0;
-    mLocked.iconHotSpotY = 0;
-
     mLocked.fadeAlpha = 1;
     mLocked.inactivityFadeDelay = INACTIVITY_FADE_DELAY_NORMAL;
 
-    mLocked.wantVisible = false;
     mLocked.visible = false;
-    mLocked.drawn = false;
 
-    mHandler = new WeakMessageHandler(this);
+    mLocked.sprite = mSpriteController->createSprite();
 }
 
 PointerController::~PointerController() {
     mLooper->removeMessages(mHandler);
 
-    if (mSurfaceControl != NULL) {
-        mSurfaceControl->clear();
-        mSurfaceControl.clear();
-    }
+    AutoMutex _l(mLock);
 
-    if (mSurfaceComposerClient != NULL) {
-        mSurfaceComposerClient->dispose();
-        mSurfaceComposerClient.clear();
-    }
-
-    delete mLocked.iconBitmap;
+    mLocked.sprite.clear();
 }
 
 bool PointerController::getBounds(float* outMinX, float* outMinY,
@@ -214,75 +203,11 @@
 }
 
 void PointerController::updateLocked() {
-    bool wantVisibleAndHavePointerIcon = mLocked.wantVisible && mLocked.iconBitmap;
-
-    if (wantVisibleAndHavePointerIcon) {
-        // Want the pointer to be visible.
-        // Ensure the surface is created and drawn.
-        if (!createSurfaceIfNeededLocked() || !drawPointerIfNeededLocked()) {
-            return;
-        }
-    } else {
-        // Don't want the pointer to be visible.
-        // If it is not visible then we are done.
-        if (mSurfaceControl == NULL || !mLocked.visible) {
-            return;
-        }
-    }
-
-    status_t status = mSurfaceComposerClient->openTransaction();
-    if (status) {
-        LOGE("Error opening surface transaction to update pointer surface.");
-        return;
-    }
-
-    if (wantVisibleAndHavePointerIcon) {
-        status = mSurfaceControl->setPosition(
-                mLocked.pointerX - mLocked.iconHotSpotX,
-                mLocked.pointerY - mLocked.iconHotSpotY);
-        if (status) {
-            LOGE("Error %d moving pointer surface.", status);
-            goto CloseTransaction;
-        }
-
-        status = mSurfaceControl->setAlpha(mLocked.fadeAlpha);
-        if (status) {
-            LOGE("Error %d setting pointer surface alpha.", status);
-            goto CloseTransaction;
-        }
-
-        if (!mLocked.visible) {
-            status = mSurfaceControl->setLayer(mPointerLayer);
-            if (status) {
-                LOGE("Error %d setting pointer surface layer.", status);
-                goto CloseTransaction;
-            }
-
-            status = mSurfaceControl->show(mPointerLayer);
-            if (status) {
-                LOGE("Error %d showing pointer surface.", status);
-                goto CloseTransaction;
-            }
-
-            mLocked.visible = true;
-        }
-    } else {
-        if (mLocked.visible) {
-            status = mSurfaceControl->hide();
-            if (status) {
-                LOGE("Error %d hiding pointer surface.", status);
-                goto CloseTransaction;
-            }
-
-            mLocked.visible = false;
-        }
-    }
-
-CloseTransaction:
-    status = mSurfaceComposerClient->closeTransaction();
-    if (status) {
-        LOGE("Error closing surface transaction to update pointer surface.");
-    }
+    mLocked.sprite->openTransaction();
+    mLocked.sprite->setPosition(mLocked.pointerX, mLocked.pointerY);
+    mLocked.sprite->setAlpha(mLocked.fadeAlpha);
+    mLocked.sprite->setVisible(mLocked.visible);
+    mLocked.sprite->closeTransaction();
 }
 
 void PointerController::setDisplaySize(int32_t width, int32_t height) {
@@ -339,7 +264,7 @@
         case DISPLAY_ORIENTATION_90:
             temp = x;
             x = y;
-            y = mLocked.displayWidth - x;
+            y = mLocked.displayWidth - temp;
             break;
         case DISPLAY_ORIENTATION_180:
             x = mLocked.displayWidth - x;
@@ -365,106 +290,7 @@
 void PointerController::setPointerIcon(const SkBitmap* bitmap, float hotSpotX, float hotSpotY) {
     AutoMutex _l(mLock);
 
-    if (mLocked.iconBitmap) {
-        delete mLocked.iconBitmap;
-        mLocked.iconBitmap = NULL;
-    }
-
-    if (bitmap) {
-        mLocked.iconBitmap = new SkBitmap();
-        bitmap->copyTo(mLocked.iconBitmap, SkBitmap::kARGB_8888_Config);
-    }
-
-    mLocked.iconHotSpotX = hotSpotX;
-    mLocked.iconHotSpotY = hotSpotY;
-    mLocked.drawn = false;
-}
-
-bool PointerController::createSurfaceIfNeededLocked() {
-    if (!mLocked.iconBitmap) {
-        // If we don't have a pointer icon, then no point allocating a surface now.
-        return false;
-    }
-
-    if (mSurfaceComposerClient == NULL) {
-        mSurfaceComposerClient = new SurfaceComposerClient();
-    }
-
-    if (mSurfaceControl == NULL) {
-        mSurfaceControl = mSurfaceComposerClient->createSurface(getpid(),
-                String8("Pointer Icon"), 0,
-                mLocked.iconBitmap->width(), mLocked.iconBitmap->height(),
-                PIXEL_FORMAT_RGBA_8888);
-        if (mSurfaceControl == NULL) {
-            LOGE("Error creating pointer surface.");
-            return false;
-        }
-    }
-    return true;
-}
-
-bool PointerController::drawPointerIfNeededLocked() {
-    if (!mLocked.drawn) {
-        if (!mLocked.iconBitmap) {
-            return false;
-        }
-
-        if (!resizeSurfaceLocked(mLocked.iconBitmap->width(), mLocked.iconBitmap->height())) {
-            return false;
-        }
-
-        sp<Surface> surface = mSurfaceControl->getSurface();
-
-        Surface::SurfaceInfo surfaceInfo;
-        status_t status = surface->lock(&surfaceInfo);
-        if (status) {
-            LOGE("Error %d locking pointer surface before drawing.", status);
-            return false;
-        }
-
-        SkBitmap surfaceBitmap;
-        ssize_t bpr = surfaceInfo.s * bytesPerPixel(surfaceInfo.format);
-        surfaceBitmap.setConfig(SkBitmap::kARGB_8888_Config, surfaceInfo.w, surfaceInfo.h, bpr);
-        surfaceBitmap.setPixels(surfaceInfo.bits);
-
-        SkCanvas surfaceCanvas;
-        surfaceCanvas.setBitmapDevice(surfaceBitmap);
-
-        SkPaint paint;
-        paint.setXfermodeMode(SkXfermode::kSrc_Mode);
-        surfaceCanvas.drawBitmap(*mLocked.iconBitmap, 0, 0, &paint);
-
-        status = surface->unlockAndPost();
-        if (status) {
-            LOGE("Error %d unlocking pointer surface after drawing.", status);
-            return false;
-        }
-    }
-
-    mLocked.drawn = true;
-    return true;
-}
-
-bool PointerController::resizeSurfaceLocked(int32_t width, int32_t height) {
-    status_t status = mSurfaceComposerClient->openTransaction();
-    if (status) {
-        LOGE("Error opening surface transaction to resize pointer surface.");
-        return false;
-    }
-
-    status = mSurfaceControl->setSize(width, height);
-    if (status) {
-        LOGE("Error %d setting pointer surface size.", status);
-        return false;
-    }
-
-    status = mSurfaceComposerClient->closeTransaction();
-    if (status) {
-        LOGE("Error closing surface transaction to resize pointer surface.");
-        return false;
-    }
-
-    return true;
+    mLocked.sprite->setBitmap(bitmap, hotSpotX, hotSpotY);
 }
 
 void PointerController::handleMessage(const Message& message) {
@@ -481,7 +307,7 @@
     sendFadeStepMessageDelayedLocked(getInactivityFadeDelayTimeLocked());
 
     if (isFadingLocked()) {
-        mLocked.wantVisible = true;
+        mLocked.visible = true;
         mLocked.fadeAlpha = 1;
         return true; // update required to effect the unfade
     }
@@ -501,11 +327,11 @@
 }
 
 void PointerController::fadeStepLocked() {
-    if (mLocked.wantVisible) {
+    if (mLocked.visible) {
         mLocked.fadeAlpha -= FADE_DECAY_PER_FRAME;
         if (mLocked.fadeAlpha < 0) {
             mLocked.fadeAlpha = 0;
-            mLocked.wantVisible = false;
+            mLocked.visible = false;
         } else {
             sendFadeStepMessageDelayedLocked(FADE_FRAME_INTERVAL);
         }
@@ -514,7 +340,7 @@
 }
 
 bool PointerController::isFadingLocked() {
-    return !mLocked.wantVisible || mLocked.fadeAlpha != 1;
+    return !mLocked.visible || mLocked.fadeAlpha != 1;
 }
 
 nsecs_t PointerController::getInactivityFadeDelayTimeLocked() {
diff --git a/services/input/PointerController.h b/services/input/PointerController.h
index e1dab5c..d467a5a 100644
--- a/services/input/PointerController.h
+++ b/services/input/PointerController.h
@@ -17,16 +17,14 @@
 #ifndef _UI_POINTER_CONTROLLER_H
 #define _UI_POINTER_CONTROLLER_H
 
+#include "SpriteController.h"
+
 #include <ui/DisplayInfo.h>
 #include <ui/Input.h>
 #include <utils/RefBase.h>
 #include <utils/Looper.h>
 #include <utils/String8.h>
 
-#include <surfaceflinger/Surface.h>
-#include <surfaceflinger/SurfaceComposerClient.h>
-#include <surfaceflinger/ISurfaceComposer.h>
-
 #include <SkBitmap.h>
 
 namespace android {
@@ -86,7 +84,7 @@
         INACTIVITY_FADE_DELAY_SHORT = 1,
     };
 
-    PointerController(const sp<Looper>& looper, int32_t pointerLayer);
+    PointerController(const sp<Looper>& looper, const sp<SpriteController>& spriteController);
 
     virtual bool getBounds(float* outMinX, float* outMinY,
             float* outMaxX, float* outMaxY) const;
@@ -111,9 +109,8 @@
     mutable Mutex mLock;
 
     sp<Looper> mLooper;
-    int32_t mPointerLayer;
-    sp<SurfaceComposerClient> mSurfaceComposerClient;
-    sp<SurfaceControl> mSurfaceControl;
+    sp<SpriteController> mSpriteController;
+    sp<WeakMessageHandler> mHandler;
 
     struct Locked {
         int32_t displayWidth;
@@ -124,26 +121,17 @@
         float pointerY;
         uint32_t buttonState;
 
-        SkBitmap* iconBitmap;
-        float iconHotSpotX;
-        float iconHotSpotY;
-
         float fadeAlpha;
         InactivityFadeDelay inactivityFadeDelay;
 
-        bool wantVisible;
         bool visible;
-        bool drawn;
-    } mLocked;
 
-    sp<WeakMessageHandler> mHandler;
+        sp<Sprite> sprite;
+    } mLocked;
 
     bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
     void setPositionLocked(float x, float y);
     void updateLocked();
-    bool createSurfaceIfNeededLocked();
-    bool drawPointerIfNeededLocked();
-    bool resizeSurfaceLocked(int32_t width, int32_t height);
 
     void handleMessage(const Message& message);
     bool unfadeBeforeUpdateLocked();
diff --git a/services/input/SpotController.cpp b/services/input/SpotController.cpp
new file mode 100644
index 0000000..dffad81
--- /dev/null
+++ b/services/input/SpotController.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "SpotController"
+
+//#define LOG_NDEBUG 0
+
+// Log debug messages about spot updates
+#define DEBUG_SPOT_UPDATES 0
+
+#include "SpotController.h"
+
+#include <cutils/log.h>
+
+namespace android {
+
+// --- SpotController ---
+
+SpotController::SpotController(const sp<Looper>& looper,
+        const sp<SpriteController>& spriteController) :
+        mLooper(looper), mSpriteController(spriteController) {
+    mHandler = new WeakMessageHandler(this);
+}
+
+SpotController::~SpotController() {
+    mLooper->removeMessages(mHandler);
+}
+
+void SpotController:: handleMessage(const Message& message) {
+}
+
+} // namespace android
diff --git a/services/input/SpotController.h b/services/input/SpotController.h
new file mode 100644
index 0000000..1d091d7
--- /dev/null
+++ b/services/input/SpotController.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_SPOT_CONTROLLER_H
+#define _UI_SPOT_CONTROLLER_H
+
+#include "SpriteController.h"
+
+#include <utils/RefBase.h>
+#include <utils/Looper.h>
+
+#include <SkBitmap.h>
+
+namespace android {
+
+/*
+ * Interface for displaying spots on screen that visually represent the positions
+ * of fingers on a touch pad.
+ *
+ * The spot controller is responsible for providing synchronization and for tracking
+ * display orientation changes if needed.
+ */
+class SpotControllerInterface : public virtual RefBase {
+protected:
+    SpotControllerInterface() { }
+    virtual ~SpotControllerInterface() { }
+
+public:
+
+};
+
+
+/*
+ * Sprite-based spot controller implementation.
+ */
+class SpotController : public SpotControllerInterface, public MessageHandler {
+protected:
+    virtual ~SpotController();
+
+public:
+    SpotController(const sp<Looper>& looper, const sp<SpriteController>& spriteController);
+
+private:
+    mutable Mutex mLock;
+
+    sp<Looper> mLooper;
+    sp<SpriteController> mSpriteController;
+    sp<WeakMessageHandler> mHandler;
+
+    struct Locked {
+    } mLocked;
+
+    void handleMessage(const Message& message);
+};
+
+} // namespace android
+
+#endif // _UI_SPOT_CONTROLLER_H
diff --git a/services/input/SpriteController.cpp b/services/input/SpriteController.cpp
new file mode 100644
index 0000000..c6d4390f
--- /dev/null
+++ b/services/input/SpriteController.cpp
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Sprites"
+
+//#define LOG_NDEBUG 0
+
+#include "SpriteController.h"
+
+#include <cutils/log.h>
+#include <utils/String8.h>
+
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkColor.h>
+#include <SkPaint.h>
+#include <SkXfermode.h>
+
+namespace android {
+
+// --- SpriteController ---
+
+SpriteController::SpriteController(const sp<Looper>& looper, int32_t overlayLayer) :
+        mLooper(looper), mOverlayLayer(overlayLayer) {
+    mHandler = new WeakMessageHandler(this);
+}
+
+SpriteController::~SpriteController() {
+    mLooper->removeMessages(mHandler);
+
+    if (mSurfaceComposerClient != NULL) {
+        mSurfaceComposerClient->dispose();
+        mSurfaceComposerClient.clear();
+    }
+}
+
+sp<Sprite> SpriteController::createSprite() {
+    return new SpriteImpl(this);
+}
+
+void SpriteController::invalidateSpriteLocked(const sp<SpriteImpl>& sprite) {
+    bool wasEmpty = mInvalidatedSprites.isEmpty();
+    mInvalidatedSprites.push(sprite);
+    if (wasEmpty) {
+        mLooper->sendMessage(mHandler, Message(MSG_UPDATE_SPRITES));
+    }
+}
+
+void SpriteController::disposeSurfaceLocked(const sp<SurfaceControl>& surfaceControl) {
+    bool wasEmpty = mDisposedSurfaces.isEmpty();
+    mDisposedSurfaces.push(surfaceControl);
+    if (wasEmpty) {
+        mLooper->sendMessage(mHandler, Message(MSG_DISPOSE_SURFACES));
+    }
+}
+
+void SpriteController::handleMessage(const Message& message) {
+    switch (message.what) {
+    case MSG_UPDATE_SPRITES:
+        doUpdateSprites();
+        break;
+    case MSG_DISPOSE_SURFACES:
+        doDisposeSurfaces();
+        break;
+    }
+}
+
+void SpriteController::doUpdateSprites() {
+    // Collect information about sprite updates.
+    // Each sprite update record includes a reference to its associated sprite so we can
+    // be certain the sprites will not be deleted while this function runs.  Sprites
+    // may invalidate themselves again during this time but we will handle those changes
+    // in the next iteration.
+    Vector<SpriteUpdate> updates;
+    size_t numSprites;
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        numSprites = mInvalidatedSprites.size();
+        for (size_t i = 0; i < numSprites; i++) {
+            const sp<SpriteImpl>& sprite = mInvalidatedSprites.itemAt(i);
+
+            updates.push(SpriteUpdate(sprite, sprite->getStateLocked()));
+            sprite->resetDirtyLocked();
+        }
+        mInvalidatedSprites.clear();
+    } // release lock
+
+    // Create missing surfaces.
+    bool surfaceChanged = false;
+    for (size_t i = 0; i < numSprites; i++) {
+        SpriteUpdate& update = updates.editItemAt(i);
+
+        if (update.state.surfaceControl == NULL && update.state.wantSurfaceVisible()) {
+            update.state.surfaceWidth = update.state.bitmap.width();
+            update.state.surfaceHeight = update.state.bitmap.height();
+            update.state.surfaceDrawn = false;
+            update.state.surfaceVisible = false;
+            update.state.surfaceControl = obtainSurface(
+                    update.state.surfaceWidth, update.state.surfaceHeight);
+            if (update.state.surfaceControl != NULL) {
+                update.surfaceChanged = surfaceChanged = true;
+            }
+        }
+    }
+
+    // Resize sprites if needed, inside a global transaction.
+    bool haveGlobalTransaction = false;
+    for (size_t i = 0; i < numSprites; i++) {
+        SpriteUpdate& update = updates.editItemAt(i);
+
+        if (update.state.surfaceControl != NULL && update.state.wantSurfaceVisible()) {
+            int32_t desiredWidth = update.state.bitmap.width();
+            int32_t desiredHeight = update.state.bitmap.height();
+            if (update.state.surfaceWidth < desiredWidth
+                    || update.state.surfaceHeight < desiredHeight) {
+                if (!haveGlobalTransaction) {
+                    SurfaceComposerClient::openGlobalTransaction();
+                    haveGlobalTransaction = true;
+                }
+
+                status_t status = update.state.surfaceControl->setSize(desiredWidth, desiredHeight);
+                if (status) {
+                    LOGE("Error %d resizing sprite surface from %dx%d to %dx%d",
+                            status, update.state.surfaceWidth, update.state.surfaceHeight,
+                            desiredWidth, desiredHeight);
+                } else {
+                    update.state.surfaceWidth = desiredWidth;
+                    update.state.surfaceHeight = desiredHeight;
+                    update.state.surfaceDrawn = false;
+                    update.surfaceChanged = surfaceChanged = true;
+
+                    if (update.state.surfaceVisible) {
+                        status = update.state.surfaceControl->hide();
+                        if (status) {
+                            LOGE("Error %d hiding sprite surface after resize.", status);
+                        } else {
+                            update.state.surfaceVisible = false;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    if (haveGlobalTransaction) {
+        SurfaceComposerClient::closeGlobalTransaction();
+    }
+
+    // Redraw sprites if needed.
+    for (size_t i = 0; i < numSprites; i++) {
+        SpriteUpdate& update = updates.editItemAt(i);
+
+        if ((update.state.dirty & DIRTY_BITMAP) && update.state.surfaceDrawn) {
+            update.state.surfaceDrawn = false;
+            update.surfaceChanged = surfaceChanged = true;
+        }
+
+        if (update.state.surfaceControl != NULL && !update.state.surfaceDrawn
+                && update.state.wantSurfaceVisible()) {
+            sp<Surface> surface = update.state.surfaceControl->getSurface();
+            Surface::SurfaceInfo surfaceInfo;
+            status_t status = surface->lock(&surfaceInfo);
+            if (status) {
+                LOGE("Error %d locking sprite surface before drawing.", status);
+            } else {
+                SkBitmap surfaceBitmap;
+                ssize_t bpr = surfaceInfo.s * bytesPerPixel(surfaceInfo.format);
+                surfaceBitmap.setConfig(SkBitmap::kARGB_8888_Config,
+                        surfaceInfo.w, surfaceInfo.h, bpr);
+                surfaceBitmap.setPixels(surfaceInfo.bits);
+
+                SkCanvas surfaceCanvas;
+                surfaceCanvas.setBitmapDevice(surfaceBitmap);
+
+                SkPaint paint;
+                paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+                surfaceCanvas.drawBitmap(update.state.bitmap, 0, 0, &paint);
+
+                if (surfaceInfo.w > uint32_t(update.state.bitmap.width())) {
+                    paint.setColor(0); // transparent fill color
+                    surfaceCanvas.drawRectCoords(update.state.bitmap.width(), 0,
+                            surfaceInfo.w, update.state.bitmap.height(), paint);
+                }
+                if (surfaceInfo.h > uint32_t(update.state.bitmap.height())) {
+                    paint.setColor(0); // transparent fill color
+                    surfaceCanvas.drawRectCoords(0, update.state.bitmap.height(),
+                            surfaceInfo.w, surfaceInfo.h, paint);
+                }
+
+                status = surface->unlockAndPost();
+                if (status) {
+                    LOGE("Error %d unlocking and posting sprite surface after drawing.", status);
+                } else {
+                    update.state.surfaceDrawn = true;
+                    update.surfaceChanged = surfaceChanged = true;
+                }
+            }
+        }
+    }
+
+    // Set sprite surface properties and make them visible.
+    bool haveTransaction = false;
+    for (size_t i = 0; i < numSprites; i++) {
+        SpriteUpdate& update = updates.editItemAt(i);
+
+        bool wantSurfaceVisibleAndDrawn = update.state.wantSurfaceVisible()
+                && update.state.surfaceDrawn;
+        bool becomingVisible = wantSurfaceVisibleAndDrawn && !update.state.surfaceVisible;
+        bool becomingHidden = !wantSurfaceVisibleAndDrawn && update.state.surfaceVisible;
+        if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden
+                || (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
+                        | DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
+                        | DIRTY_VISIBILITY | DIRTY_HOTSPOT))))) {
+            status_t status;
+            if (!haveTransaction) {
+                status = mSurfaceComposerClient->openTransaction();
+                if (status) {
+                    LOGE("Error %d opening transation to update sprite surface.", status);
+                    break;
+                }
+                haveTransaction = true;
+            }
+
+            if (wantSurfaceVisibleAndDrawn
+                    && (becomingVisible || (update.state.dirty & DIRTY_ALPHA))) {
+                status = update.state.surfaceControl->setAlpha(update.state.alpha);
+                if (status) {
+                    LOGE("Error %d setting sprite surface alpha.", status);
+                }
+            }
+
+            if (wantSurfaceVisibleAndDrawn
+                    && (becomingVisible || (update.state.dirty & (DIRTY_POSITION
+                            | DIRTY_HOTSPOT)))) {
+                status = update.state.surfaceControl->setPosition(
+                        update.state.positionX - update.state.hotSpotX,
+                        update.state.positionY - update.state.hotSpotY);
+                if (status) {
+                    LOGE("Error %d setting sprite surface position.", status);
+                }
+            }
+
+            if (wantSurfaceVisibleAndDrawn
+                    && (becomingVisible
+                            || (update.state.dirty & DIRTY_TRANSFORMATION_MATRIX))) {
+                status = update.state.surfaceControl->setMatrix(
+                        update.state.transformationMatrix.dsdx,
+                        update.state.transformationMatrix.dtdx,
+                        update.state.transformationMatrix.dsdy,
+                        update.state.transformationMatrix.dtdy);
+                if (status) {
+                    LOGE("Error %d setting sprite surface transformation matrix.", status);
+                }
+            }
+
+            int32_t surfaceLayer = mOverlayLayer + update.state.layer;
+            if (wantSurfaceVisibleAndDrawn
+                    && (becomingVisible || (update.state.dirty & DIRTY_LAYER))) {
+                status = update.state.surfaceControl->setLayer(surfaceLayer);
+                if (status) {
+                    LOGE("Error %d setting sprite surface layer.", status);
+                }
+            }
+
+            if (becomingVisible) {
+                status = update.state.surfaceControl->show(surfaceLayer);
+                if (status) {
+                    LOGE("Error %d showing sprite surface.", status);
+                } else {
+                    update.state.surfaceVisible = true;
+                    update.surfaceChanged = surfaceChanged = true;
+                }
+            } else if (becomingHidden) {
+                status = update.state.surfaceControl->hide();
+                if (status) {
+                    LOGE("Error %d hiding sprite surface.", status);
+                } else {
+                    update.state.surfaceVisible = false;
+                    update.surfaceChanged = surfaceChanged = true;
+                }
+            }
+        }
+    }
+
+    if (haveTransaction) {
+        status_t status = mSurfaceComposerClient->closeTransaction();
+        if (status) {
+            LOGE("Error %d closing transaction to update sprite surface.", status);
+        }
+    }
+
+    // If any surfaces were changed, write back the new surface properties to the sprites.
+    if (surfaceChanged) { // acquire lock
+        AutoMutex _l(mLock);
+
+        for (size_t i = 0; i < numSprites; i++) {
+            const SpriteUpdate& update = updates.itemAt(i);
+
+            if (update.surfaceChanged) {
+                update.sprite->setSurfaceLocked(update.state.surfaceControl,
+                        update.state.surfaceWidth, update.state.surfaceHeight,
+                        update.state.surfaceDrawn, update.state.surfaceVisible);
+            }
+        }
+    } // release lock
+
+    // Clear the sprite update vector outside the lock.  It is very important that
+    // we do not clear sprite references inside the lock since we could be releasing
+    // the last remaining reference to the sprite here which would result in the
+    // sprite being deleted and the lock being reacquired by the sprite destructor
+    // while already held.
+    updates.clear();
+}
+
+void SpriteController::doDisposeSurfaces() {
+    // Collect disposed surfaces.
+    Vector<sp<SurfaceControl> > disposedSurfaces;
+    { // acquire lock
+        disposedSurfaces = mDisposedSurfaces;
+        mDisposedSurfaces.clear();
+    } // release lock
+
+    // Release the last reference to each surface outside of the lock.
+    // We don't want the surfaces to be deleted while we are holding our lock.
+    disposedSurfaces.clear();
+}
+
+void SpriteController::ensureSurfaceComposerClient() {
+    if (mSurfaceComposerClient == NULL) {
+        mSurfaceComposerClient = new SurfaceComposerClient();
+    }
+}
+
+sp<SurfaceControl> SpriteController::obtainSurface(int32_t width, int32_t height) {
+    ensureSurfaceComposerClient();
+
+    sp<SurfaceControl> surfaceControl = mSurfaceComposerClient->createSurface(
+            getpid(), String8("Sprite"), 0, width, height, PIXEL_FORMAT_RGBA_8888);
+    if (surfaceControl == NULL) {
+        LOGE("Error creating sprite surface.");
+        return NULL;
+    }
+    return surfaceControl;
+}
+
+
+// --- SpriteController::SpriteImpl ---
+
+SpriteController::SpriteImpl::SpriteImpl(const sp<SpriteController> controller) :
+        mController(controller), mTransactionNestingCount(0) {
+}
+
+SpriteController::SpriteImpl::~SpriteImpl() {
+    AutoMutex _m(mController->mLock);
+
+    // Let the controller take care of deleting the last reference to sprite
+    // surfaces so that we do not block the caller on an IPC here.
+    if (mState.surfaceControl != NULL) {
+        mController->disposeSurfaceLocked(mState.surfaceControl);
+        mState.surfaceControl.clear();
+    }
+}
+
+void SpriteController::SpriteImpl::setBitmap(const SkBitmap* bitmap,
+        float hotSpotX, float hotSpotY) {
+    AutoMutex _l(mController->mLock);
+
+    if (bitmap) {
+        bitmap->copyTo(&mState.bitmap, SkBitmap::kARGB_8888_Config);
+    } else {
+        mState.bitmap.reset();
+    }
+
+    uint32_t dirty = DIRTY_BITMAP;
+    if (mState.hotSpotX != hotSpotX || mState.hotSpotY != hotSpotY) {
+        mState.hotSpotX = hotSpotX;
+        mState.hotSpotY = hotSpotY;
+        dirty |= DIRTY_HOTSPOT;
+    }
+
+    invalidateLocked(dirty);
+}
+
+void SpriteController::SpriteImpl::setVisible(bool visible) {
+    AutoMutex _l(mController->mLock);
+
+    if (mState.visible != visible) {
+        mState.visible = visible;
+        invalidateLocked(DIRTY_VISIBILITY);
+    }
+}
+
+void SpriteController::SpriteImpl::setPosition(float x, float y) {
+    AutoMutex _l(mController->mLock);
+
+    if (mState.positionX != x || mState.positionY != y) {
+        mState.positionX = x;
+        mState.positionY = y;
+        invalidateLocked(DIRTY_POSITION);
+    }
+}
+
+void SpriteController::SpriteImpl::setLayer(int32_t layer) {
+    AutoMutex _l(mController->mLock);
+
+    if (mState.layer != layer) {
+        mState.layer = layer;
+        invalidateLocked(DIRTY_LAYER);
+    }
+}
+
+void SpriteController::SpriteImpl::setAlpha(float alpha) {
+    AutoMutex _l(mController->mLock);
+
+    if (mState.alpha != alpha) {
+        mState.alpha = alpha;
+        invalidateLocked(DIRTY_ALPHA);
+    }
+}
+
+void SpriteController::SpriteImpl::setTransformationMatrix(
+        const SpriteTransformationMatrix& matrix) {
+    AutoMutex _l(mController->mLock);
+
+    if (mState.transformationMatrix != matrix) {
+        mState.transformationMatrix = matrix;
+        invalidateLocked(DIRTY_TRANSFORMATION_MATRIX);
+    }
+}
+
+void SpriteController::SpriteImpl::openTransaction() {
+    AutoMutex _l(mController->mLock);
+
+    mTransactionNestingCount += 1;
+}
+
+void SpriteController::SpriteImpl::closeTransaction() {
+    AutoMutex _l(mController->mLock);
+
+    LOG_ALWAYS_FATAL_IF(mTransactionNestingCount == 0,
+            "Sprite closeTransaction() called but there is no open sprite transaction");
+
+    mTransactionNestingCount -= 1;
+    if (mTransactionNestingCount == 0 && mState.dirty) {
+        mController->invalidateSpriteLocked(this);
+    }
+}
+
+void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) {
+    if (mTransactionNestingCount > 0) {
+        bool wasDirty = mState.dirty;
+        mState.dirty |= dirty;
+        if (!wasDirty) {
+            mController->invalidateSpriteLocked(this);
+        }
+    }
+}
+
+} // namespace android
diff --git a/services/input/SpriteController.h b/services/input/SpriteController.h
new file mode 100644
index 0000000..27afb5e
--- /dev/null
+++ b/services/input/SpriteController.h
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_SPRITES_H
+#define _UI_SPRITES_H
+
+#include <utils/RefBase.h>
+#include <utils/Looper.h>
+
+#include <surfaceflinger/Surface.h>
+#include <surfaceflinger/SurfaceComposerClient.h>
+#include <surfaceflinger/ISurfaceComposer.h>
+
+#include <SkBitmap.h>
+
+namespace android {
+
+/*
+ * Transformation matrix for a sprite.
+ */
+struct SpriteTransformationMatrix {
+    inline SpriteTransformationMatrix() : dsdx(1.0f), dtdx(0.0f), dsdy(0.0f), dtdy(1.0f) { }
+
+    float dsdx;
+    float dtdx;
+    float dsdy;
+    float dtdy;
+
+    inline bool operator== (const SpriteTransformationMatrix& other) {
+        return dsdx == other.dsdx
+                && dtdx == other.dtdx
+                && dsdy == other.dsdy
+                && dtdy == other.dtdy;
+    }
+
+    inline bool operator!= (const SpriteTransformationMatrix& other) {
+        return !(*this == other);
+    }
+};
+
+/*
+ * A sprite is a simple graphical object that is displayed on-screen above other layers.
+ * The basic sprite class is an interface.
+ * The implementation is provided by the sprite controller.
+ */
+class Sprite : public RefBase {
+protected:
+    Sprite() { }
+    virtual ~Sprite() { }
+
+public:
+    /* Sets the bitmap that is drawn by the sprite.
+     * The sprite retains a copy of the bitmap for subsequent rendering. */
+    virtual void setBitmap(const SkBitmap* bitmap, float hotSpotX, float hotSpotY) = 0;
+
+    /* Sets whether the sprite is visible. */
+    virtual void setVisible(bool visible) = 0;
+
+    /* Sets the sprite position on screen, relative to the sprite's hot spot. */
+    virtual void setPosition(float x, float y) = 0;
+
+    /* Sets the layer of the sprite, relative to the system sprite overlay layer.
+     * Layer 0 is the overlay layer, > 0 appear above this layer. */
+    virtual void setLayer(int32_t layer) = 0;
+
+    /* Sets the sprite alpha blend ratio between 0.0 and 1.0. */
+    virtual void setAlpha(float alpha) = 0;
+
+    /* Sets the sprite transformation matrix. */
+    virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix) = 0;
+
+    /* Opens or closes a transaction to perform a batch of sprite updates as part of
+     * a single operation such as setPosition and setAlpha.  It is not necessary to
+     * open a transaction when updating a single property.
+     * Calls to openTransaction() nest and must be matched by an equal number
+     * of calls to closeTransaction(). */
+    virtual void openTransaction() = 0;
+    virtual void closeTransaction() = 0;
+};
+
+/*
+ * Displays sprites on the screen.
+ *
+ * This interface is used by PointerController and SpotController to draw pointers or
+ * spot representations of fingers.  It is not intended for general purpose use
+ * by other components.
+ *
+ * All sprite position updates and rendering is performed asynchronously.
+ *
+ * Clients are responsible for animating sprites by periodically updating their properties.
+ */
+class SpriteController : public MessageHandler {
+protected:
+    virtual ~SpriteController();
+
+public:
+    SpriteController(const sp<Looper>& looper, int32_t overlayLayer);
+
+    /* Creates a new sprite, initially invisible. */
+    sp<Sprite> createSprite();
+
+private:
+    enum {
+        MSG_UPDATE_SPRITES,
+        MSG_DISPOSE_SURFACES,
+    };
+
+    enum {
+        DIRTY_BITMAP = 1 << 0,
+        DIRTY_ALPHA = 1 << 1,
+        DIRTY_POSITION = 1 << 2,
+        DIRTY_TRANSFORMATION_MATRIX = 1 << 3,
+        DIRTY_LAYER = 1 << 4,
+        DIRTY_VISIBILITY = 1 << 5,
+        DIRTY_HOTSPOT = 1 << 6,
+    };
+
+    /* Describes the state of a sprite.
+     * This structure is designed so that it can be copied during updates so that
+     * surfaces can be resized and redrawn without blocking the client by holding a lock
+     * on the sprites for a long time.
+     * Note that the SkBitmap holds a reference to a shared (and immutable) pixel ref. */
+    struct SpriteState {
+        inline SpriteState() :
+                dirty(0), hotSpotX(0), hotSpotY(0), visible(false),
+                positionX(0), positionY(0), layer(0), alpha(1.0f),
+                surfaceWidth(0), surfaceHeight(0), surfaceDrawn(false), surfaceVisible(false) {
+        }
+
+        uint32_t dirty;
+
+        SkBitmap bitmap;
+        float hotSpotX;
+        float hotSpotY;
+        bool visible;
+        float positionX;
+        float positionY;
+        int32_t layer;
+        float alpha;
+        SpriteTransformationMatrix transformationMatrix;
+
+        sp<SurfaceControl> surfaceControl;
+        int32_t surfaceWidth;
+        int32_t surfaceHeight;
+        bool surfaceDrawn;
+        bool surfaceVisible;
+
+        inline bool wantSurfaceVisible() const {
+            return visible && alpha > 0.0f && !bitmap.isNull() && !bitmap.empty();
+        }
+    };
+
+    /* Client interface for a sprite.
+     * Requests acquire a lock on the controller, update local state and request the
+     * controller to invalidate the sprite.
+     * The real heavy lifting of creating, resizing and redrawing surfaces happens
+     * asynchronously with no locks held except in short critical section to copy
+     * the sprite state before the work and update the sprite surface control afterwards.
+     */
+    class SpriteImpl : public Sprite {
+    protected:
+        virtual ~SpriteImpl();
+
+    public:
+        SpriteImpl(const sp<SpriteController> controller);
+
+        virtual void setBitmap(const SkBitmap* bitmap, float hotSpotX, float hotSpotY);
+        virtual void setVisible(bool visible);
+        virtual void setPosition(float x, float y);
+        virtual void setLayer(int32_t layer);
+        virtual void setAlpha(float alpha);
+        virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix);
+        virtual void openTransaction();
+        virtual void closeTransaction();
+
+        inline const SpriteState& getStateLocked() const {
+            return mState;
+        }
+
+        inline void resetDirtyLocked() {
+            mState.dirty = 0;
+        }
+
+        inline void setSurfaceLocked(const sp<SurfaceControl>& surfaceControl,
+                int32_t width, int32_t height, bool drawn, bool visible) {
+            mState.surfaceControl = surfaceControl;
+            mState.surfaceWidth = width;
+            mState.surfaceHeight = height;
+            mState.surfaceDrawn = drawn;
+            mState.surfaceVisible = visible;
+        }
+
+    private:
+        sp<SpriteController> mController;
+
+        SpriteState mState; // guarded by mController->mLock
+        uint32_t mTransactionNestingCount; // guarded by mController->mLock
+
+        void invalidateLocked(uint32_t dirty);
+    };
+
+    /* Stores temporary information collected during the sprite update cycle. */
+    struct SpriteUpdate {
+        inline SpriteUpdate() : surfaceChanged(false) { }
+        inline SpriteUpdate(const sp<SpriteImpl> sprite, const SpriteState& state) :
+                sprite(sprite), state(state), surfaceChanged(false) {
+        }
+
+        sp<SpriteImpl> sprite;
+        SpriteState state;
+        bool surfaceChanged;
+    };
+
+    mutable Mutex mLock;
+
+    sp<Looper> mLooper;
+    const int32_t mOverlayLayer;
+    sp<WeakMessageHandler> mHandler;
+
+    sp<SurfaceComposerClient> mSurfaceComposerClient;
+
+    Vector<sp<SpriteImpl> > mInvalidatedSprites; // guarded by mLock
+    Vector<sp<SurfaceControl> > mDisposedSurfaces; // guarded by mLock
+
+    void invalidateSpriteLocked(const sp<SpriteImpl>& sprite);
+    void disposeSurfaceLocked(const sp<SurfaceControl>& surfaceControl);
+
+    void handleMessage(const Message& message);
+    void doUpdateSprites();
+    void doDisposeSurfaces();
+
+    void ensureSurfaceComposerClient();
+    sp<SurfaceControl> obtainSurface(int32_t width, int32_t height);
+};
+
+} // namespace android
+
+#endif // _UI_SPRITES_H
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index 4c5f239..ba8ca9c 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -192,6 +192,10 @@
     virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) {
         return mPointerControllers.valueFor(deviceId);
     }
+
+    virtual sp<SpotControllerInterface> obtainSpotController(int32_t device) {
+        return NULL;
+    }
 };
 
 
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 1455764..7028772 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -1399,6 +1399,24 @@
         }
     }
 
+    public InputMethodSubtype getLastInputMethodSubtype() {
+        synchronized (mMethodMap) {
+            final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked();
+            // TODO: Handle the case of the last IME with no subtypes
+            if (lastIme == null || TextUtils.isEmpty(lastIme.first)
+                    || TextUtils.isEmpty(lastIme.second)) return null;
+            final InputMethodInfo lastImi = mMethodMap.get(lastIme.first);
+            if (lastImi == null) return null;
+            try {
+                final int lastSubtypeHash = Integer.valueOf(lastIme.second);
+                return lastImi.getSubtypeAt(getSubtypeIdFromHashCode(
+                        lastImi, lastSubtypeHash));
+            } catch (NumberFormatException e) {
+                return null;
+            }
+        }
+    }
+
     private void setInputMethodWithSubtypeId(IBinder token, String id, int subtypeId) {
         synchronized (mMethodMap) {
             if (token == null) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index f44b889..31b7f86 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -441,8 +441,7 @@
     /**
      * List of intents that were used to start the most recent tasks.
      */
-    final ArrayList<TaskRecord> mRecentTasks
-            = new ArrayList<TaskRecord>();
+    final ArrayList<TaskRecord> mRecentTasks = new ArrayList<TaskRecord>();
 
     /**
      * All of the applications we currently have running organized by name.
@@ -450,8 +449,7 @@
      * returned by the package manager), and the keys are ApplicationRecord
      * objects.
      */
-    final ProcessMap<ProcessRecord> mProcessNames
-            = new ProcessMap<ProcessRecord>();
+    final ProcessMap<ProcessRecord> mProcessNames = new ProcessMap<ProcessRecord>();
 
     /**
      * The currently running heavy-weight process, if any.
@@ -480,8 +478,7 @@
      * <p>NOTE: This object is protected by its own lock, NOT the global
      * activity manager lock!
      */
-    final SparseArray<ProcessRecord> mPidsSelfLocked
-            = new SparseArray<ProcessRecord>();
+    final SparseArray<ProcessRecord> mPidsSelfLocked = new SparseArray<ProcessRecord>();
 
     /**
      * All of the processes that have been forced to be foreground.  The key
@@ -2981,7 +2978,10 @@
         
         synchronized (this) {
             if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) {
-                Process.killProcess(app.pid);
+                Slog.w(TAG, "Killing " + app + ": background ANR");
+                EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+                        app.processName, app.setAdj, "background ANR");
+                Process.killProcessQuiet(app.pid);
                 return;
             }
     
@@ -3446,7 +3446,9 @@
                     bringDownServiceLocked(sr, true);
                 }
             }
-            Process.killProcess(pid);
+            EventLog.writeEvent(EventLogTags.AM_KILL, pid,
+                    app.processName, app.setAdj, "start timeout");
+            Process.killProcessQuiet(pid);
             if (mBackupTarget != null && mBackupTarget.app.pid == pid) {
                 Slog.w(TAG, "Unattached app died before backup, skipping");
                 try {
@@ -3492,7 +3494,7 @@
                     + " (IApplicationThread " + thread + "); dropping process");
             EventLog.writeEvent(EventLogTags.AM_DROP_PROCESS, pid);
             if (pid > 0 && pid != MY_PID) {
-                Process.killProcess(pid);
+                Process.killProcessQuiet(pid);
             } else {
                 try {
                     thread.scheduleExit();
@@ -3890,18 +3892,17 @@
             }
             for (int i=0; i<intents.length; i++) {
                 Intent intent = intents[i];
-                if (intent == null) {
-                    throw new IllegalArgumentException("Null intent at index " + i);
+                if (intent != null) {
+                    if (intent.hasFileDescriptors()) {
+                        throw new IllegalArgumentException("File descriptors passed in Intent");
+                    }
+                    if (type == INTENT_SENDER_BROADCAST &&
+                            (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
+                        throw new IllegalArgumentException(
+                                "Can't use FLAG_RECEIVER_BOOT_UPGRADE here");
+                    }
+                    intents[i] = new Intent(intent);
                 }
-                if (intent.hasFileDescriptors()) {
-                    throw new IllegalArgumentException("File descriptors passed in Intent");
-                }
-                if (type == INTENT_SENDER_BROADCAST &&
-                        (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
-                    throw new IllegalArgumentException(
-                            "Can't use FLAG_RECEIVER_BOOT_UPGRADE here");
-                }
-                intents[i] = new Intent(intent);
             }
             if (resolvedTypes != null && resolvedTypes.length != intents.length) {
                 throw new IllegalArgumentException(
@@ -4832,11 +4833,6 @@
                 throw new SecurityException(msg);
             }
 
-            final boolean canReadFb = (flags&ActivityManager.TASKS_GET_THUMBNAILS) != 0
-                    && checkCallingPermission(
-                            android.Manifest.permission.READ_FRAME_BUFFER)
-                            == PackageManager.PERMISSION_GRANTED;
-
             int pos = mMainStack.mHistory.size()-1;
             ActivityRecord next =
                 pos >= 0 ? (ActivityRecord)mMainStack.mHistory.get(pos) : null;
@@ -4876,13 +4872,6 @@
                     ci.id = curTask.taskId;
                     ci.baseActivity = r.intent.getComponent();
                     ci.topActivity = top.intent.getComponent();
-                    if (canReadFb) {
-                        if (top.state == ActivityState.RESUMED) {
-                            ci.thumbnail = top.stack.screenshotActivities(top);
-                        } else if (top.thumbHolder != null) {
-                            ci.thumbnail = top.thumbHolder.lastThumbnail;
-                        }
-                    }
                     if (top.thumbHolder != null) {
                         ci.description = top.thumbHolder.lastDescription;
                     }
@@ -4955,8 +4944,6 @@
 
             IPackageManager pm = AppGlobals.getPackageManager();
             
-            ActivityRecord resumed = mMainStack.mResumedActivity;
-            
             final int N = mRecentTasks.size();
             ArrayList<ActivityManager.RecentTaskInfo> res
                     = new ArrayList<ActivityManager.RecentTaskInfo>(
@@ -5002,75 +4989,122 @@
         }
     }
 
-    public ActivityManager.TaskThumbnails getTaskThumbnails(int id) {
-        synchronized (this) {
-            enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER,
-                    "getTaskThumbnail()");
-            ActivityRecord resumed = mMainStack.mResumedActivity;
-            final int N = mRecentTasks.size();
-            for (int i=0; i<N; i++) {
-                TaskRecord tr = mRecentTasks.get(i);
-                if (tr.taskId == id) {
-                    final ActivityManager.TaskThumbnails thumbs
-                            = new ActivityManager.TaskThumbnails();
-                    if (resumed != null && resumed.thumbHolder == tr) {
-                        thumbs.mainThumbnail = resumed.stack.screenshotActivities(resumed);
-                    } else {
-                        thumbs.mainThumbnail = tr.lastThumbnail;
-                    }
-                    // How many different sub-thumbnails?
-                    final int NA = mMainStack.mHistory.size();
-                    int j = 0;
-                    ThumbnailHolder holder = null;
-                    while (j < NA) {
-                        ActivityRecord ar = (ActivityRecord)mMainStack.mHistory.get(j);
-                        j++;
-                        if (ar.task == tr) {
-                            holder = ar.thumbHolder;
-                            break;
-                        }
-                    }
-                    ArrayList<Bitmap> bitmaps = new ArrayList<Bitmap>();
-                    thumbs.otherThumbnails = bitmaps;
-                    ActivityRecord lastActivity = null;
-                    while (j < NA) {
-                        ActivityRecord ar = (ActivityRecord)mMainStack.mHistory.get(j);
-                        j++;
-                        if (ar.task != tr) {
-                            break;
-                        }
-                        lastActivity = ar;
-                        if (ar.thumbHolder != holder && holder != null) {
-                            thumbs.numSubThumbbails++;
-                            holder = ar.thumbHolder;
-                            bitmaps.add(holder.lastThumbnail);
-                        }
-                    }
-                    if (lastActivity != null && bitmaps.size() > 0) {
-                        if (resumed == lastActivity) {
-                            Bitmap bm = lastActivity.stack.screenshotActivities(lastActivity);
-                            if (bm != null) {
-                                bitmaps.remove(bitmaps.size()-1);
-                                bitmaps.add(bm);
-                            }
-                        }
-                    }
-                    if (thumbs.numSubThumbbails > 0) {
-                        thumbs.retriever = new IThumbnailRetriever.Stub() {
-                            public Bitmap getThumbnail(int index) {
-                                if (index < 0 || index >= thumbs.otherThumbnails.size()) {
-                                    return null;
-                                }
-                                return thumbs.otherThumbnails.get(index);
-                            }
-                        };
-                    }
-                    return thumbs;
-                }
+    private TaskRecord taskForIdLocked(int id) {
+        final int N = mRecentTasks.size();
+        for (int i=0; i<N; i++) {
+            TaskRecord tr = mRecentTasks.get(i);
+            if (tr.taskId == id) {
+                return tr;
             }
         }
         return null;
     }
+
+    public ActivityManager.TaskThumbnails getTaskThumbnails(int id) {
+        synchronized (this) {
+            enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER,
+                    "getTaskThumbnails()");
+            TaskRecord tr = taskForIdLocked(id);
+            if (tr != null) {
+                return mMainStack.getTaskThumbnailsLocked(tr);
+            }
+        }
+        return null;
+    }
+
+    public boolean removeSubTask(int taskId, int subTaskIndex) {
+        synchronized (this) {
+            enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS,
+                    "removeSubTask()");
+            long ident = Binder.clearCallingIdentity();
+            try {
+                return mMainStack.removeTaskActivitiesLocked(taskId, subTaskIndex) != null;
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    private void removeTaskProcessesLocked(ActivityRecord root) {
+        TaskRecord tr = root.task;
+        Intent baseIntent = new Intent(
+                tr.intent != null ? tr.intent : tr.affinityIntent);
+        ComponentName component = baseIntent.getComponent();
+        if (component == null) {
+            Slog.w(TAG, "Now component for base intent of task: " + tr);
+            return;
+        }
+
+        // Find any running services associated with this app.
+        ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
+        for (ServiceRecord sr : mServices.values()) {
+            if (sr.packageName.equals(component.getPackageName())) {
+                services.add(sr);
+            }
+        }
+
+        // Take care of any running services associated with the app.
+        for (int i=0; i<services.size(); i++) {
+            ServiceRecord sr = services.get(i);
+            if (sr.startRequested) {
+                if ((sr.serviceInfo.flags&ServiceInfo.FLAG_STOP_WITH_TASK) != 0) {
+                    stopServiceLocked(sr);
+                } else {
+                    sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true,
+                            sr.makeNextStartId(), baseIntent, -1));
+                    if (sr.app != null && sr.app.thread != null) {
+                        sendServiceArgsLocked(sr, false);
+                    }
+                }
+            }
+        }
+
+        // Find any running processes associated with this app.
+        ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>();
+        SparseArray<ProcessRecord> appProcs
+                = mProcessNames.getMap().get(component.getPackageName());
+        if (appProcs != null) {
+            for (int i=0; i<appProcs.size(); i++) {
+                procs.add(appProcs.valueAt(i));
+            }
+        }
+
+        // Kill the running processes.
+        for (int i=0; i<procs.size(); i++) {
+            ProcessRecord pr = procs.get(i);
+            if (pr.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) {
+                Slog.i(TAG, "Killing " + pr + ": remove task");
+                EventLog.writeEvent(EventLogTags.AM_KILL, pr.pid,
+                        pr.processName, pr.setAdj, "remove task");
+                Process.killProcessQuiet(pr.pid);
+            } else {
+                pr.waitingToKill = "remove task";
+            }
+        }
+    }
+
+    public boolean removeTask(int taskId, int flags) {
+        synchronized (this) {
+            enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS,
+                    "removeTask()");
+            long ident = Binder.clearCallingIdentity();
+            try {
+                ActivityRecord r = mMainStack.removeTaskActivitiesLocked(taskId, -1);
+                if (r != null) {
+                    mRecentTasks.remove(r.task);
+
+                    if ((flags&ActivityManager.REMOVE_TASK_KILL_PROCESS) != 0) {
+                        removeTaskProcessesLocked(r);
+                    }
+
+                    return true;
+                }
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+        return false;
+    }
     
     private final int findAffinityTaskTopLocked(int startIndex, String affinity) {
         int j;
@@ -5123,21 +5157,18 @@
             }
             final long origId = Binder.clearCallingIdentity();
             try {
-                int N = mRecentTasks.size();
-                for (int i=0; i<N; i++) {
-                    TaskRecord tr = mRecentTasks.get(i);
-                    if (tr.taskId == task) {
-                        if ((flags&ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
-                            mMainStack.mUserLeaving = true;
-                        }
-                        if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
-                            // Caller wants the home activity moved with it.  To accomplish this,
-                            // we'll just move the home task to the top first.
-                            mMainStack.moveHomeToFrontLocked();
-                        }
-                        mMainStack.moveTaskToFrontLocked(tr, null);
-                        return;
+                TaskRecord tr = taskForIdLocked(task);
+                if (tr != null) {
+                    if ((flags&ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
+                        mMainStack.mUserLeaving = true;
                     }
+                    if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
+                        // Caller wants the home activity moved with it.  To accomplish this,
+                        // we'll just move the home task to the top first.
+                        mMainStack.moveHomeToFrontLocked();
+                    }
+                    mMainStack.moveTaskToFrontLocked(tr, null);
+                    return;
                 }
                 for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
                     ActivityRecord hr = (ActivityRecord)mMainStack.mHistory.get(i);
@@ -6661,11 +6692,10 @@
             }
             if (app.pid > 0 && app.pid != MY_PID) {
                 handleAppCrashLocked(app);
-                Slog.i(ActivityManagerService.TAG, "Killing "
-                        + app.processName + " (pid=" + app.pid + "): user's request");
+                Slog.i(ActivityManagerService.TAG, "Killing " + app + ": user's request");
                 EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
                         app.processName, app.setAdj, "user's request after error");
-                Process.killProcess(app.pid);
+                Process.killProcessQuiet(app.pid);
             }
         }
     }
@@ -8946,7 +8976,7 @@
                         + " in dying process " + proc.processName);
                 EventLog.writeEvent(EventLogTags.AM_KILL, capp.pid,
                         capp.processName, capp.setAdj, "dying provider " + proc.processName);
-                Process.killProcess(capp.pid);
+                Process.killProcessQuiet(capp.pid);
             }
         }
         
@@ -9453,7 +9483,7 @@
                 if (si.doneExecutingCount > 0) {
                     flags |= Service.START_FLAG_REDELIVERY;
                 }
-                r.app.thread.scheduleServiceArgs(r, si.id, flags, si.intent);
+                r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);
             } catch (RemoteException e) {
                 // Remote process gone...  we'll let the normal cleanup take
                 // care of this.
@@ -9539,11 +9569,8 @@
         // pending arguments, then fake up one so its onStartCommand() will
         // be called.
         if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
-            r.lastStartId++;
-            if (r.lastStartId < 1) {
-                r.lastStartId = 1;
-            }
-            r.pendingStarts.add(new ServiceRecord.StartItem(r, r.lastStartId, null, -1));
+            r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
+                    null, -1));
         }
         
         sendServiceArgsLocked(r, true);
@@ -9897,11 +9924,7 @@
             }
             r.startRequested = true;
             r.callStart = false;
-            r.lastStartId++;
-            if (r.lastStartId < 1) {
-                r.lastStartId = 1;
-            }
-            r.pendingStarts.add(new ServiceRecord.StartItem(r, r.lastStartId,
+            r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
                     service, targetPermissionUid));
             r.lastActivity = SystemClock.uptimeMillis();
             synchronized (r.stats.getBatteryStats()) {
@@ -9943,6 +9966,15 @@
         }
     }
 
+    private void stopServiceLocked(ServiceRecord service) {
+        synchronized (service.stats.getBatteryStats()) {
+            service.stats.stopRunningLocked();
+        }
+        service.startRequested = false;
+        service.callStart = false;
+        bringDownServiceLocked(service, false);
+    }
+
     public int stopService(IApplicationThread caller, Intent service,
             String resolvedType) {
         // Refuse possible leaked file descriptors
@@ -9966,14 +9998,12 @@
             ServiceLookupResult r = findServiceLocked(service, resolvedType);
             if (r != null) {
                 if (r.record != null) {
-                    synchronized (r.record.stats.getBatteryStats()) {
-                        r.record.stats.stopRunningLocked();
-                    }
-                    r.record.startRequested = false;
-                    r.record.callStart = false;
                     final long origId = Binder.clearCallingIdentity();
-                    bringDownServiceLocked(r.record, false);
-                    Binder.restoreCallingIdentity(origId);
+                    try {
+                        stopServiceLocked(r.record);
+                    } finally {
+                        Binder.restoreCallingIdentity(origId);
+                    }
                     return 1;
                 }
                 return -1;
@@ -10035,7 +10065,7 @@
                         }
                     }
                     
-                    if (r.lastStartId != startId) {
+                    if (r.getLastStartId() != startId) {
                         return false;
                     }
                     
@@ -10476,7 +10506,7 @@
                         case Service.START_NOT_STICKY: {
                             // We are done with the associated start arguments.
                             r.findDeliveredStart(startId, true);
-                            if (r.lastStartId == startId) {
+                            if (r.getLastStartId() == startId) {
                                 // There is no more work, and this service
                                 // doesn't want to hang around if killed.
                                 r.stopIfKilled = true;
@@ -10496,6 +10526,12 @@
                             }
                             break;
                         }
+                        case Service.START_TASK_REMOVED_COMPLETE: {
+                            // Special processing for onTaskRemoved().  Don't
+                            // impact normal onStartCommand() processing.
+                            r.findDeliveredStart(startId, true);
+                            break;
+                        }
                         default:
                             throw new IllegalArgumentException(
                                     "Unknown service start result: " + res);
@@ -12885,23 +12921,31 @@
                 if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG,
                         "Setting process group of " + app.processName
                         + " to " + app.curSchedGroup);
-                if (true) {
-                    long oldId = Binder.clearCallingIdentity();
-                    try {
-                        Process.setProcessGroup(app.pid, app.curSchedGroup);
-                    } catch (Exception e) {
-                        Slog.w(TAG, "Failed setting process group of " + app.pid
-                                + " to " + app.curSchedGroup);
-                        e.printStackTrace();
-                    } finally {
-                        Binder.restoreCallingIdentity(oldId);
-                    }
-                }
-                if (false) {
-                    if (app.thread != null) {
+                if (app.waitingToKill != null &&
+                        app.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) {
+                    Slog.i(TAG, "Killing " + app + ": " + app.waitingToKill);
+                    EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+                            app.processName, app.setAdj, app.waitingToKill);
+                    Process.killProcessQuiet(app.pid);
+                } else {
+                    if (true) {
+                        long oldId = Binder.clearCallingIdentity();
                         try {
-                            app.thread.setSchedulingGroup(app.curSchedGroup);
-                        } catch (RemoteException e) {
+                            Process.setProcessGroup(app.pid, app.curSchedGroup);
+                        } catch (Exception e) {
+                            Slog.w(TAG, "Failed setting process group of " + app.pid
+                                    + " to " + app.curSchedGroup);
+                            e.printStackTrace();
+                        } finally {
+                            Binder.restoreCallingIdentity(oldId);
+                        }
+                    }
+                    if (false) {
+                        if (app.thread != null) {
+                            try {
+                                app.thread.setSchedulingGroup(app.curSchedGroup);
+                            } catch (RemoteException e) {
+                            }
                         }
                     }
                 }
@@ -13024,7 +13068,9 @@
                         + (app.thread != null ? app.thread.asBinder() : null)
                         + ")\n");
                     if (app.pid > 0 && app.pid != MY_PID) {
-                        Process.killProcess(app.pid);
+                        EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+                                app.processName, app.setAdj, "empty");
+                        Process.killProcessQuiet(app.pid);
                     } else {
                         try {
                             app.thread.scheduleExit();
@@ -13090,7 +13136,9 @@
                             + (app.thread != null ? app.thread.asBinder() : null)
                             + ")\n");
                         if (app.pid > 0 && app.pid != MY_PID) {
-                            Process.killProcess(app.pid);
+                            EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+                                    app.processName, app.setAdj, "empty");
+                            Process.killProcessQuiet(app.pid);
                         } else {
                             try {
                                 app.thread.scheduleExit();
@@ -13147,7 +13195,9 @@
                               + (app.thread != null ? app.thread.asBinder() : null)
                               + ")\n");
                         if (app.pid > 0 && app.pid != MY_PID) {
-                            Process.killProcess(app.pid);
+                            EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+                                    app.processName, app.setAdj, "old background");
+                            Process.killProcessQuiet(app.pid);
                         } else {
                             try {
                                 app.thread.scheduleExit();
@@ -13364,4 +13414,11 @@
             }
         }
     }
+
+    // Multi-user methods
+
+    public boolean switchUser(int userid) {
+        // TODO
+        return true;
+    }
 }
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index e1d380b..9558895 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -21,8 +21,10 @@
 import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
 
 import android.app.Activity;
+import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.IActivityManager;
+import android.app.IThumbnailRetriever;
 import static android.app.IActivityManager.START_CLASS_NOT_FOUND;
 import static android.app.IActivityManager.START_DELIVERED_TO_TOP;
 import static android.app.IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
@@ -69,7 +71,7 @@
 /**
  * State and management of a single stack of activities.
  */
-final public class ActivityStack {
+final class ActivityStack {
     static final String TAG = ActivityManagerService.TAG;
     static final boolean localLOGV = ActivityManagerService.localLOGV;
     static final boolean DEBUG_SWITCH = ActivityManagerService.DEBUG_SWITCH;
@@ -135,14 +137,14 @@
      * The back history of all previous (and possibly still
      * running) activities.  It contains HistoryRecord objects.
      */
-    final ArrayList mHistory = new ArrayList();
+    final ArrayList<ActivityRecord> mHistory = new ArrayList<ActivityRecord>();
     
     /**
      * List of running activities, sorted by recent usage.
      * The first entry in the list is the least recently used.
      * It contains HistoryRecord objects.
      */
-    final ArrayList mLRUActivities = new ArrayList();
+    final ArrayList<ActivityRecord> mLRUActivities = new ArrayList<ActivityRecord>();
 
     /**
      * List of activities that are waiting for a new activity
@@ -346,7 +348,7 @@
     final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
         int i = mHistory.size()-1;
         while (i >= 0) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            ActivityRecord r = mHistory.get(i);
             if (!r.finishing && r != notTop) {
                 return r;
             }
@@ -358,7 +360,7 @@
     final ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
         int i = mHistory.size()-1;
         while (i >= 0) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            ActivityRecord r = mHistory.get(i);
             if (!r.finishing && !r.delayedResume && r != notTop) {
                 return r;
             }
@@ -379,7 +381,7 @@
     final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) {
         int i = mHistory.size()-1;
         while (i >= 0) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            ActivityRecord r = mHistory.get(i);
             // Note: the taskId check depends on real taskId fields being non-zero
             if (!r.finishing && (token != r) && (taskId != r.task.taskId)) {
                 return r;
@@ -425,7 +427,7 @@
 
         final int N = mHistory.size();
         for (int i=(N-1); i>=0; i--) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            ActivityRecord r = mHistory.get(i);
             if (!r.finishing && r.task != cp
                     && r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
                 cp = r.task;
@@ -469,7 +471,7 @@
 
         final int N = mHistory.size();
         for (int i=(N-1); i>=0; i--) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            ActivityRecord r = mHistory.get(i);
             if (!r.finishing) {
                 if (r.intent.getComponent().equals(cls)) {
                     //Slog.i(TAG, "Found matching class!");
@@ -504,6 +506,7 @@
         }
 
         r.app = app;
+        app.waitingToKill = null;
 
         if (localLOGV) Slog.v(TAG, "Launching: " + r);
 
@@ -678,7 +681,7 @@
         }
         // Ensure activities are no longer sleeping.
         for (int i=mHistory.size()-1; i>=0; i--) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            ActivityRecord r = mHistory.get(i);
             r.setSleeping(false);
         }
         mGoingToSleepActivities.clear();
@@ -724,7 +727,7 @@
             // Make sure any stopped but visible activities are now sleeping.
             // This ensures that the activity's onStop() is called.
             for (int i=mHistory.size()-1; i>=0; i--) {
-                ActivityRecord r = (ActivityRecord)mHistory.get(i);
+                ActivityRecord r = mHistory.get(i);
                 if (r.state == ActivityState.STOPPING || r.state == ActivityState.STOPPED) {
                     r.setSleeping(true);
                 }
@@ -862,7 +865,7 @@
         synchronized (mService) {
             int index = indexOfTokenLocked(token);
             if (index >= 0) {
-                r = (ActivityRecord)mHistory.get(index);
+                r = mHistory.get(index);
                 mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
                 if (mPausingActivity == r) {
                     r.state = ActivityState.PAUSED;
@@ -1024,7 +1027,7 @@
         ActivityRecord r;
         boolean behindFullscreen = false;
         for (; i>=0; i--) {
-            r = (ActivityRecord)mHistory.get(i);
+            r = mHistory.get(i);
             if (DEBUG_VISBILITY) Slog.v(
                     TAG, "Make visible? " + r + " finishing=" + r.finishing
                     + " state=" + r.state);
@@ -1108,7 +1111,7 @@
         // Now for any activities that aren't visible to the user, make
         // sure they no longer are keeping the screen frozen.
         while (i >= 0) {
-            r = (ActivityRecord)mHistory.get(i);
+            r = mHistory.get(i);
             if (DEBUG_VISBILITY) Slog.v(
                     TAG, "Make invisible? " + r + " finishing=" + r.finishing
                     + " state=" + r.state
@@ -1499,7 +1502,7 @@
             // If starting in an existing task, find where that is...
             boolean startIt = true;
             for (int i = NH-1; i >= 0; i--) {
-                ActivityRecord p = (ActivityRecord)mHistory.get(i);
+                ActivityRecord p = mHistory.get(i);
                 if (p.finishing) {
                     continue;
                 }
@@ -1646,7 +1649,7 @@
         int replyChainEnd = -1;
         int lastReparentPos = -1;
         for (int i=mHistory.size()-1; i>=-1; i--) {
-            ActivityRecord below = i >= 0 ? (ActivityRecord)mHistory.get(i) : null;
+            ActivityRecord below = i >= 0 ? mHistory.get(i) : null;
             
             if (below != null && below.finishing) {
                 continue;
@@ -1700,7 +1703,7 @@
                         // bottom of the activity stack.  This also keeps it
                         // correctly ordered with any activities we previously
                         // moved.
-                        ActivityRecord p = (ActivityRecord)mHistory.get(0);
+                        ActivityRecord p = mHistory.get(0);
                         if (target.taskAffinity != null
                                 && target.taskAffinity.equals(p.task.affinity)) {
                             // If the activity currently at the bottom has the
@@ -1727,7 +1730,7 @@
                         int dstPos = 0;
                         ThumbnailHolder curThumbHolder = target.thumbHolder;
                         for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
-                            p = (ActivityRecord)mHistory.get(srcPos);
+                            p = mHistory.get(srcPos);
                             if (p.finishing) {
                                 continue;
                             }
@@ -1764,7 +1767,7 @@
                             // like these are all in the reply chain.
                             replyChainEnd = targetI+1;
                             while (replyChainEnd < mHistory.size() &&
-                                    ((ActivityRecord)mHistory.get(
+                                    (mHistory.get(
                                                 replyChainEnd)).task == task) {
                                 replyChainEnd++;
                             }
@@ -1774,7 +1777,7 @@
                         }
                         ActivityRecord p = null;
                         for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
-                            p = (ActivityRecord)mHistory.get(srcPos);
+                            p = mHistory.get(srcPos);
                             if (p.finishing) {
                                 continue;
                             }
@@ -1834,7 +1837,7 @@
                     }
                     ActivityRecord p = null;
                     for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
-                        p = (ActivityRecord)mHistory.get(srcPos);
+                        p = mHistory.get(srcPos);
                         if (p.finishing) {
                             continue;
                         }
@@ -1852,7 +1855,7 @@
                         replyChainEnd = targetI;
                     }
                     for (int srcPos=replyChainEnd; srcPos>=targetI; srcPos--) {
-                        ActivityRecord p = (ActivityRecord)mHistory.get(srcPos);
+                        ActivityRecord p = mHistory.get(srcPos);
                         if (p.finishing) {
                             continue;
                         }
@@ -1881,7 +1884,7 @@
                     // below so it remains singleTop.
                     if (target.info.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) {
                         for (int j=lastReparentPos-1; j>=0; j--) {
-                            ActivityRecord p = (ActivityRecord)mHistory.get(j);
+                            ActivityRecord p = mHistory.get(j);
                             if (p.finishing) {
                                 continue;
                             }
@@ -1922,7 +1925,7 @@
         // First find the requested task.
         while (i > 0) {
             i--;
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            ActivityRecord r = mHistory.get(i);
             if (r.task.taskId == taskId) {
                 i++;
                 break;
@@ -1932,7 +1935,7 @@
         // Now clear it.
         while (i > 0) {
             i--;
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            ActivityRecord r = mHistory.get(i);
             if (r.finishing) {
                 continue;
             }
@@ -1944,7 +1947,7 @@
                 ActivityRecord ret = r;
                 while (i < (mHistory.size()-1)) {
                     i++;
-                    r = (ActivityRecord)mHistory.get(i);
+                    r = mHistory.get(i);
                     if (r.task.taskId != taskId) {
                         break;
                     }
@@ -1980,6 +1983,28 @@
     }
 
     /**
+     * Completely remove all activities associated with an existing
+     * task starting at a specified index.
+     */
+    private final void performClearTaskAtIndexLocked(int taskId, int i) {
+        while (i < (mHistory.size()-1)) {
+            ActivityRecord r = mHistory.get(i);
+            if (r.task.taskId != taskId) {
+                // Whoops hit the end.
+                return;
+            }
+            if (r.finishing) {
+                i++;
+                continue;
+            }
+            if (!finishActivityLocked(r, i, Activity.RESULT_CANCELED,
+                    null, "clear")) {
+                i++;
+            }
+        }
+    }
+
+    /**
      * Completely remove all activities associated with an existing task.
      */
     private final void performClearTaskLocked(int taskId) {
@@ -1988,37 +2013,23 @@
         // First find the requested task.
         while (i > 0) {
             i--;
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            ActivityRecord r = mHistory.get(i);
             if (r.task.taskId == taskId) {
                 i++;
                 break;
             }
         }
 
-        // Now clear it.
+        // Now find the start and clear it.
         while (i > 0) {
             i--;
-            ActivityRecord r = (ActivityRecord)mHistory.get(i);
+            ActivityRecord r = mHistory.get(i);
             if (r.finishing) {
                 continue;
             }
             if (r.task.taskId != taskId) {
                 // We hit the bottom.  Now finish it all...
-                while (i < (mHistory.size()-1)) {
-                    i++;
-                    r = (ActivityRecord)mHistory.get(i);
-                    if (r.task.taskId != taskId) {
-                        // Whoops hit the end.
-                        return;
-                    }
-                    if (r.finishing) {
-                        continue;
-                    }
-                    if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
-                            null, "clear")) {
-                        i--;
-                    }
-                }
+                performClearTaskAtIndexLocked(taskId, i+1);
                 return;
             }
         }
@@ -2032,7 +2043,7 @@
         int i = mHistory.size();
         while (i > 0) {
             i--;
-            ActivityRecord candidate = (ActivityRecord)mHistory.get(i);
+            ActivityRecord candidate = mHistory.get(i);
             if (candidate.task.taskId != task) {
                 break;
             }
@@ -2049,9 +2060,9 @@
      * brought to the front.
      */
     private final ActivityRecord moveActivityToFrontLocked(int where) {
-        ActivityRecord newTop = (ActivityRecord)mHistory.remove(where);
+        ActivityRecord newTop = mHistory.remove(where);
         int top = mHistory.size();
-        ActivityRecord oldTop = (ActivityRecord)mHistory.get(top-1);
+        ActivityRecord oldTop = mHistory.get(top-1);
         mHistory.add(top, newTop);
         oldTop.frontOfTask = false;
         newTop.frontOfTask = true;
@@ -2094,7 +2105,7 @@
             if (DEBUG_RESULTS) Slog.v(
                 TAG, "Sending result to " + resultTo + " (index " + index + ")");
             if (index >= 0) {
-                sourceRecord = (ActivityRecord)mHistory.get(index);
+                sourceRecord = mHistory.get(index);
                 if (requestCode >= 0 && !sourceRecord.finishing) {
                     resultRecord = sourceRecord;
                 }
@@ -2576,7 +2587,7 @@
             // this case should never happen.
             final int N = mHistory.size();
             ActivityRecord prev =
-                N > 0 ? (ActivityRecord)mHistory.get(N-1) : null;
+                N > 0 ? mHistory.get(N-1) : null;
             r.setTask(prev != null
                     ? prev.task
                     : new TaskRecord(mService.mCurTask, r.info, intent), null, true);
@@ -3021,7 +3032,7 @@
             // Get the activity record.
             int index = indexOfTokenLocked(token);
             if (index >= 0) {
-                ActivityRecord r = (ActivityRecord)mHistory.get(index);
+                ActivityRecord r = mHistory.get(index);
 
                 if (fromTimeout) {
                     reportActivityLaunchedLocked(fromTimeout, r, -1, -1);
@@ -3153,12 +3164,12 @@
         if (index < 0) {
             return false;
         }
-        ActivityRecord r = (ActivityRecord)mHistory.get(index);
+        ActivityRecord r = mHistory.get(index);
 
         // Is this the last activity left?
         boolean lastActivity = true;
         for (int i=mHistory.size()-1; i>=0; i--) {
-            ActivityRecord p = (ActivityRecord)mHistory.get(i);
+            ActivityRecord p = mHistory.get(i);
             if (!p.finishing && p != r) {
                 lastActivity = false;
                 break;
@@ -3193,7 +3204,7 @@
                 System.identityHashCode(r),
                 r.task.taskId, r.shortComponentName, reason);
         if (index < (mHistory.size()-1)) {
-            ActivityRecord next = (ActivityRecord)mHistory.get(index+1);
+            ActivityRecord next = mHistory.get(index+1);
             if (next.task == r.task) {
                 if (r.frontOfTask) {
                     // The next activity is now the front of the task.
@@ -3249,7 +3260,7 @@
 
         if (mResumedActivity == r) {
             boolean endTask = index <= 0
-                    || ((ActivityRecord)mHistory.get(index-1)).task != r.task;
+                    || (mHistory.get(index-1)).task != r.task;
             if (DEBUG_TRANSITION) Slog.v(TAG,
                     "Prepare close transition: finishing " + r);
             mService.mWindowManager.prepareAppTransition(endTask
@@ -3391,6 +3402,7 @@
         // Get rid of any pending idle timeouts.
         mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
         mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
+        mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r);
     }
 
     private final void removeActivityFromHistoryLocked(ActivityRecord r) {
@@ -3516,7 +3528,7 @@
             
             int index = indexOfTokenLocked(token);
             if (index >= 0) {
-                ActivityRecord r = (ActivityRecord)mHistory.get(index);
+                ActivityRecord r = mHistory.get(index);
                 if (r.state == ActivityState.DESTROYING) {
                     final long origId = Binder.clearCallingIdentity();
                     removeActivityFromHistoryLocked(r);
@@ -3558,7 +3570,7 @@
     final void moveHomeToFrontLocked() {
         TaskRecord homeTask = null;
         for (int i=mHistory.size()-1; i>=0; i--) {
-            ActivityRecord hr = (ActivityRecord)mHistory.get(i);
+            ActivityRecord hr = mHistory.get(i);
             if (hr.isHomeActivity) {
                 homeTask = hr.task;
                 break;
@@ -3576,7 +3588,7 @@
         final int task = tr.taskId;
         int top = mHistory.size()-1;
 
-        if (top < 0 || ((ActivityRecord)mHistory.get(top)).task.taskId == task) {
+        if (top < 0 || (mHistory.get(top)).task.taskId == task) {
             // nothing to do!
             return;
         }
@@ -3591,7 +3603,7 @@
         // Shift all activities with this task up to the top
         // of the stack, keeping them in the same internal order.
         while (pos >= 0) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(pos);
+            ActivityRecord r = mHistory.get(pos);
             if (localLOGV) Slog.v(
                 TAG, "At " + pos + " ckp " + r.task + ": " + r);
             if (r.task.taskId == task) {
@@ -3680,7 +3692,7 @@
         // Shift all activities with this task down to the bottom
         // of the stack, keeping them in the same internal order.
         while (pos < N) {
-            ActivityRecord r = (ActivityRecord)mHistory.get(pos);
+            ActivityRecord r = mHistory.get(pos);
             if (localLOGV) Slog.v(
                 TAG, "At " + pos + " ckp " + r.task + ": " + r);
             if (r.task.taskId == task) {
@@ -3714,6 +3726,106 @@
         return true;
     }
     
+    public ActivityManager.TaskThumbnails getTaskThumbnailsLocked(TaskRecord tr) {
+        TaskAccessInfo info = getTaskAccessInfoLocked(tr.taskId, true);
+        ActivityRecord resumed = mResumedActivity;
+        if (resumed != null && resumed.thumbHolder == tr) {
+            info.mainThumbnail = resumed.stack.screenshotActivities(resumed);
+        } else {
+            info.mainThumbnail = tr.lastThumbnail;
+        }
+        return info;
+    }
+
+    public ActivityRecord removeTaskActivitiesLocked(int taskId, int subTaskIndex) {
+        TaskAccessInfo info = getTaskAccessInfoLocked(taskId, false);
+        if (info.root == null) {
+            Slog.w(TAG, "removeTaskLocked: unknown taskId " + taskId);
+            return null;
+        }
+
+        if (subTaskIndex < 0) {
+            // Just remove the entire task.
+            performClearTaskAtIndexLocked(taskId, info.rootIndex);
+            return info.root;
+        }
+
+        if (subTaskIndex >= info.subtasks.size()) {
+            Slog.w(TAG, "removeTaskLocked: unknown subTaskIndex " + subTaskIndex);
+            return null;
+        }
+
+        // Remove all of this task's activies starting at the sub task.
+        TaskAccessInfo.SubTask subtask = info.subtasks.get(subTaskIndex);
+        performClearTaskAtIndexLocked(taskId, subtask.index);
+        return subtask.activity;
+    }
+
+    public TaskAccessInfo getTaskAccessInfoLocked(int taskId, boolean inclThumbs) {
+        ActivityRecord resumed = mResumedActivity;
+        final TaskAccessInfo thumbs = new TaskAccessInfo();
+        // How many different sub-thumbnails?
+        final int NA = mHistory.size();
+        int j = 0;
+        ThumbnailHolder holder = null;
+        while (j < NA) {
+            ActivityRecord ar = mHistory.get(j);
+            if (!ar.finishing && ar.task.taskId == taskId) {
+                holder = ar.thumbHolder;
+                break;
+            }
+            j++;
+        }
+
+        if (j >= NA) {
+            return thumbs;
+        }
+
+        thumbs.root = mHistory.get(j);
+        thumbs.rootIndex = j;
+
+        ArrayList<TaskAccessInfo.SubTask> subtasks = new ArrayList<TaskAccessInfo.SubTask>();
+        thumbs.subtasks = subtasks;
+        ActivityRecord lastActivity = null;
+        while (j < NA) {
+            ActivityRecord ar = mHistory.get(j);
+            j++;
+            if (ar.finishing) {
+                continue;
+            }
+            if (ar.task.taskId != taskId) {
+                break;
+            }
+            lastActivity = ar;
+            if (ar.thumbHolder != holder && holder != null) {
+                thumbs.numSubThumbbails++;
+                holder = ar.thumbHolder;
+                TaskAccessInfo.SubTask sub = new TaskAccessInfo.SubTask();
+                sub.thumbnail = holder.lastThumbnail;
+                sub.activity = ar;
+                sub.index = j-1;
+                subtasks.add(sub);
+            }
+        }
+        if (lastActivity != null && subtasks.size() > 0) {
+            if (resumed == lastActivity) {
+                TaskAccessInfo.SubTask sub = subtasks.get(subtasks.size()-1);
+                sub.thumbnail = lastActivity.stack.screenshotActivities(lastActivity);
+            }
+        }
+        if (thumbs.numSubThumbbails > 0) {
+            thumbs.retriever = new IThumbnailRetriever.Stub() {
+                public Bitmap getThumbnail(int index) {
+                    if (index < 0 || index >= thumbs.subtasks.size()) {
+                        return null;
+                    }
+                    return thumbs.subtasks.get(index).thumbnail;
+                }
+            };
+        }
+        return thumbs;
+    }
+
     private final void logStartActivity(int tag, ActivityRecord r,
             TaskRecord task) {
         EventLog.writeEvent(tag,
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index 963a691..b4fdc9f 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -449,6 +449,7 @@
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         boolean isCheckin = false;
+        boolean noOutput = false;
         if (args != null) {
             for (String arg : args) {
                 if ("--checkin".equals(arg)) {
@@ -457,10 +458,22 @@
                     synchronized (mStats) {
                         mStats.resetAllStatsLocked();
                         pw.println("Battery stats reset.");
+                        noOutput = true;
                     }
+                } else if ("--write".equals(arg)) {
+                    synchronized (mStats) {
+                        mStats.writeSyncLocked();
+                        pw.println("Battery stats written.");
+                        noOutput = true;
+                    }
+                } else {
+                    pw.println("Unknown option: " + arg);
                 }
             }
         }
+        if (noOutput) {
+            return;
+        }
         if (isCheckin) {
             List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(0);
             synchronized (mStats) {
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 353ff6d..e39c239 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -65,6 +65,7 @@
     boolean foregroundServices; // Running any services that are foreground?
     boolean bad;                // True if disabled in the bad process list
     boolean killedBackground;   // True when proc has been killed due to too many bg
+    String waitingToKill;       // Process is waiting to be killed when in the bg; reason
     IBinder forcingToForeground;// Token that is forcing this process to be foreground
     int adjSeq;                 // Sequence id for identifying oom_adj assignment cycles
     int lruSeq;                 // Sequence id for identifying LRU update cycles
@@ -202,8 +203,9 @@
                 pw.print(" lastLowMemory=");
                 TimeUtils.formatDuration(lastLowMemory, now, pw);
                 pw.print(" reportLowMemory="); pw.println(reportLowMemory);
-        if (killedBackground) {
-            pw.print(prefix); pw.print("killedBackground="); pw.println(killedBackground);
+        if (killedBackground || waitingToKill != null) {
+            pw.print(prefix); pw.print("killedBackground="); pw.print(killedBackground);
+                    pw.print(" waitingToKill="); pw.println(waitingToKill);
         }
         if (debugging || crashing || crashDialog != null || notResponding
                 || anrDialog != null || bad) {
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 1a617dd..da00c0c 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -84,7 +84,6 @@
     boolean startRequested; // someone explicitly called start?
     boolean stopIfKilled;   // last onStart() said to stop if service killed?
     boolean callStart;      // last onStart() has asked to alway be called on restart.
-    int lastStartId;        // identifier of most recent start request.
     int executeNesting;     // number of outstanding operations keeping foreground.
     long executingStart;    // start time of last execute request.
     int crashCount;         // number of times proc has crashed with service running
@@ -96,8 +95,11 @@
 
     String stringName;      // caching of toString
     
+    private int lastStartId;    // identifier of most recent start request.
+
     static class StartItem {
         final ServiceRecord sr;
+        final boolean taskRemoved;
         final int id;
         final Intent intent;
         final int targetPermissionUid;
@@ -108,8 +110,10 @@
 
         String stringName;      // caching of toString
 
-        StartItem(ServiceRecord _sr, int _id, Intent _intent, int _targetPermissionUid) {
+        StartItem(ServiceRecord _sr, boolean _taskRemoved, int _id, Intent _intent,
+                int _targetPermissionUid) {
             sr = _sr;
+            taskRemoved = _taskRemoved;
             id = _id;
             intent = _intent;
             targetPermissionUid = _targetPermissionUid;
@@ -321,6 +325,18 @@
         return null;
     }
     
+    public int getLastStartId() {
+        return lastStartId;
+    }
+
+    public int makeNextStartId() {
+        lastStartId++;
+        if (lastStartId < 1) {
+            lastStartId = 1;
+        }
+        return lastStartId;
+    }
+
     public void postNotification() {
         final int appUid = appInfo.uid;
         final int appPid = app.pid;
diff --git a/services/java/com/android/server/am/TaskAccessInfo.java b/services/java/com/android/server/am/TaskAccessInfo.java
new file mode 100644
index 0000000..5618c1aa
--- /dev/null
+++ b/services/java/com/android/server/am/TaskAccessInfo.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import java.util.ArrayList;
+
+import android.app.ActivityManager.TaskThumbnails;
+import android.graphics.Bitmap;
+
+final class TaskAccessInfo extends TaskThumbnails {
+    final static class SubTask {
+        Bitmap thumbnail;
+        ActivityRecord activity;
+        int index;
+    }
+    
+    public ActivityRecord root;
+    public int rootIndex;
+    
+    public ArrayList<SubTask> subtasks;
+}
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 5853696..9ff5233 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -49,8 +49,9 @@
 import android.util.Log;
 
 import com.android.internal.telephony.Phone;
-import com.android.internal.util.HierarchicalState;
-import com.android.internal.util.HierarchicalStateMachine;
+import com.android.internal.util.IState;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -122,7 +123,7 @@
     // resampled each time we turn on tethering - used as cache for settings/config-val
     private boolean mDunRequired;  // configuration info - must use DUN apn on 3g
 
-    private HierarchicalStateMachine mTetherMasterSM;
+    private StateMachine mTetherMasterSM;
 
     private Notification mTetheredNotification;
 
@@ -668,7 +669,7 @@
     }
 
 
-    class TetherInterfaceSM extends HierarchicalStateMachine {
+    class TetherInterfaceSM extends StateMachine {
         // notification from the master SM that it's not in tether mode
         static final int CMD_TETHER_MODE_DEAD            =  1;
         // request from the user that it wants to tether
@@ -694,13 +695,13 @@
         // the upstream connection has changed
         static final int CMD_TETHER_CONNECTION_CHANGED   = 12;
 
-        private HierarchicalState mDefaultState;
+        private State mDefaultState;
 
-        private HierarchicalState mInitialState;
-        private HierarchicalState mStartingState;
-        private HierarchicalState mTetheredState;
+        private State mInitialState;
+        private State mStartingState;
+        private State mTetheredState;
 
-        private HierarchicalState mUnavailableState;
+        private State mUnavailableState;
 
         private boolean mAvailable;
         private boolean mTethered;
@@ -732,7 +733,7 @@
         public String toString() {
             String res = new String();
             res += mIfaceName + " - ";
-            HierarchicalState current = getCurrentState();
+            IState current = getCurrentState();
             if (current == mInitialState) res += "InitialState";
             if (current == mStartingState) res += "StartingState";
             if (current == mTetheredState) res += "TetheredState";
@@ -782,7 +783,7 @@
             return (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR);
         }
 
-        class InitialState extends HierarchicalState {
+        class InitialState extends State {
             @Override
             public void enter() {
                 setAvailable(true);
@@ -812,7 +813,7 @@
             }
         }
 
-        class StartingState extends HierarchicalState {
+        class StartingState extends State {
             @Override
             public void enter() {
                 setAvailable(false);
@@ -870,7 +871,7 @@
             }
         }
 
-        class TetheredState extends HierarchicalState {
+        class TetheredState extends State {
             @Override
             public void enter() {
                 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
@@ -1034,7 +1035,7 @@
             }
         }
 
-        class UnavailableState extends HierarchicalState {
+        class UnavailableState extends State {
             @Override
             public void enter() {
                 setAvailable(false);
@@ -1064,7 +1065,7 @@
 
     }
 
-    class TetherMasterSM extends HierarchicalStateMachine {
+    class TetherMasterSM extends StateMachine {
         // an interface SM has requested Tethering
         static final int CMD_TETHER_MODE_REQUESTED   = 1;
         // an interface SM has unrequested Tethering
@@ -1082,14 +1083,14 @@
         // We do not flush the old ones.
         private int mSequenceNumber;
 
-        private HierarchicalState mInitialState;
-        private HierarchicalState mTetherModeAliveState;
+        private State mInitialState;
+        private State mTetherModeAliveState;
 
-        private HierarchicalState mSetIpForwardingEnabledErrorState;
-        private HierarchicalState mSetIpForwardingDisabledErrorState;
-        private HierarchicalState mStartTetheringErrorState;
-        private HierarchicalState mStopTetheringErrorState;
-        private HierarchicalState mSetDnsForwardersErrorState;
+        private State mSetIpForwardingEnabledErrorState;
+        private State mSetIpForwardingDisabledErrorState;
+        private State mStartTetheringErrorState;
+        private State mStopTetheringErrorState;
+        private State mSetDnsForwardersErrorState;
 
         private ArrayList mNotifyList;
 
@@ -1125,7 +1126,7 @@
             setInitialState(mInitialState);
         }
 
-        class TetherMasterUtilState extends HierarchicalState {
+        class TetherMasterUtilState extends State {
             protected final static boolean TRY_TO_SETUP_MOBILE_CONNECTION = true;
             protected final static boolean WAIT_FOR_NETWORK_TO_SETTLE     = false;
 
@@ -1440,7 +1441,7 @@
             }
         }
 
-        class ErrorState extends HierarchicalState {
+        class ErrorState extends State {
             int mErrorNotification;
             @Override
             public boolean processMessage(Message message) {
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index 11dde75..16b55c3 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -1336,6 +1336,19 @@
         }
         mPendingPackages.clear();
 
+        /*
+         * Make sure all the updated system packages have their shared users
+         * associated with them.
+         */
+        final Iterator<PackageSetting> disabledIt = mDisabledSysPackages.values().iterator();
+        while (disabledIt.hasNext()) {
+            final PackageSetting disabledPs = disabledIt.next();
+            final Object id = getUserIdLPr(disabledPs.userId);
+            if (id != null && id instanceof SharedUserSetting) {
+                disabledPs.sharedUser = (SharedUserSetting) id;
+            }
+        }
+
         readStoppedLPw();
 
         mReadMessages.append("Read completed successfully: " + mPackages.size() + " packages, "
diff --git a/services/java/com/android/server/pm/UserDetails.java b/services/java/com/android/server/pm/UserDetails.java
new file mode 100644
index 0000000..2aeed7c
--- /dev/null
+++ b/services/java/com/android/server/pm/UserDetails.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import com.android.internal.util.FastXmlSerializer;
+
+import android.content.pm.UserInfo;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+public class UserDetails {
+    private static final String TAG_NAME = "name";
+
+    private static final String ATTR_FLAGS = "flags";
+
+    private static final String ATTR_ID = "id";
+
+    private static final String TAG_USERS = "users";
+
+    private static final String TAG_USER = "user";
+
+    private static final String TAG = "UserDetails";
+
+    private static final String USER_INFO_DIR = "system/users";
+    private static final String USER_LIST_FILENAME = "userlist.xml";
+
+    private SparseArray<UserInfo> mUsers;
+
+    private final File mUsersDir;
+    private final File mUserListFile;
+
+    /**
+     * Available for testing purposes.
+     */
+    UserDetails(File dataDir) {
+        mUsersDir = new File(dataDir, USER_INFO_DIR);
+        mUsersDir.mkdirs();
+        FileUtils.setPermissions(mUsersDir.toString(),
+                FileUtils.S_IRWXU|FileUtils.S_IRWXG
+                |FileUtils.S_IROTH|FileUtils.S_IXOTH,
+                -1, -1);
+        mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
+        readUserList();
+    }
+
+    public UserDetails() {
+        this(Environment.getDataDirectory());
+    }
+
+    public List<UserInfo> getUsers() {
+        ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
+        for (int i = 0; i < mUsers.size(); i++) {
+            users.add(mUsers.valueAt(i));
+        }
+        return users;
+    }
+
+    private void readUserList() {
+        mUsers = new SparseArray<UserInfo>();
+        if (!mUserListFile.exists()) {
+            fallbackToSingleUser();
+            return;
+        }
+        FileInputStream fis = null;
+        try {
+            fis = new FileInputStream(mUserListFile);
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(fis, null);
+            int type;
+            while ((type = parser.next()) != XmlPullParser.START_TAG
+                    && type != XmlPullParser.END_DOCUMENT) {
+                ;
+            }
+
+            if (type != XmlPullParser.START_TAG) {
+                Slog.e(TAG, "Unable to read user list");
+                fallbackToSingleUser();
+                return;
+            }
+
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+                if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
+                    String id = parser.getAttributeValue(null, ATTR_ID);
+                    UserInfo user = readUser(Integer.parseInt(id));
+                    if (user != null) {
+                        mUsers.put(user.id, user);
+                    }
+                }
+            }
+        } catch (IOException ioe) {
+            fallbackToSingleUser();
+        } catch (XmlPullParserException pe) {
+            fallbackToSingleUser();
+        }
+    }
+
+    private void fallbackToSingleUser() {
+        // Create the primary user
+        UserInfo primary = new UserInfo(0, "Primary",
+                UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
+        mUsers.put(0, primary);
+
+        writeUserList();
+        writeUser(primary);
+    }
+
+    /*
+     * Writes the user file in this format:
+     *
+     * <user flags="20039023" id="0">
+     *   <name>Primary</name>
+     * </user>
+     */
+    private void writeUser(UserInfo userInfo) {
+        try {
+            final File mUserFile = new File(mUsersDir, userInfo.id + ".xml");
+            final FileOutputStream fos = new FileOutputStream(mUserFile);
+            final BufferedOutputStream bos = new BufferedOutputStream(fos);
+
+            // XmlSerializer serializer = XmlUtils.serializerInstance();
+            final XmlSerializer serializer = new FastXmlSerializer();
+            serializer.setOutput(bos, "utf-8");
+            serializer.startDocument(null, true);
+            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+            serializer.startTag(null, TAG_USER);
+            serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id));
+            serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags));
+
+            serializer.startTag(null, TAG_NAME);
+            serializer.text(userInfo.name);
+            serializer.endTag(null, TAG_NAME);
+
+            serializer.endTag(null, TAG_USER);
+
+            serializer.endDocument();
+        } catch (IOException ioe) {
+            Slog.e(TAG, "Error writing user info " + userInfo.id + "\n" + ioe);
+        }
+    }
+
+    /*
+     * Writes the user list file in this format:
+     *
+     * <users>
+     *   <user id="0"></user>
+     *   <user id="2"></user>
+     * </users>
+     */
+    private void writeUserList() {
+        try {
+            final FileOutputStream fos = new FileOutputStream(mUserListFile);
+            final BufferedOutputStream bos = new BufferedOutputStream(fos);
+
+            // XmlSerializer serializer = XmlUtils.serializerInstance();
+            final XmlSerializer serializer = new FastXmlSerializer();
+            serializer.setOutput(bos, "utf-8");
+            serializer.startDocument(null, true);
+            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+            serializer.startTag(null, TAG_USERS);
+
+            for (int i = 0; i < mUsers.size(); i++) {
+                UserInfo user = mUsers.valueAt(i);
+                serializer.startTag(null, TAG_USER);
+                serializer.attribute(null, ATTR_ID, Integer.toString(user.id));
+                serializer.endTag(null, TAG_USER);
+                Slog.e(TAG, "Wrote user " + user.id + " to userlist.xml");
+            }
+
+            serializer.endTag(null, TAG_USERS);
+
+            serializer.endDocument();
+        } catch (IOException ioe) {
+            Slog.e(TAG, "Error writing user list");
+        }
+    }
+
+    private UserInfo readUser(int id) {
+        int flags = 0;
+        String name = null;
+
+        FileInputStream fis = null;
+        try {
+            File userFile = new File(mUsersDir, Integer.toString(id) + ".xml");
+            fis = new FileInputStream(userFile);
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(fis, null);
+            int type;
+            while ((type = parser.next()) != XmlPullParser.START_TAG
+                    && type != XmlPullParser.END_DOCUMENT) {
+                ;
+            }
+
+            if (type != XmlPullParser.START_TAG) {
+                Slog.e(TAG, "Unable to read user " + id);
+                return null;
+            }
+
+            if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
+                String storedId = parser.getAttributeValue(null, ATTR_ID);
+                if (Integer.parseInt(storedId) != id) {
+                    Slog.e(TAG, "User id does not match the file name");
+                    return null;
+                }
+                String flagString = parser.getAttributeValue(null, ATTR_FLAGS);
+                flags = Integer.parseInt(flagString);
+
+                while ((type = parser.next()) != XmlPullParser.START_TAG
+                        && type != XmlPullParser.END_DOCUMENT) {
+                }
+                if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_NAME)) {
+                    type = parser.next();
+                    if (type == XmlPullParser.TEXT) {
+                        name = parser.getText();
+                    }
+                }
+            }
+            fis.close();
+
+            UserInfo userInfo = new UserInfo(id, name, flags);
+            return userInfo;
+
+        } catch (IOException ioe) {
+        } catch (XmlPullParserException pe) {
+        }
+        return null;
+    }
+
+    public UserInfo createUser(String name, int flags) {
+        int id = getNextAvailableId();
+        UserInfo userInfo = new UserInfo(id, name, flags);
+        if (!createPackageFolders(id)) {
+            return null;
+        }
+        mUsers.put(id, userInfo);
+        writeUserList();
+        writeUser(userInfo);
+        return userInfo;
+    }
+
+    public void removeUser(int id) {
+        // Remove from the list
+        UserInfo userInfo = mUsers.get(id);
+        if (userInfo != null) {
+            // Remove this user from the list
+            mUsers.remove(id);
+            // Remove user file
+            File userFile = new File(mUsersDir, id + ".xml");
+            userFile.delete();
+            writeUserList();
+            removePackageFolders(id);
+        }
+    }
+
+    private int getNextAvailableId() {
+        int i = 0;
+        while (i < Integer.MAX_VALUE) {
+            if (mUsers.indexOfKey(i) < 0) {
+                break;
+            }
+            i++;
+        }
+        return i;
+    }
+
+    private boolean createPackageFolders(int id) {
+        // TODO: Create data directories for all the packages for a new user, w/ specified user id.
+        return true;
+    }
+
+    private boolean removePackageFolders(int id) {
+        // TODO: Remove all the data directories for the specified user.
+        return true;
+    }
+}
diff --git a/services/jni/com_android_server_AlarmManagerService.cpp b/services/jni/com_android_server_AlarmManagerService.cpp
index aa8c9b3..c9a702a 100644
--- a/services/jni/com_android_server_AlarmManagerService.cpp
+++ b/services/jni/com_android_server_AlarmManagerService.cpp
@@ -2,16 +2,16 @@
 **
 ** Copyright 2006, 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 
+** 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 
+**     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 
+** 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.
 */
 
@@ -84,7 +84,7 @@
     struct timespec ts;
     ts.tv_sec = seconds;
     ts.tv_nsec = nanoseconds;
-    
+
 	int result = ioctl(fd, ANDROID_ALARM_SET(type), &ts);
 	if (result < 0)
 	{
@@ -97,18 +97,18 @@
 {
 #ifdef HAVE_ANDROID_OS
 	int result = 0;
-	
+
 	do
 	{
 		result = ioctl(fd, ANDROID_ALARM_WAIT);
 	} while (result < 0 && errno == EINTR);
-	
+
 	if (result < 0)
 	{
         LOGE("Unable to wait on alarm: %s\n", strerror(errno));
         return 0;
     }
-    
+
     return result;
 #endif
 }
@@ -124,14 +124,6 @@
 
 int register_android_server_AlarmManagerService(JNIEnv* env)
 {
-    jclass clazz = env->FindClass("com/android/server/AlarmManagerService");
-
-    if (clazz == NULL)
-	{
-        LOGE("Can't find com/android/server/AlarmManagerService");
-        return -1;
-    }
-
     return jniRegisterNativeMethods(env, "com/android/server/AlarmManagerService",
                                     sMethods, NELEM(sMethods));
 }
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index ab2c125..aaa305e 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -36,6 +36,8 @@
 
 #include <input/InputManager.h>
 #include <input/PointerController.h>
+#include <input/SpotController.h>
+#include <input/SpriteController.h>
 
 #include <android_os_MessageQueue.h>
 #include <android_view_KeyEvent.h>
@@ -163,6 +165,7 @@
     virtual nsecs_t getVirtualKeyQuietTime();
     virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames);
     virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId);
+    virtual sp<SpotControllerInterface> obtainSpotController(int32_t deviceId);
 
     /* --- InputDispatcherPolicyInterface implementation --- */
 
@@ -213,12 +216,16 @@
         // System UI visibility.
         int32_t systemUiVisibility;
 
+        // Sprite controller singleton, created on first use.
+        sp<SpriteController> spriteController;
+
         // Pointer controller singleton, created and destroyed as needed.
         wp<PointerController> pointerController;
     } mLocked;
 
     void updateInactivityFadeDelayLocked(const sp<PointerController>& controller);
     void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags);
+    void ensureSpriteControllerLocked();
 
     // Power manager interactions.
     bool isScreenOn();
@@ -419,18 +426,15 @@
 
     sp<PointerController> controller = mLocked.pointerController.promote();
     if (controller == NULL) {
-        JNIEnv* env = jniEnv();
-        jint layer = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.getPointerLayer);
-        if (checkAndClearExceptionFromCallback(env, "getPointerLayer")) {
-            layer = -1;
-        }
+        ensureSpriteControllerLocked();
 
-        controller = new PointerController(mLooper, layer);
+        controller = new PointerController(mLooper, mLocked.spriteController);
         mLocked.pointerController = controller;
 
         controller->setDisplaySize(mLocked.displayWidth, mLocked.displayHeight);
         controller->setDisplayOrientation(mLocked.displayOrientation);
 
+        JNIEnv* env = jniEnv();
         jobject iconObj = env->CallObjectMethod(mCallbacksObj, gCallbacksClassInfo.getPointerIcon);
         if (!checkAndClearExceptionFromCallback(env, "getPointerIcon") && iconObj) {
             jfloat iconHotSpotX = env->GetFloatField(iconObj, gPointerIconClassInfo.hotSpotX);
@@ -451,6 +455,24 @@
     return controller;
 }
 
+sp<SpotControllerInterface> NativeInputManager::obtainSpotController(int32_t deviceId) {
+    AutoMutex _l(mLock);
+
+    ensureSpriteControllerLocked();
+    return new SpotController(mLooper, mLocked.spriteController);
+}
+
+void NativeInputManager::ensureSpriteControllerLocked() {
+    if (mLocked.spriteController == NULL) {
+        JNIEnv* env = jniEnv();
+        jint layer = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.getPointerLayer);
+        if (checkAndClearExceptionFromCallback(env, "getPointerLayer")) {
+            layer = -1;
+        }
+        mLocked.spriteController = new SpriteController(mLooper, layer);
+    }
+}
+
 void NativeInputManager::notifySwitch(nsecs_t when, int32_t switchCode,
         int32_t switchValue, uint32_t policyFlags) {
 #if DEBUG_INPUT_DISPATCHER_POLICY
diff --git a/services/jni/com_android_server_UsbService.cpp b/services/jni/com_android_server_UsbService.cpp
index 00ee7e3..9cd04f6 100644
--- a/services/jni/com_android_server_UsbService.cpp
+++ b/services/jni/com_android_server_UsbService.cpp
@@ -37,13 +37,6 @@
 namespace android
 {
 
-static struct file_descriptor_offsets_t
-{
-    jclass mClass;
-    jmethodID mConstructor;
-    jfieldID mDescriptor;
-} gFileDescriptorOffsets;
-
 static struct parcel_file_descriptor_offsets_t
 {
     jclass mClass;
@@ -167,11 +160,8 @@
     int newFD = dup(fd);
     usb_device_close(device);
 
-    jobject fileDescriptor = env->NewObject(gFileDescriptorOffsets.mClass,
-        gFileDescriptorOffsets.mConstructor);
-    if (fileDescriptor != NULL) {
-        env->SetIntField(fileDescriptor, gFileDescriptorOffsets.mDescriptor, newFD);
-    } else {
+    jobject fileDescriptor = jniCreateFileDescriptor(env, newFD);
+    if (fileDescriptor == NULL) {
         return NULL;
     }
     return env->NewObject(gParcelFileDescriptorOffsets.mClass,
@@ -221,11 +211,8 @@
         LOGE("could not open %s", DRIVER_NAME);
         return NULL;
     }
-    jobject fileDescriptor = env->NewObject(gFileDescriptorOffsets.mClass,
-        gFileDescriptorOffsets.mConstructor);
-    if (fileDescriptor != NULL) {
-        env->SetIntField(fileDescriptor, gFileDescriptorOffsets.mDescriptor, fd);
-    } else {
+    jobject fileDescriptor = jniCreateFileDescriptor(env, fd);
+    if (fileDescriptor == NULL) {
         return NULL;
     }
     return env->NewObject(gParcelFileDescriptorOffsets.mClass,
@@ -260,14 +247,6 @@
         return -1;
     }
 
-    clazz = env->FindClass("java/io/FileDescriptor");
-    LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
-    gFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
-    gFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "()V");
-    gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I");
-    LOG_FATAL_IF(gFileDescriptorOffsets.mDescriptor == NULL,
-                 "Unable to find descriptor field in java.io.FileDescriptor");
-
     clazz = env->FindClass("android/os/ParcelFileDescriptor");
     LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor");
     gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index 64cff96..a774841 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -93,7 +93,11 @@
 int DisplayHardware::getHeight() const          { return mHeight; }
 PixelFormat DisplayHardware::getFormat() const  { return mFormat; }
 uint32_t DisplayHardware::getMaxTextureSize() const { return mMaxTextureSize; }
-uint32_t DisplayHardware::getMaxViewportDims() const { return mMaxViewportDims; }
+
+uint32_t DisplayHardware::getMaxViewportDims() const {
+    return mMaxViewportDims[0] < mMaxViewportDims[1] ?
+            mMaxViewportDims[0] : mMaxViewportDims[1];
+}
 
 void DisplayHardware::init(uint32_t dpy)
 {
@@ -228,7 +232,7 @@
             eglQueryString(display, EGL_EXTENSIONS));
 
     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
-    glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &mMaxViewportDims);
+    glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);
 
 
 #ifdef EGL_ANDROID_swap_rectangle
@@ -260,7 +264,7 @@
     LOGI("version   : %s", extensions.getVersion());
     LOGI("extensions: %s", extensions.getExtension());
     LOGI("GL_MAX_TEXTURE_SIZE = %d", mMaxTextureSize);
-    LOGI("GL_MAX_VIEWPORT_DIMS = %d", mMaxViewportDims);
+    LOGI("GL_MAX_VIEWPORT_DIMS = %d x %d", mMaxViewportDims[0], mMaxViewportDims[1]);
     LOGI("flags = %08x", mFlags);
 
     // Unbind the context from this thread
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
index ee7a2af..cdf89fd 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
@@ -108,7 +108,7 @@
     PixelFormat     mFormat;
     uint32_t        mFlags;
     mutable uint32_t mPageFlipCount;
-    GLint           mMaxViewportDims;
+    GLint           mMaxViewportDims[2];
     GLint           mMaxTextureSize;
     
     HWComposer*     mHwc;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index ea283c606..2f3a144 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2553,22 +2553,9 @@
         LOGE("createGraphicBuffer: unable to create GraphicBuffer");
         return 0;
     }
-    Mutex::Autolock _l(mLock);
-    mBuffers.add(graphicBuffer);
     return graphicBuffer;
 }
 
-void GraphicBufferAlloc::freeAllGraphicBuffersExcept(int bufIdx) {
-    Mutex::Autolock _l(mLock);
-    if (bufIdx >= 0 && size_t(bufIdx) < mBuffers.size()) {
-        sp<GraphicBuffer> b(mBuffers[bufIdx]);
-        mBuffers.clear();
-        mBuffers.add(b);
-    } else {
-        mBuffers.clear();
-    }
-}
-
 // ---------------------------------------------------------------------------
 
 GraphicPlane::GraphicPlane()
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 0964848..8d43157 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -125,14 +125,8 @@
 public:
     GraphicBufferAlloc();
     virtual ~GraphicBufferAlloc();
-
     virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
         PixelFormat format, uint32_t usage);
-    virtual void freeAllGraphicBuffersExcept(int bufIdx);
-
-private:
-    Vector<sp<GraphicBuffer> > mBuffers;
-    Mutex mLock;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index f115f42..2fcce78 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -15,7 +15,7 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.frameworks.servicestests">
+        package="com.android.frameworks.servicestests">
 
     <uses-permission android:name="android.permission.READ_LOGS" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserDetailsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserDetailsTest.java
new file mode 100644
index 0000000..7b77aac
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/UserDetailsTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import com.android.server.pm.UserDetails;
+
+import android.content.pm.UserInfo;
+import android.os.Debug;
+import android.os.Environment;
+import android.test.AndroidTestCase;
+
+import java.util.List;
+
+/** Test {@link UserDetails} functionality. */
+public class UserDetailsTest extends AndroidTestCase {
+
+    UserDetails mDetails = null;
+
+    @Override
+    public void setUp() throws Exception {
+        mDetails = new UserDetails(Environment.getExternalStorageDirectory());
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        List<UserInfo> users = mDetails.getUsers();
+        // Remove all except the primary user
+        for (UserInfo user : users) {
+            if (!user.isPrimary()) {
+                mDetails.removeUser(user.id);
+            }
+        }
+    }
+
+    public void testHasPrimary() throws Exception {
+        assertTrue(findUser(0));
+    }
+
+    public void testAddUser() throws Exception {
+        final UserDetails details = mDetails;
+
+        UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST);
+        assertTrue(userInfo != null);
+
+        List<UserInfo> list = details.getUsers();
+        boolean found = false;
+        for (UserInfo user : list) {
+            if (user.id == userInfo.id && user.name.equals("Guest 1")
+                    && user.isGuest()
+                    && !user.isAdmin()
+                    && !user.isPrimary()) {
+                found = true;
+            }
+        }
+        assertTrue(found);
+    }
+
+    public void testAdd2Users() throws Exception {
+        final UserDetails details = mDetails;
+
+        UserInfo user1 = details.createUser("Guest 1", UserInfo.FLAG_GUEST);
+        UserInfo user2 = details.createUser("User 2", UserInfo.FLAG_ADMIN);
+
+        assertTrue(user1 != null);
+        assertTrue(user2 != null);
+
+        assertTrue(findUser(0));
+        assertTrue(findUser(user1.id));
+        assertTrue(findUser(user2.id));
+    }
+
+    public void testRemoveUser() throws Exception {
+        final UserDetails details = mDetails;
+
+        UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST);
+
+        details.removeUser(userInfo.id);
+
+        assertFalse(findUser(userInfo.id));
+    }
+
+    private boolean findUser(int id) {
+        List<UserInfo> list = mDetails.getUsers();
+
+        for (UserInfo user : list) {
+            if (user.id == id) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index 0c47e86..791fbfd 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -17,8 +17,8 @@
 package com.android.internal.telephony;
 
 
-import com.android.internal.util.HierarchicalState;
-import com.android.internal.util.HierarchicalStateMachine;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
 
 import android.net.LinkAddress;
 import android.net.LinkCapabilities;
@@ -37,7 +37,7 @@
 /**
  * {@hide}
  *
- * DataConnection HierarchicalStateMachine.
+ * DataConnection StateMachine.
  *
  * This is an abstract base class for representing a single data connection.
  * Instances of this class such as <code>CdmaDataConnection</code> and
@@ -55,7 +55,7 @@
  *
  * The other public methods are provided for debugging.
  */
-public abstract class DataConnection extends HierarchicalStateMachine {
+public abstract class DataConnection extends StateMachine {
     protected static final boolean DBG = true;
 
     protected static Object mCountLock = new Object();
@@ -484,17 +484,17 @@
     /**
      * The parent state for all other states.
      */
-    private class DcDefaultState extends HierarchicalState {
+    private class DcDefaultState extends State {
         @Override
-        protected void enter() {
+        public void enter() {
             phone.mCM.registerForRilConnected(getHandler(), EVENT_RIL_CONNECTED, null);
         }
         @Override
-        protected void exit() {
+        public void exit() {
             phone.mCM.unregisterForRilConnected(getHandler());
         }
         @Override
-        protected boolean processMessage(Message msg) {
+        public boolean processMessage(Message msg) {
             AsyncResult ar;
 
             switch (msg.what) {
@@ -547,7 +547,7 @@
     /**
      * The state machine is inactive and expects a EVENT_CONNECT.
      */
-    private class DcInactiveState extends HierarchicalState {
+    private class DcInactiveState extends State {
         private ConnectionParams mConnectionParams = null;
         private FailCause mFailCause = null;
         private DisconnectParams mDisconnectParams = null;
@@ -563,7 +563,8 @@
             mDisconnectParams = dp;
         }
 
-        @Override protected void enter() {
+        @Override
+        public void enter() {
             mTag += 1;
 
             /**
@@ -583,14 +584,16 @@
             }
         }
 
-        @Override protected void exit() {
+        @Override
+        public void exit() {
             // clear notifications
             mConnectionParams = null;
             mFailCause = null;
             mDisconnectParams = null;
         }
 
-        @Override protected boolean processMessage(Message msg) {
+        @Override
+        public boolean processMessage(Message msg) {
             boolean retVal;
 
             switch (msg.what) {
@@ -626,8 +629,9 @@
     /**
      * The state machine is activating a connection.
      */
-    private class DcActivatingState extends HierarchicalState {
-        @Override protected boolean processMessage(Message msg) {
+    private class DcActivatingState extends State {
+        @Override
+        public boolean processMessage(Message msg) {
             boolean retVal;
             AsyncResult ar;
             ConnectionParams cp;
@@ -722,7 +726,7 @@
     /**
      * The state machine is connected, expecting an EVENT_DISCONNECT.
      */
-    private class DcActiveState extends HierarchicalState {
+    private class DcActiveState extends State {
         private ConnectionParams mConnectionParams = null;
         private FailCause mFailCause = null;
 
@@ -746,13 +750,15 @@
             }
         }
 
-        @Override protected void exit() {
+        @Override
+        public void exit() {
             // clear notifications
             mConnectionParams = null;
             mFailCause = null;
         }
 
-        @Override protected boolean processMessage(Message msg) {
+        @Override
+        public boolean processMessage(Message msg) {
             boolean retVal;
 
             switch (msg.what) {
@@ -778,8 +784,9 @@
     /**
      * The state machine is disconnecting.
      */
-    private class DcDisconnectingState extends HierarchicalState {
-        @Override protected boolean processMessage(Message msg) {
+    private class DcDisconnectingState extends State {
+        @Override
+        public boolean processMessage(Message msg) {
             boolean retVal;
 
             switch (msg.what) {
@@ -812,8 +819,9 @@
     /**
      * The state machine is disconnecting after an creating a connection.
      */
-    private class DcDisconnectionErrorCreatingConnection extends HierarchicalState {
-        @Override protected boolean processMessage(Message msg) {
+    private class DcDisconnectionErrorCreatingConnection extends State {
+        @Override
+        public boolean processMessage(Message msg) {
             boolean retVal;
 
             switch (msg.what) {
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index d5b65e1..3280c44 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -97,7 +97,6 @@
     protected static final int EVENT_TRY_SETUP_DATA = 5;
     protected static final int EVENT_DATA_STATE_CHANGED = 6;
     protected static final int EVENT_POLL_PDP = 7;
-    protected static final int EVENT_GET_PDP_LIST_COMPLETE = 11;
     protected static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 12;
     protected static final int EVENT_VOICE_CALL_STARTED = 14;
     protected static final int EVENT_VOICE_CALL_ENDED = 15;
@@ -408,7 +407,7 @@
     }
 
     /** TODO: See if we can remove */
-    public String getActiveApnString() {
+    public String getActiveApnString(String apnType) {
         String result = null;
         if (mActiveApn != null) {
             result = mActiveApn.apn;
@@ -466,6 +465,8 @@
     protected abstract void onVoiceCallEnded();
     protected abstract void onCleanUpConnection(boolean tearDown, int apnId, String reason);
     protected abstract void onCleanUpAllConnections(String cause);
+    protected abstract boolean isDataPossible();
+    protected abstract boolean isDataPossible(String apnType);
 
     @Override
     public void handleMessage(Message msg) {
@@ -719,29 +720,6 @@
         notifyOffApnsOfAvailability(reason, isDataPossible());
     }
 
-    /**
-     * The only circumstances under which we report that data connectivity is not
-     * possible are
-     * <ul>
-     * <li>Data is disallowed (roaming, power state, voice call, etc).</li>
-     * <li>The current data state is {@code DISCONNECTED} for a reason other than
-     * having explicitly disabled connectivity. In other words, data is not available
-     * because the phone is out of coverage or some like reason.</li>
-     * </ul>
-     * @return {@code true} if data connectivity is possible, {@code false} otherwise.
-     */
-    protected boolean isDataPossible() {
-        boolean dataAllowed = isDataAllowed();
-        boolean anyDataEnabled = getAnyDataEnabled();
-        boolean possible = (dataAllowed
-                && !(anyDataEnabled && (mState == State.FAILED || mState == State.IDLE)));
-        if (!possible && DBG) {
-            log("isDataPossible() " + possible + ", dataAllowed=" + dataAllowed +
-                    " anyDataEnabled=" + anyDataEnabled + " dataState=" + mState);
-        }
-        return possible;
-    }
-
     public boolean isApnTypeEnabled(String apnType) {
         if (apnType == null) {
             return false;
@@ -949,10 +927,6 @@
         }
     }
 
-    public synchronized boolean getDataEnabled() {
-        return mDataEnabled;
-    }
-
     public void cleanUpAllConnections(String cause) {
         Message msg = obtainMessage(EVENT_CLEAN_UP_ALL_CONNECTIONS);
         msg.obj = cause;
diff --git a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index 83db3d1..910905a 100644
--- a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -114,8 +114,8 @@
         try {
             mRegistry.notifyDataConnection(
                     convertDataState(state),
-                    sender.isDataConnectivityPossible(), reason,
-                    sender.getActiveApnHost(),
+                    sender.isDataConnectivityPossible(apnType), reason,
+                    sender.getActiveApnHost(apnType),
                     apnType,
                     linkProperties,
                     linkCapabilities,
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index 9f16d31..56be570 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -159,8 +159,8 @@
     static final String REASON_ROAMING_OFF = "roamingOff";
     static final String REASON_DATA_DISABLED = "dataDisabled";
     static final String REASON_DATA_ENABLED = "dataEnabled";
-    static final String REASON_GPRS_ATTACHED = "gprsAttached";
-    static final String REASON_GPRS_DETACHED = "gprsDetached";
+    static final String REASON_DATA_ATTACHED = "dataAttached";
+    static final String REASON_DATA_DETACHED = "dataDetached";
     static final String REASON_CDMA_DATA_ATTACHED = "cdmaDataAttached";
     static final String REASON_CDMA_DATA_DETACHED = "cdmaDataDetached";
     static final String REASON_APN_CHANGED = "apnChanged";
@@ -333,7 +333,7 @@
      * Returns string for the active APN host.
      *  @return type as a string or null if none.
      */
-    String getActiveApnHost();
+    String getActiveApnHost(String apnType);
 
     /**
      * Return the LinkProperties for the named apn or null if not available
@@ -1375,6 +1375,11 @@
     boolean isDataConnectivityPossible();
 
     /**
+     * Report on whether data connectivity is allowed for an APN.
+     */
+    boolean isDataConnectivityPossible(String apnType);
+
+    /**
      * Retrieves the unique device ID, e.g., IMEI for GSM phones and MEID for CDMA phones.
      */
     String getDeviceId();
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index 3224995..5a77da7 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -977,8 +977,8 @@
         return mDataConnectionTracker.getActiveApnTypes();
     }
 
-    public String getActiveApnHost() {
-        return mDataConnectionTracker.getActiveApnString();
+    public String getActiveApnHost(String apnType) {
+        return mDataConnectionTracker.getActiveApnString(apnType);
     }
 
     public LinkProperties getLinkProperties(String apnType) {
@@ -1001,6 +1001,11 @@
         return ((mDataConnectionTracker != null) && (mDataConnectionTracker.isDataPossible()));
     }
 
+    public boolean isDataConnectivityPossible(String apnType) {
+        return ((mDataConnectionTracker != null) &&
+                (mDataConnectionTracker.isDataPossible(apnType)));
+    }
+
     /**
      * simulateDataConnection
      *
diff --git a/telephony/java/com/android/internal/telephony/PhoneFactory.java b/telephony/java/com/android/internal/telephony/PhoneFactory.java
index ab0bb63..a04623f 100644
--- a/telephony/java/com/android/internal/telephony/PhoneFactory.java
+++ b/telephony/java/com/android/internal/telephony/PhoneFactory.java
@@ -106,10 +106,32 @@
                         Settings.Secure.PREFERRED_NETWORK_MODE, preferredNetworkMode);
                 Log.i(LOG_TAG, "Network Mode set to " + Integer.toString(networkMode));
 
-                //Get preferredNetworkMode from Settings.System
-                int cdmaSubscription = Settings.Secure.getInt(context.getContentResolver(),
-                        Settings.Secure.PREFERRED_CDMA_SUBSCRIPTION, preferredCdmaSubscription);
-                Log.i(LOG_TAG, "Cdma Subscription set to " + Integer.toString(cdmaSubscription));
+                // Get cdmaSubscription
+                // TODO: Change when the ril will provides a way to know at runtime
+                //       the configuration, bug 4202572. And the ril issues the
+                //       RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED, bug 4295439.
+                int cdmaSubscription;
+                int lteOnCdma = SystemProperties.getInt(
+                                TelephonyProperties.PROPERTY_NETWORK_LTE_ON_CDMA, -1);
+                switch (lteOnCdma) {
+                    case 0:
+                        cdmaSubscription = RILConstants.SUBSCRIPTION_FROM_NV;
+                        Log.i(LOG_TAG, "lteOnCdma is 0 use SUBSCRIPTION_FROM_NV");
+                        break;
+                    case 1:
+                        cdmaSubscription = RILConstants.SUBSCRIPTION_FROM_RUIM;
+                        Log.i(LOG_TAG, "lteOnCdma is 1 use SUBSCRIPTION_FROM_RUIM");
+                        break;
+                    case -1:
+                    default:
+                        //Get cdmaSubscription mode from Settings.System
+                        cdmaSubscription = Settings.Secure.getInt(context.getContentResolver(),
+                                Settings.Secure.PREFERRED_CDMA_SUBSCRIPTION,
+                                preferredCdmaSubscription);
+                        Log.i(LOG_TAG, "lteOnCdma not set, using PREFERRED_CDMA_SUBSCRIPTION");
+                        break;
+                }
+                Log.i(LOG_TAG, "Cdma Subscription set to " + cdmaSubscription);
 
                 //reads the system properties and makes commandsinterface
                 sCommandsInterface = new RIL(context, networkMode, cdmaSubscription);
@@ -156,7 +178,10 @@
         case RILConstants.NETWORK_MODE_GSM_UMTS:
             return Phone.PHONE_TYPE_GSM;
 
+        // Use CDMA Phone for the global mode including CDMA
         case RILConstants.NETWORK_MODE_GLOBAL:
+        case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO:
+        case RILConstants.NETWORK_MODE_LTE_CMDA_EVDO_GSM_WCDMA:
             return Phone.PHONE_TYPE_CDMA;
 
         case RILConstants.NETWORK_MODE_LTE_ONLY:
diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java
index 49497b4..5e506b3 100644
--- a/telephony/java/com/android/internal/telephony/PhoneProxy.java
+++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java
@@ -208,8 +208,8 @@
         return mActivePhone.getActiveApnTypes();
     }
 
-    public String getActiveApnHost() {
-        return mActivePhone.getActiveApnHost();
+    public String getActiveApnHost(String apnType) {
+        return mActivePhone.getActiveApnHost(apnType);
     }
 
     public LinkProperties getLinkProperties(String apnType) {
@@ -661,6 +661,10 @@
         return mActivePhone.isDataConnectivityPossible();
     }
 
+    public boolean isDataConnectivityPossible(String apnType) {
+        return mActivePhone.isDataConnectivityPossible(apnType);
+    }
+
     public String getDeviceId() {
         return mActivePhone.getDeviceId();
     }
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index 863daef..c052e5142 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -234,9 +234,6 @@
     // WAKE_LOCK_TIMEOUT occurs.
     int mRequestMessagesWaiting;
 
-    // Is this the first radio state change?
-    private boolean mInitialRadioStateChange = true;
-
     //I'd rather this be LinkedList or something
     ArrayList<RILRequest> mRequestsList = new ArrayList<RILRequest>();
 
@@ -613,11 +610,6 @@
 
 
     //***** Constructors
-    public
-    RIL(Context context) {
-        this(context, RILConstants.PREFERRED_NETWORK_MODE,
-                RILConstants.PREFERRED_CDMA_SUBSCRIPTION);
-    }
 
     public RIL(Context context, int networkMode, int cdmaSubscription) {
         super(context);
@@ -913,10 +905,7 @@
     getIMSI(Message result) {
         RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_IMSI, result);
 
-        if (RILJ_LOGD) riljLog(rr.serialString() +
-                              "> getIMSI:RIL_REQUEST_GET_IMSI " +
-                              RIL_REQUEST_GET_IMSI +
-                              " " + requestToString(rr.mRequest));
+        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
 
         send(rr);
     }
@@ -1394,24 +1383,6 @@
 
     public void
     setRadioPower(boolean on, Message result) {
-        //if radio is OFF set preferred NW type and cmda subscription
-        if(mInitialRadioStateChange) {
-            synchronized (mStateMonitor) {
-                if (!mState.isOn()) {
-                    setPreferredNetworkType(mNetworkMode, null);
-
-                    RILRequest rrCs = RILRequest.obtain(
-                                   RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE, null);
-                    rrCs.mp.writeInt(1);
-                    rrCs.mp.writeInt(mCdmaSubscription);
-                    if (RILJ_LOGD) {
-                        riljLog(rrCs.serialString() + "> "
-                                + requestToString(rrCs.mRequest) + " : " + mCdmaSubscription);
-                    }
-                    send(rrCs);
-                }
-            }
-        }
         RILRequest rr = RILRequest.obtain(RIL_REQUEST_RADIO_POWER, result);
 
         rr.mp.writeInt(1);
@@ -2058,26 +2029,7 @@
     }
 
     private void switchToRadioState(RadioState newState) {
-
-        if (mInitialRadioStateChange) {
-            if (newState.isOn()) {
-                /* If this is our first notification, make sure the radio
-                 * is powered off.  This gets the radio into a known state,
-                 * since it's possible for the phone proc to have restarted
-                 * (eg, if it or the runtime crashed) without the RIL
-                 * and/or radio knowing.
-                 */
-                if (RILJ_LOGD) Log.d(LOG_TAG, "Radio ON @ init; reset to OFF");
-                setRadioPower(false, null);
-            } else {
-                if (RILJ_LOGD) Log.d(LOG_TAG, "Radio OFF @ init");
-                setRadioState(newState);
-                setPreferredNetworkType(mNetworkMode, null);
-            }
-            mInitialRadioStateChange = false;
-        } else {
-            setRadioState(newState);
-        }
+        setRadioState(newState);
     }
 
     /**
@@ -2467,7 +2419,7 @@
             case RIL_UNSOL_OEM_HOOK_RAW: ret = responseRaw(p); break;
             case RIL_UNSOL_RINGBACK_TONE: ret = responseInts(p); break;
             case RIL_UNSOL_RESEND_INCALL_MUTE: ret = responseVoid(p); break;
-            case RIL_UNSOL_CDMA_SUBSCRIPTION_CHANGED: ret = responseInts(p); break;
+            case RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED: ret = responseInts(p); break;
             case RIL_UNSOl_CDMA_PRL_CHANGED: ret = responseInts(p); break;
             case RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE: ret = responseVoid(p); break;
             case RIL_UNSOL_RIL_CONNECTED: ret = responseInts(p); break;
@@ -2775,7 +2727,7 @@
                 }
                 break;
 
-            case RIL_UNSOL_CDMA_SUBSCRIPTION_CHANGED:
+            case RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED:
                 if (RILJ_LOGD) unsljLogRet(response, ret);
 
                 if (mCdmaSubscriptionChangedRegistrants != null) {
@@ -2804,6 +2756,11 @@
 
             case RIL_UNSOL_RIL_CONNECTED: {
                 if (RILJ_LOGD) unsljLogRet(response, ret);
+
+                // Initial conditions
+                setRadioPower(false, null);
+                setPreferredNetworkType(mNetworkMode, null);
+                setCdmaSubscriptionSource(mCdmaSubscription, null);
                 notifyRegistrantsRilConnectionChanged(((int[])ret)[0]);
                 break;
             }
@@ -3554,7 +3511,7 @@
             case RIL_UNSOL_OEM_HOOK_RAW: return "UNSOL_OEM_HOOK_RAW";
             case RIL_UNSOL_RINGBACK_TONE: return "UNSOL_RINGBACK_TONG";
             case RIL_UNSOL_RESEND_INCALL_MUTE: return "UNSOL_RESEND_INCALL_MUTE";
-            case RIL_UNSOL_CDMA_SUBSCRIPTION_CHANGED: return "CDMA_SUBSCRIPTION_CHANGED";
+            case RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED: return "CDMA_SUBSCRIPTION_SOURCE_CHANGED";
             case RIL_UNSOl_CDMA_PRL_CHANGED: return "UNSOL_CDMA_PRL_CHANGED";
             case RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE: return "UNSOL_EXIT_EMERGENCY_CALLBACK_MODE";
             case RIL_UNSOL_RIL_CONNECTED: return "UNSOL_RIL_CONNECTED";
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 73dfdc0..2a27926 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -286,7 +286,7 @@
     int RIL_UNSOL_OEM_HOOK_RAW = 1028;
     int RIL_UNSOL_RINGBACK_TONE = 1029;
     int RIL_UNSOL_RESEND_INCALL_MUTE = 1030;
-    int RIL_UNSOL_CDMA_SUBSCRIPTION_CHANGED = 1031;
+    int RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED = 1031;
     int RIL_UNSOl_CDMA_PRL_CHANGED = 1032;
     int RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE = 1033;
     int RIL_UNSOL_RIL_CONNECTED = 1034;
diff --git a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
index e58ccfc..695805c 100644
--- a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -398,7 +398,7 @@
         synchronized (this) {
             if (!mPendingRadioPowerOffAfterDataOff) {
                 if (dcTracker.isAnyActiveDataConnections()) {
-                    dcTracker.cleanUpAllConnections(null);
+                    dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
                     Message msg = Message.obtain(this);
                     msg.what = EVENT_SET_RADIO_POWER_OFF;
                     msg.arg1 = ++mPendingRadioPowerOffAfterDataOffTag;
@@ -410,7 +410,7 @@
                         hangupAndPowerOff();
                     }
                 } else {
-                    dcTracker.cleanUpAllConnections(null);
+                    dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
                     if (DBG) log("Data disconnected, turn off radio right away.");
                     hangupAndPowerOff();
                 }
diff --git a/telephony/java/com/android/internal/telephony/cat/RilMessageDecoder.java b/telephony/java/com/android/internal/telephony/cat/RilMessageDecoder.java
index a197c9a..2a1f508 100644
--- a/telephony/java/com/android/internal/telephony/cat/RilMessageDecoder.java
+++ b/telephony/java/com/android/internal/telephony/cat/RilMessageDecoder.java
@@ -20,15 +20,15 @@
 import com.android.internal.telephony.IccUtils;
 
 import android.os.Handler;
-import com.android.internal.util.HierarchicalState;
-import com.android.internal.util.HierarchicalStateMachine;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
 import android.os.Message;
 
 /**
  * Class used for queuing raw ril messages, decoding them into CommanParams
  * objects and sending the result back to the CAT Service.
  */
-class RilMessageDecoder extends HierarchicalStateMachine {
+class RilMessageDecoder extends StateMachine {
 
     // constants
     private static final int CMD_START = 1;
@@ -101,8 +101,9 @@
         mCmdParamsFactory = CommandParamsFactory.getInstance(this, fh);
     }
 
-    private class StateStart extends HierarchicalState {
-        @Override protected boolean processMessage(Message msg) {
+    private class StateStart extends State {
+        @Override
+        public boolean processMessage(Message msg) {
             if (msg.what == CMD_START) {
                 if (decodeMessageParams((RilMessage)msg.obj)) {
                     transitionTo(mStateCmdParamsReady);
@@ -115,8 +116,9 @@
         }
     }
 
-    private class StateCmdParamsReady extends HierarchicalState {
-        @Override protected boolean processMessage(Message msg) {
+    private class StateCmdParamsReady extends State {
+        @Override
+        public boolean processMessage(Message msg) {
             if (msg.what == CMD_PARAMS_READY) {
                 mCurrentRilMessage.mResCode = ResultCode.fromInt(msg.arg1);
                 mCurrentRilMessage.mData = msg.obj;
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
index 1cb1118..e45141a 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
@@ -164,10 +164,7 @@
     }
 
     public String getActiveApn(String apnType) {
-        if (mDataConnectionTracker instanceof CdmaDataConnectionTracker)
-            return mDataConnectionTracker.getActiveApnString();
-
-        return ((GsmDataConnectionTracker)mDataConnectionTracker).getActiveApnString(apnType);
+        return mDataConnectionTracker.getActiveApnString(apnType);
     }
 
     protected void log(String s) {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index 2637507..dc85017 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -215,6 +215,36 @@
         return allowed;
     }
 
+    /**
+     * The only circumstances under which we report that data connectivity is not
+     * possible are
+     * <ul>
+     * <li>Data is disallowed (roaming, power state, voice call, etc).</li>
+     * <li>The current data state is {@code DISCONNECTED} for a reason other than
+     * having explicitly disabled connectivity. In other words, data is not available
+     * because the phone is out of coverage or some like reason.</li>
+     * </ul>
+     * @return {@code true} if data connectivity is possible, {@code false} otherwise.
+     */
+    @Override
+    protected boolean isDataPossible() {
+        boolean dataAllowed = isDataAllowed();
+        boolean anyDataEnabled = getAnyDataEnabled();
+        boolean possible = (dataAllowed
+                && !(anyDataEnabled && (mState == State.FAILED || mState == State.IDLE)));
+        if (!possible && DBG) {
+            log("isDataPossible() " + possible + ", dataAllowed=" + dataAllowed +
+                    " anyDataEnabled=" + anyDataEnabled + " dataState=" + mState);
+        }
+        return possible;
+    }
+ 
+    @Override
+    protected boolean isDataPossible(String apnType) {
+        return isDataPossible();
+    }
+
+
     private boolean trySetupData(String reason) {
         if (DBG) log("***trySetupData due to " + (reason == null ? "(unspecified)" : reason));
 
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 816d7ad..fc1d536 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -67,7 +67,6 @@
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.Iterator;
 import java.util.Map;
 import java.util.HashMap;
 
@@ -222,7 +221,7 @@
     @Override
     protected boolean isDataPossible() {
         boolean possible = (isDataAllowed()
-                && getAnyDataEnabled() && (getOverallState() == State.CONNECTED));
+                && !(getAnyDataEnabled() && (getOverallState() == State.FAILED)));
         if (!possible && DBG && isDataAllowed()) {
             log("Data not possible.  No coverage: dataState = " + getOverallState());
         }
@@ -230,6 +229,28 @@
     }
 
     @Override
+    protected boolean isDataPossible(String apnType) {
+        ApnContext apnContext = mApnContexts.get(apnType);
+        if (apnContext == null) {
+            return false;
+        }
+        boolean apnContextIsEnabled = apnContext.isEnabled();
+        State apnContextState = apnContext.getState();
+        boolean apnTypePossible = !(apnContextIsEnabled &&
+                (apnContextState == State.FAILED));
+        boolean dataAllowed = isDataAllowed();
+        boolean possible = dataAllowed && apnTypePossible;
+
+        if (DBG) {
+            log(String.format("isDataPossible(%s): possible=%b isDataAllowed=%b " +
+                    "apnTypePossible=%b apnContextisEnabled=%b apnContextState()=%s",
+                    apnType, possible, dataAllowed, apnTypePossible,
+                    apnContextIsEnabled, apnContextState));
+        }
+        return possible;
+    }
+
+    @Override
     protected void finalize() {
         if(DBG) log("finalize");
     }
@@ -296,25 +317,29 @@
     @Override
     protected LinkProperties getLinkProperties(String apnType) {
         ApnContext apnContext = mApnContexts.get(apnType);
-        if (apnContext != null && apnContext.getDataConnection() != null) {
-             if (DBG) log("get active pdp is not null, return link properites for " + apnType);
-             return apnContext.getDataConnection().getLinkProperties();
-        } else {
-            if (DBG) log("return new LinkProperties");
-            return new LinkProperties();
+        if (apnContext != null) {
+            DataConnection dataConnection = apnContext.getDataConnection();
+            if (dataConnection != null) {
+                if (DBG) log("get active pdp is not null, return link properites for " + apnType);
+                return dataConnection.getLinkProperties();
+            }
         }
+        if (DBG) log("return new LinkProperties");
+        return new LinkProperties();
     }
 
     @Override
     protected LinkCapabilities getLinkCapabilities(String apnType) {
         ApnContext apnContext = mApnContexts.get(apnType);
-        if (apnContext!=null && apnContext.getDataConnection() != null) {
-             if (DBG) log("get active pdp is not null, return link Capabilities for " + apnType);
-             return apnContext.getDataConnection().getLinkCapabilities();
-        } else {
-            if (DBG) log("return new LinkCapabilities");
-            return new LinkCapabilities();
+        if (apnContext!=null) {
+            DataConnection dataConnection = apnContext.getDataConnection();
+            if (dataConnection != null) {
+                if (DBG) log("get active pdp is not null, return link Capabilities for " + apnType);
+                return dataConnection.getLinkCapabilities();
+            }
         }
+        if (DBG) log("return new LinkCapabilities");
+        return new LinkCapabilities();
     }
 
     @Override
@@ -323,9 +348,7 @@
         if (DBG) log("get all active apn types");
         ArrayList<String> result = new ArrayList<String>();
 
-        Iterator<ApnContext> it = mApnContexts.values().iterator();
-        while (it.hasNext()) {
-            ApnContext apnContext = it.next();
+        for (ApnContext apnContext : mApnContexts.values()) {
             if (apnContext.isReady()) {
                 result.add(apnContext.getApnType());
             }
@@ -335,14 +358,12 @@
     }
 
     @Override
-    /**
-     * Return DEFAULT APN due to the limit of the interface
-     */
-    public String getActiveApnString() {
-        if (DBG) log( "get default active apn string");
-        ApnContext defaultApnContext = mApnContexts.get(Phone.APN_TYPE_DEFAULT);
-        if (defaultApnContext != null) {
-            ApnSetting apnSetting = defaultApnContext.getApnSetting();
+    // Return active apn of specific apn type
+    public String getActiveApnString(String apnType) {
+        if (DBG) log( "get active apn string for type:" + apnType);
+        ApnContext apnContext = mApnContexts.get(apnType);
+        if (apnContext != null) {
+            ApnSetting apnSetting = apnContext.getApnSetting();
             if (apnSetting != null) {
                 return apnSetting.apn;
             }
@@ -350,14 +371,13 @@
         return null;
     }
 
-    // Return active apn of specific apn type
-    public synchronized String getActiveApnString(String apnType) {
-        if (DBG) log( "get active apn string for type:" + apnType);
+    @Override
+    public boolean isApnTypeEnabled(String apnType) {
         ApnContext apnContext = mApnContexts.get(apnType);
-        if (apnContext != null && apnContext.getApnSetting() != null) {
-            return apnContext.getApnSetting().apn;
+        if (apnContext == null) {
+            return false;
         }
-        return null;
+        return apnContext.isEnabled();
     }
 
     @Override
@@ -367,7 +387,7 @@
 
     // Return state of specific apn type
     @Override
-    public synchronized State getState(String apnType) {
+    public State getState(String apnType) {
         ApnContext apnContext = mApnContexts.get(apnType);
         if (apnContext != null) {
             return apnContext.getState();
@@ -378,25 +398,43 @@
     // Return state of overall
     public State getOverallState() {
         boolean isConnecting = false;
-        Iterator<ApnContext> it = mApnContexts.values().iterator();
-        while (it.hasNext()) {
-            ApnContext apnContext = it.next();
-            if (apnContext.getState() == State.CONNECTED ||
-                    apnContext.getState() == State.DISCONNECTING) {
-                if (DBG) log("overall state is CONNECTED");
-                return State.CONNECTED;
-            }
-            else if (apnContext.getState() == State.CONNECTING
-                    || apnContext.getState() == State.INITING) {
-                isConnecting = true;
+        boolean isFailed = true; // All enabled Apns should be FAILED.
+        boolean isAnyEnabled = false;
+
+        for (ApnContext apnContext : mApnContexts.values()) {
+            if (apnContext.isEnabled()) {
+                isAnyEnabled = true;
+                switch (apnContext.getState()) {
+                case CONNECTED:
+                case DISCONNECTING:
+                    if (DBG) log("overall state is CONNECTED");
+                    return State.CONNECTED;
+                case CONNECTING:
+                case INITING:
+                    isConnecting = true;
+                    isFailed = false;
+                    break;
+                case IDLE:
+                case SCANNING:
+                    isFailed = false;
+                    break;
+                }
             }
         }
+
+        if (!isAnyEnabled) { // Nothing enabled. return IDLE.
+            return State.IDLE;
+        }
+
         if (isConnecting) {
             if (DBG) log( "overall state is CONNECTING");
             return State.CONNECTING;
-        } else {
+        } else if (!isFailed) {
             if (DBG) log( "overall state is IDLE");
             return State.IDLE;
+        } else {
+            if (DBG) log( "overall state is FAILED");
+            return State.FAILED;
         }
     }
 
@@ -467,9 +505,8 @@
         ApnContext apnContext = mApnContexts.get(type);
 
         if (apnContext != null) {
-            apnContext.setPendingAction(ApnContext.PENDING_ACTION_APN_DISABLE);
-
             if (apnContext.getState() != State.IDLE && apnContext.getState() != State.FAILED) {
+                apnContext.setPendingAction(ApnContext.PENDING_ACTION_APN_DISABLE);
                 Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION);
                 msg.arg1 = 1; // tearDown is true;
                 // TODO - don't set things on apnContext from public functions.
@@ -481,6 +518,7 @@
                 return Phone.APN_REQUEST_STARTED;
             } else {
                 if (DBG) log("return APN_ALREADY_INACTIVE");
+                apnContext.setEnabled(false);
                 return Phone.APN_ALREADY_INACTIVE;
             }
 
@@ -507,16 +545,6 @@
         return false;
     }
 
-    protected boolean isEnabled(String apnType) {
-        ApnContext apnContext = mApnContexts.get(apnType);
-        if (apnContext == null) return false;
-        if (apnContext.getState() == State.DISCONNECTING
-                && apnContext.getPendingAction() == ApnContext.PENDING_ACTION_APN_DISABLE) {
-            return false;
-        }
-        return true;
-    }
-
     /**
      * Report on whether data connectivity is enabled for any APN.
      * @return {@code false} if data connectivity has been explicitly disabled,
@@ -554,24 +582,16 @@
          * when GPRS detaches, but we should stop the network polling.
          */
         stopNetStatPoll();
-        notifyDataConnection(Phone.REASON_GPRS_DETACHED);
+        notifyDataConnection(Phone.REASON_DATA_DETACHED);
     }
 
     private void onDataConnectionAttached() {
         if (getOverallState() == State.CONNECTED) {
             startNetStatPoll();
-            notifyDataConnection(Phone.REASON_GPRS_ATTACHED);
-        } else {
-            // Only check for default APN state
-            ApnContext defaultApnContext = mApnContexts.get(Phone.APN_TYPE_DEFAULT);
-            if (defaultApnContext != null) {
-                if (defaultApnContext.getState() == State.FAILED) {
-                    cleanUpConnection(false, defaultApnContext);
-                    defaultApnContext.getDataConnection().resetRetryCount();
-                }
-                trySetupData(Phone.REASON_GPRS_ATTACHED, Phone.APN_TYPE_DEFAULT);
-            }
+            notifyDataConnection(Phone.REASON_DATA_ATTACHED);
         }
+
+        setupDataOnReadyApns(Phone.REASON_DATA_ATTACHED);
     }
 
     @Override
@@ -580,7 +600,7 @@
         boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
 
         boolean allowed =
-                    (gprsState == ServiceState.STATE_IN_SERVICE || mAutoAttachOnCreation) &&
+                    gprsState == ServiceState.STATE_IN_SERVICE &&
                     mPhone.mSIMRecords.getRecordsLoaded() &&
                     mPhone.getState() == Phone.State.IDLE &&
                     mInternalDataEnabled &&
@@ -589,7 +609,7 @@
                     desiredPowerState;
         if (!allowed && DBG) {
             String reason = "";
-            if (!((gprsState == ServiceState.STATE_IN_SERVICE) || mAutoAttachOnCreation)) {
+            if (!(gprsState == ServiceState.STATE_IN_SERVICE)) {
                 reason += " - gprs= " + gprsState;
             }
             if (!mPhone.mSIMRecords.getRecordsLoaded()) reason += " - SIM not loaded";
@@ -607,6 +627,26 @@
         return allowed;
     }
 
+    private void setupDataOnReadyApns(String reason) {
+        // Only check for default APN state
+        for (ApnContext apnContext : mApnContexts.values()) {
+            if (apnContext.isReady()) {
+                if (apnContext.getState() == State.FAILED) {
+                    cleanUpConnection(false, apnContext);
+                    if (apnContext.getDataConnection() != null) {
+                        apnContext.getDataConnection().resetRetryCount();
+                    }
+                }
+                // Do not start ApnContext in SCANNING state
+                // FAILED state must be reset to IDLE by now
+                if (apnContext.getState() == State.IDLE) {
+                    apnContext.setReason(reason);
+                    trySetupData(apnContext);
+                }
+            }
+        }
+    }
+
     private boolean trySetupData(String reason, String type) {
         if (DBG) {
             log("***trySetupData for type:" + type +
@@ -636,7 +676,7 @@
         if (DBG) {
             log("trySetupData for type:" + apnContext.getApnType() +
                     " due to " + apnContext.getReason());
-            log("[DSAC DEB] " + "trySetupData with mIsPsRestricted=" + mIsPsRestricted);
+            log("trySetupData with mIsPsRestricted=" + mIsPsRestricted);
         }
 
         if (mPhone.getSimulatedRadioControl() != null) {
@@ -713,10 +753,8 @@
     protected void cleanUpAllConnections(boolean tearDown, String reason) {
         if (DBG) log("Clean up all connections due to " + reason);
 
-        Iterator<ApnContext> it = mApnContexts.values().iterator();
-        while (it.hasNext()) {
-            ApnContext apnContext = it.next();
-                apnContext.setReason(reason);
+        for (ApnContext apnContext : mApnContexts.values()) {
+            apnContext.setReason(reason);
             cleanUpConnection(tearDown, apnContext);
         }
 
@@ -941,14 +979,11 @@
 
         // TODO: It'd be nice to only do this if the changed entrie(s)
         // match the current operator.
+        if (DBG) log("onApnChanged createAllApnList and cleanUpAllConnections");
         createAllApnList();
-        if (DBG) log("onApnChanged clean all connections");
         cleanUpAllConnections(isConnected, Phone.REASON_APN_CHANGED);
         if (!isConnected) {
-            // TODO: Won't work for multiple connections!!!!
-            defaultApnContext.getDataConnection().resetRetryCount();
-            defaultApnContext.setReason(Phone.REASON_APN_CHANGED);
-            trySetupData(defaultApnContext);
+            setupDataOnReadyApns(Phone.REASON_APN_CHANGED);
         }
     }
 
@@ -958,7 +993,7 @@
      * via an unsolicited response (which could have happened at any
      * previous state
      */
-    private void onDataStateChanged (AsyncResult ar, boolean explicitPoll) {
+    private void onDataStateChanged (AsyncResult ar) {
         ArrayList<DataCallState> dataCallStates;
 
         dataCallStates = (ArrayList<DataCallState>)(ar.result);
@@ -970,15 +1005,12 @@
             return;
         }
 
-        Iterator<ApnContext> it = mApnContexts.values().iterator();
-        while (it.hasNext()) {
-            ApnContext apnContext = it.next();
-            onDataStateChanged(dataCallStates, explicitPoll, apnContext);
+        for (ApnContext apnContext : mApnContexts.values()) {
+            onDataStateChanged(dataCallStates, apnContext);
         }
     }
 
     private void onDataStateChanged (ArrayList<DataCallState> dataCallStates,
-                                     boolean explicitPoll,
                                      ApnContext apnContext) {
 
         if (apnContext == null) {
@@ -1008,25 +1040,16 @@
                 return;
             } else if (!dataCallStatesHasActiveCID(dataCallStates,
                     apnContext.getDataConnection().getCid())) {
-                // Here, we only consider this authoritative if we asked for the
-                // PDP list. If it was an unsolicited response, we poll again
-                // to make sure everyone agrees on the initial state.
 
-                if (!explicitPoll) {
-                    // We think it disconnected but aren't sure...poll from our side
-                    mPhone.mCM.getPDPContextList(
-                            this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE));
-                } else {
-                    Log.i(LOG_TAG, "PDP connection has dropped (active=false case). "
+                Log.i(LOG_TAG, "PDP connection has dropped (active=false case). "
                                     + " Reconnecting");
 
-                    // Log the network drop on the event log.
-                    int cid = getCellLocationId();
-                    EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP, cid,
-                            TelephonyManager.getDefault().getNetworkType());
+                // Log the network drop on the event log.
+                int cid = getCellLocationId();
+                EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP, cid,
+                        TelephonyManager.getDefault().getNetworkType());
 
-                    cleanUpConnection(true, apnContext);
-                }
+                cleanUpConnection(true, apnContext);
             }
         }
     }
@@ -1178,7 +1201,7 @@
                     // It's possible the PDP context went down and we weren't notified.
                     // Start polling the context list in an attempt to recover.
                     if (DBG) log("no DATAIN in a while; polling PDP");
-                    mPhone.mCM.getDataCallList(obtainMessage(EVENT_GET_PDP_LIST_COMPLETE));
+                    mPhone.mCM.getDataCallList(obtainMessage(EVENT_DATA_STATE_CHANGED));
 
                     mNoRecvPollCount++;
 
@@ -1298,19 +1321,9 @@
     }
 
     private void onRecordsLoaded() {
+        if (DBG) log("onRecordsLoaded: createAllApnList");
         createAllApnList();
-        for (ApnContext apnContext : mApnContexts.values()) {
-            if (apnContext.isReady()) {
-                apnContext.setReason(Phone.REASON_SIM_LOADED);
-                if (apnContext.getState() == State.FAILED) {
-                    if (DBG) {
-                        log("onRecordsLoaded clean connection for " + apnContext.getApnType());
-                    }
-                    cleanUpConnection(false, apnContext);
-                }
-                sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, apnContext));
-            }
-        }
+        setupDataOnReadyApns(Phone.REASON_SIM_LOADED);
     }
 
     @Override
@@ -1396,7 +1409,8 @@
     @Override
     // TODO: We shouldnt need this.
     protected boolean onTrySetupData(String reason) {
-        return trySetupData(reason, Phone.APN_TYPE_DEFAULT);
+        setupDataOnReadyApns(reason);
+        return true;
     }
 
     protected boolean onTrySetupData(ApnContext apnContext) {
@@ -1404,16 +1418,14 @@
     }
 
     @Override
-    // TODO: Need to understand if more than DEFAULT is impacted?
     protected void onRoamingOff() {
-        trySetupData(Phone.REASON_ROAMING_OFF, Phone.APN_TYPE_DEFAULT);
+        setupDataOnReadyApns(Phone.REASON_ROAMING_OFF);
     }
 
     @Override
-    // TODO: Need to understand if more than DEFAULT is impacted?
     protected void onRoamingOn() {
         if (getDataOnRoamingEnabled()) {
-            trySetupData(Phone.REASON_ROAMING_ON, Phone.APN_TYPE_DEFAULT);
+            setupDataOnReadyApns(Phone.REASON_ROAMING_ON);
         } else {
             if (DBG) log("Tear down data connection on roaming.");
             cleanUpAllConnections(true, Phone.REASON_ROAMING_ON);
@@ -1431,6 +1443,10 @@
             log("We're on the simulator; assuming data is connected");
         }
 
+        if (mPhone.mSIMRecords.getRecordsLoaded()) {
+            notifyDataAvailability(null);
+        }
+
         if (getOverallState() != State.IDLE) {
             cleanUpConnection(true, null);
         }
@@ -1472,8 +1488,8 @@
                 log(String.format("onDataSetupComplete: success apn=%s",
                     apnContext.getWaitingApns().get(0).apn));
             }
-            mLinkProperties = getLinkProperties(apnContext.getDataConnection());
-            mLinkCapabilities = getLinkCapabilities(apnContext.getDataConnection());
+            mLinkProperties = getLinkProperties(apnContext.getApnType());
+            mLinkCapabilities = getLinkCapabilities(apnContext.getApnType());
 
             ApnSetting apn = apnContext.getApnSetting();
             if (apn.proxy != null && apn.proxy.length() != 0) {
@@ -1582,6 +1598,12 @@
 
         mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
 
+        // Check if APN disabled.
+        if (apnContext.getPendingAction() == ApnContext.PENDING_ACTION_APN_DISABLE) {
+           apnContext.setEnabled(false);
+           apnContext.setPendingAction(ApnContext.PENDING_ACTION_NONE);
+        }
+
         // if all data connection are gone, check whether Airplane mode request was
         // pending.
         if (!isConnected()) {
@@ -1591,20 +1613,12 @@
             }
         }
 
-        // Check if APN disabled.
-        if (apnContext.getPendingAction() == ApnContext.PENDING_ACTION_APN_DISABLE) {
-           apnContext.setEnabled(false);
-           apnContext.setPendingAction(ApnContext.PENDING_ACTION_NONE);
-        }
-
-        if (TextUtils.equals(apnContext.getApnType(), Phone.APN_TYPE_DEFAULT)
-            && retryAfterDisconnected(apnContext.getReason())) {
+        // If APN is still enabled, try to bring it back up automatically
+        if (apnContext.isReady() && retryAfterDisconnected(apnContext.getReason())) {
             SystemProperties.set("gsm.defaultpdpcontext.active", "false");
-            trySetupData(apnContext);
-        }
-        else if (apnContext.getPendingAction() == ApnContext.PENDING_ACTION_RECONNECT)
-        {
-            apnContext.setPendingAction(ApnContext.PENDING_ACTION_NONE);
+            if (apnContext.getPendingAction() == ApnContext.PENDING_ACTION_RECONNECT) {
+                apnContext.setPendingAction(ApnContext.PENDING_ACTION_NONE);
+            }
             trySetupData(apnContext);
         }
     }
@@ -1612,7 +1626,7 @@
     protected void onPollPdp() {
         if (getOverallState() == State.CONNECTED) {
             // only poll when connected
-            mPhone.mCM.getPDPContextList(this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE));
+            mPhone.mCM.getDataCallList(this.obtainMessage(EVENT_DATA_STATE_CHANGED));
             sendMessageDelayed(obtainMessage(EVENT_POLL_PDP), POLL_PDP_MILLIS);
         }
     }
@@ -1637,13 +1651,7 @@
             }
         } else {
             // reset reconnect timer
-            ApnContext defaultApnContext = mApnContexts.get(Phone.APN_TYPE_DEFAULT);
-            if (defaultApnContext != null) {
-                defaultApnContext.getDataConnection().resetRetryCount();
-                mReregisterOnReconnectFailure = false;
-                // in case data setup was attempted when we were on a voice call
-                trySetupData(Phone.REASON_VOICE_CALL_ENDED, Phone.APN_TYPE_DEFAULT);
-            }
+            setupDataOnReadyApns(Phone.REASON_VOICE_CALL_ENDED);
         }
     }
 
@@ -1655,9 +1663,7 @@
     }
 
     protected boolean isConnected() {
-        Iterator<ApnContext> it = mApnContexts.values().iterator();
-         while (it.hasNext()) {
-            ApnContext apnContext = it.next();
+        for (ApnContext apnContext : mApnContexts.values()) {
             if (apnContext.getState() == State.CONNECTED) {
             return true;
             }
@@ -1668,9 +1674,7 @@
     @Override
     protected void notifyDataConnection(String reason) {
         if (DBG) log("notify all enabled connection for:" + reason);
-        Iterator<ApnContext> it = mApnContexts.values().iterator();
-        while (it.hasNext()) {
-            ApnContext apnContext = it.next();
+        for (ApnContext apnContext : mApnContexts.values()) {
             if (apnContext.isReady()) {
                 if (DBG) log("notify for type:"+apnContext.getApnType());
                 mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(),
@@ -1896,11 +1900,7 @@
                 break;
 
             case EVENT_DATA_STATE_CHANGED:
-                onDataStateChanged((AsyncResult) msg.obj, false);
-                break;
-
-            case EVENT_GET_PDP_LIST_COMPLETE:
-                onDataStateChanged((AsyncResult) msg.obj, true);
+                onDataStateChanged((AsyncResult) msg.obj);
                 break;
 
             case EVENT_POLL_PDP:
@@ -1926,7 +1926,7 @@
                  * PDP context and notify us with PDP_CONTEXT_CHANGED.
                  * But we should stop the network polling and prevent reset PDP.
                  */
-                log("[DSAC DEB] " + "EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
+                log("EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
                 stopNetStatPoll();
                 mIsPsRestricted = true;
                 break;
@@ -1936,7 +1936,7 @@
                  * When PS restrict is removed, we need setup PDP connection if
                  * PDP connection is down.
                  */
-                log("[DSAC DEB] " + "EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted);
+                log("EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted);
                 mIsPsRestricted  = false;
                 if (isConnected()) {
                     startNetStatPoll();
diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
index e69989a..8b032ff 100755
--- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
@@ -472,17 +472,23 @@
      *  provided the SIM card. Returns null of SIM is not yet ready
      */
     public String getSIMOperatorNumeric() {
-        if (imsi == null || mncLength == UNINITIALIZED || mncLength == UNKNOWN) {
+        if (imsi == null) {
+            Log.d(LOG_TAG, "getSIMOperatorNumeric: IMSI == null");
+            return null;
+        }
+        if (mncLength == UNINITIALIZED || mncLength == UNKNOWN) {
+            Log.d(LOG_TAG, "getSIMOperatorNumeric: bad mncLength");
             return null;
         }
 
-        // Length = length of MCC + length of MNC
-        // length of mcc = 3 (TS 23.003 Section 2.2)
+        // STOPSHIP: to be removed
         if (SystemProperties.getInt(com.android.internal.telephony.TelephonyProperties
                 .PROPERTY_NETWORK_LTE_ON_CDMA, 0) == 1) {
             Log.e(LOG_TAG, "getSIMOperatorNumeric: STOPSHIP bad numeric operators in lte");
             return SystemProperties.get("ro.cdma.home.operator.numeric", "310004");
         }
+        // Length = length of MCC + length of MNC
+        // length of mcc = 3 (TS 23.003 Section 2.2)
         return imsi.substring(0, 3 + mncLength);
     }
 
@@ -524,7 +530,7 @@
                     imsi = null;
                 }
 
-                Log.d(LOG_TAG, "IMSI: " + imsi.substring(0, 6) + "xxxxxxx");
+                Log.d(LOG_TAG, "IMSI: " + /* imsi.substring(0, 6) +*/ "xxxxxxx");
 
                 if (((mncLength == UNKNOWN) || (mncLength == 2)) &&
                         ((imsi != null) && (imsi.length() >= 6))) {
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 0d20496..d84f1e5 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -23,32 +23,30 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.FeatureInfo;
-import android.content.pm.IPackageDeleteObserver;
 import android.content.pm.IPackageDataObserver;
+import android.content.pm.IPackageDeleteObserver;
 import android.content.pm.IPackageInstallObserver;
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
-import android.os.RemoteException;
 
 import java.util.List;
 
 /**
  * A mock {@link android.content.pm.PackageManager} class.  All methods are non-functional and throw
- * {@link java.lang.UnsupportedOperationException}. Override it to provide the operations that you 
+ * {@link java.lang.UnsupportedOperationException}. Override it to provide the operations that you
  * need.
  */
 public class MockPackageManager extends PackageManager {
@@ -63,12 +61,12 @@
     public String[] currentToCanonicalPackageNames(String[] names) {
         throw new UnsupportedOperationException();
     }
-    
+
     @Override
     public String[] canonicalToCurrentPackageNames(String[] names) {
         throw new UnsupportedOperationException();
     }
-    
+
     @Override
     public Intent getLaunchIntentForPackage(String packageName) {
         throw new UnsupportedOperationException();
@@ -101,7 +99,7 @@
     public List<PermissionGroupInfo> getAllPermissionGroups(int flags) {
         throw new UnsupportedOperationException();
     }
-    
+
     @Override
     public ApplicationInfo getApplicationInfo(String packageName, int flags)
     throws NameNotFoundException {
@@ -176,7 +174,7 @@
     public String getNameForUid(int uid) {
         throw new UnsupportedOperationException();
     }
-    
+
     /**
      * @hide - to match hiding in superclass
      */
@@ -273,7 +271,7 @@
     public Drawable getApplicationIcon(String packageName) throws NameNotFoundException {
         throw new UnsupportedOperationException();
     }
-    
+
     @Override
     public Drawable getActivityLogo(ComponentName activityName) throws NameNotFoundException {
         throw new UnsupportedOperationException();
@@ -354,7 +352,7 @@
     public void movePackage(String packageName, IPackageMoveObserver observer, int flags) {
         throw new UnsupportedOperationException();
     }
-    
+
     @Override
     public String getInstallerPackageName(String packageName) {
         throw new UnsupportedOperationException();
@@ -368,7 +366,7 @@
             String packageName, IPackageDataObserver observer) {
         throw new UnsupportedOperationException();
     }
-    
+
     /**
      * @hide - to match hiding in superclass
      */
@@ -377,7 +375,7 @@
             String packageName, IPackageDataObserver observer) {
         throw new UnsupportedOperationException();
     }
-    
+
     /**
      * @hide - to match hiding in superclass
      */
@@ -435,7 +433,7 @@
     public void setApplicationEnabledSetting(String packageName, int newState, int flags) {
         throw new UnsupportedOperationException();
     }
-    
+
     @Override
     public int getApplicationEnabledSetting(String packageName) {
         throw new UnsupportedOperationException();
@@ -446,7 +444,7 @@
             int match, ComponentName[] set, ComponentName activity) {
         throw new UnsupportedOperationException();
     }
-    
+
     /**
      * @hide - to match hiding in superclass
      */
@@ -475,24 +473,64 @@
             List<ComponentName> outActivities, String packageName) {
         throw new UnsupportedOperationException();
     }
-    
+
     @Override
     public String[] getSystemSharedLibraryNames() {
         throw new UnsupportedOperationException();
     }
-    
+
     @Override
     public FeatureInfo[] getSystemAvailableFeatures() {
         throw new UnsupportedOperationException();
     }
-    
+
     @Override
     public boolean hasSystemFeature(String name) {
         throw new UnsupportedOperationException();
     }
-    
+
     @Override
     public boolean isSafeMode() {
         throw new UnsupportedOperationException();
     }
+
+    /**
+     * @hide
+     */
+    @Override
+    public UserInfo createUser(String name, int flags) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public List<UserInfo> getUsers() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean removeUser(int id) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void updateUserName(int id, String name) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void updateUserFlags(int id, int flags) {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/tests/ActivityTests/AndroidManifest.xml b/tests/ActivityTests/AndroidManifest.xml
index 6fa27ed..de3b6d1 100644
--- a/tests/ActivityTests/AndroidManifest.xml
+++ b/tests/ActivityTests/AndroidManifest.xml
@@ -17,6 +17,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.google.android.test.activity">
     <uses-permission android:name="android.permission.GET_TASKS" />
+    <uses-permission android:name="android.permission.REORDER_TASKS" />
+    <uses-permission android:name="android.permission.REMOVE_TASKS" />
     <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
     <application android:label="ActivityTest">
         <activity android:name="ActivityTestMain">
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
index 8c5c35a..583c13c 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
@@ -23,6 +23,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.Application;
+import android.content.ActivityNotFoundException;
 import android.os.Bundle;
 import android.graphics.BitmapFactory;
 import android.graphics.Bitmap;
@@ -43,7 +44,11 @@
 import android.util.Log;
 
 public class ActivityTestMain extends Activity {
-    private void addThumbnail(LinearLayout container, Bitmap bm) {
+    ActivityManager mAm;
+
+    private void addThumbnail(LinearLayout container, Bitmap bm,
+            final ActivityManager.RecentTaskInfo task,
+            final ActivityManager.TaskThumbnails thumbs, final int subIndex) {
         ImageView iv = new ImageView(this);
         if (bm != null) {
             iv.setImageBitmap(bm);
@@ -52,24 +57,72 @@
         int w = getResources().getDimensionPixelSize(android.R.dimen.thumbnail_width);
         int h = getResources().getDimensionPixelSize(android.R.dimen.thumbnail_height);
         container.addView(iv, new LinearLayout.LayoutParams(w, h));
+
+        iv.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (task.id >= 0 && thumbs != null) {
+                    if (subIndex < (thumbs.numSubThumbbails-1)) {
+                        mAm.removeSubTask(task.id, subIndex+1);
+                    }
+                    mAm.moveTaskToFront(task.id, ActivityManager.MOVE_TASK_WITH_HOME);
+                } else {
+                    try {
+                        startActivity(task.baseIntent);
+                    } catch (ActivityNotFoundException e) {
+                        Log.w("foo", "Unable to start task: " + e);
+                    }
+                }
+                buildUi();
+            }
+        });
+        iv.setOnLongClickListener(new View.OnLongClickListener() {
+            @Override
+            public boolean onLongClick(View v) {
+                if (task.id >= 0 && thumbs != null) {
+                    if (subIndex < 0) {
+                        mAm.removeTask(task.id, ActivityManager.REMOVE_TASK_KILL_PROCESS);
+                    } else {
+                        mAm.removeSubTask(task.id, subIndex);
+                    }
+                    buildUi();
+                    return true;
+                }
+                return false;
+            }
+        });
     }
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        ActivityManager am = (ActivityManager)getSystemService(ACTIVITY_SERVICE);
+        mAm = (ActivityManager)getSystemService(ACTIVITY_SERVICE);
+    }
 
+    @Override
+    protected void onStart() {
+        super.onStart();
+        buildUi();
+    }
+
+    private View scrollWrap(View view) {
+        ScrollView scroller = new ScrollView(this);
+        scroller.addView(view, new ScrollView.LayoutParams(ScrollView.LayoutParams.MATCH_PARENT,
+                ScrollView.LayoutParams.MATCH_PARENT));
+        return scroller;
+    }
+
+    private void buildUi() {
         LinearLayout top = new LinearLayout(this);
         top.setOrientation(LinearLayout.VERTICAL);
 
-        List<ActivityManager.RecentTaskInfo> recents = am.getRecentTasks(10,
+        List<ActivityManager.RecentTaskInfo> recents = mAm.getRecentTasks(10,
                 ActivityManager.RECENT_WITH_EXCLUDED);
         if (recents != null) {
             for (int i=0; i<recents.size(); i++) {
                 ActivityManager.RecentTaskInfo r = recents.get(i);
-                ActivityManager.TaskThumbnails tt = r != null
-                        ? am.getTaskThumbnails(r.persistentId) : null;
+                ActivityManager.TaskThumbnails tt = mAm.getTaskThumbnails(r.persistentId);
                 TextView tv = new TextView(this);
                 tv.setText(r.baseIntent.getComponent().flattenToShortString());
                 top.addView(tv, new LinearLayout.LayoutParams(
@@ -77,9 +130,9 @@
                         LinearLayout.LayoutParams.WRAP_CONTENT));
                 LinearLayout item = new LinearLayout(this);
                 item.setOrientation(LinearLayout.HORIZONTAL);
-                addThumbnail(item, tt != null ? tt.mainThumbnail : null);
+                addThumbnail(item, tt != null ? tt.mainThumbnail : null, r, tt, -1);
                 for (int j=0; j<tt.numSubThumbbails; j++) {
-                    addThumbnail(item, tt.getSubThumbnail(j));
+                    addThumbnail(item, tt.getSubThumbnail(j), r, tt, j);
                 }
                 top.addView(item, new LinearLayout.LayoutParams(
                         LinearLayout.LayoutParams.WRAP_CONTENT,
@@ -89,11 +142,4 @@
 
         setContentView(scrollWrap(top));
     }
-
-    private View scrollWrap(View view) {
-        ScrollView scroller = new ScrollView(this);
-        scroller.addView(view, new ScrollView.LayoutParams(ScrollView.LayoutParams.MATCH_PARENT,
-                ScrollView.LayoutParams.MATCH_PARENT));
-        return scroller;
-    }
 }
diff --git a/tests/BiDiTests/AndroidManifest.xml b/tests/BiDiTests/AndroidManifest.xml
index 346ace8..727f980 100644
--- a/tests/BiDiTests/AndroidManifest.xml
+++ b/tests/BiDiTests/AndroidManifest.xml
@@ -25,7 +25,8 @@
     android:versionName="1.0">
 
     <application android:label="BiDiTests">
-        <activity android:name="BiDiTestActivity">
+        <activity android:name="BiDiTestActivity"
+                android:windowSoftInputMode="stateAlwaysHidden">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
diff --git a/tests/BiDiTests/res/values/strings.xml b/tests/BiDiTests/res/values/strings.xml
index 632a02e..d20600e 100644
--- a/tests/BiDiTests/res/values/strings.xml
+++ b/tests/BiDiTests/res/values/strings.xml
@@ -20,9 +20,10 @@
     <string name="normal_long_text">mmmmmmmmmmmmmmmmmmmmmmmm</string>
     <string name="normal_long_text_2">nnnnnnnnnnnnnnnnnnnnnnnn</string>
     <string name="normal_long_text_3">Notify me when an open network is available</string>
-    <string name="arabic_text">&#x0644;&#x0627;</string>
+    <string name="arabic_text">&#x0644;&#x0627; &#x0627;&#x0646;&#x0627; hello world</string>
     <string name="chinese_text">利比亚局势或影响美俄关系发展</string>
     <string name="italic_text">Italic String</string>
     <string name="bold_text">Bold String - other text</string>
     <string name="bold_italic_text">Bold Italic String</string>
+    <string name="mixed_text_1">he said in Arabic: &#x0644;&#x0627;. Wow this is cool</string>
 </resources>
\ No newline at end of file
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java
index f00bd06..2f9b026 100644
--- a/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java
@@ -49,6 +49,7 @@
     private String BOLD_ITALIC_TEXT;
     private String ARABIC_TEXT;
     private String CHINESE_TEXT;
+    private String MIXED_TEXT_1;
 
     private Typeface typeface;
 
@@ -79,6 +80,7 @@
         BOLD_ITALIC_TEXT = context.getString(R.string.bold_italic_text);
         ARABIC_TEXT = context.getString(R.string.arabic_text);
         CHINESE_TEXT = context.getString(R.string.chinese_text);
+        MIXED_TEXT_1 = context.getString(R.string.mixed_text_1);
 
         typeface = paint.getTypeface();
         paint.setAntiAlias(true);
@@ -124,6 +126,10 @@
         // Test Chinese
         deltaX = testString(canvas, CHINESE_TEXT, ORIGIN, ORIGIN + 10 * currentTextSize,
                 paint, typeface, false, false,  Paint.DIRECTION_LTR, currentTextSize);
+
+        // Test Mixed (English and Arabic)
+        deltaX = testString(canvas, MIXED_TEXT_1, ORIGIN, ORIGIN + 12 * currentTextSize,
+                paint, typeface, false, false,  Paint.DIRECTION_LTR, currentTextSize);
     }
 
     private int testString(Canvas canvas, String text, int x, int y, Paint paint, Typeface typeface,
@@ -139,12 +145,15 @@
             paint.setTextSkewX(DEFAULT_ITALIC_SKEW_X);
         }
 
-        drawTextWithCanvasDrawText(text, canvas, x, y, textSize, Color.WHITE);
+        Log.v(TAG, "START -- drawTextWithCanvasDrawText");
+        drawTextWithCanvasDrawText(text, canvas, x, y, textSize, Color.WHITE, dir);
+        Log.v(TAG, "END   -- drawTextWithCanvasDrawText");
 
         int length = text.length();
         float[] advances = new float[length];
-        float textWidthHB = paint.getTextRunAdvances(text, 0, length, 0, length, 0, advances, 0);
-        float textWidthICU = paint.getTextRunAdvancesICU(text, 0, length, 0, length, 0, advances, 0);
+        float textWidthHB = paint.getTextRunAdvances(text, 0, length, 0, length, dir, advances, 0);
+        setPaintDir(paint, dir);
+        float textWidthICU = paint.getTextRunAdvancesICU(text, 0, length, 0, length, dir, advances, 0);
 
         logAdvances(text, textWidthHB, textWidthICU, advances);
         drawMetricsAroundText(canvas, x, y, textWidthHB, textWidthICU, textSize, Color.RED, Color.GREEN);
@@ -156,7 +165,9 @@
 //        logGlypths(glyphs, count);
 //        drawTextWithDrawGlyph(canvas, glyphs, count, x, y + currentTextSize);
 
+        Log.v(TAG, "START -- drawTextWithGlyphs");
         drawTextWithGlyphs(canvas, text, x, y + currentTextSize, dir);
+        Log.v(TAG, "END   -- drawTextWithGlyphs");
 
         // Restore old paint properties
         paint.setFakeBoldText(oldFakeBold);
@@ -165,12 +176,17 @@
         return (int) Math.ceil(textWidthHB) + TEXT_PADDING;
     }
 
+    private void setPaintDir(Paint paint, int dir) {
+        Log.v(TAG, "Setting Paint dir=" + dir);
+        paint.setBidiFlags(dir);
+    }
+
     private void drawTextWithDrawGlyph(Canvas canvas, char[] glyphs, int count, int x, int y) {
         canvas.drawGlyphs(glyphs, 0, count, x, y, paint);
     }
 
     private void drawTextWithGlyphs(Canvas canvas, String text, int x, int y, int dir) {
-        paint.setBidiFlags(dir);
+        setPaintDir(paint, dir);
         canvas.drawTextWithGlyphs(text, x, y, paint);
     }
 
@@ -182,7 +198,6 @@
     }
 
     private int getGlyphs(String text, char[] glyphs, int dir) {
-//        int dir = 1; // Paint.DIRECTION_LTR;
         return paint.getTextGlypths(text, 0, text.length(), 0, text.length(), dir, glyphs);
     }
 
@@ -195,7 +210,8 @@
     }
 
     private void drawTextWithCanvasDrawText(String text, Canvas canvas,
-            float x, float y, float textSize, int color) {
+            float x, float y, float textSize, int color, int dir) {
+        setPaintDir(paint, dir);
         paint.setColor(color);
         paint.setTextSize(textSize);
         canvas.drawText(text, x, y, paint);
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index 15570e4..fa84e93 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -40,6 +40,7 @@
           mWantUTF16(false), mValues(false),
           mCompressionMethod(0), mOutputAPKFile(NULL),
           mManifestPackageNameOverride(NULL), mInstrumentationPackageNameOverride(NULL),
+          mIsOverlayPackage(false),
           mAutoAddOverlay(false), mAssetSourceDir(NULL), mProguardFile(NULL),
           mAndroidManifestFile(NULL), mPublicOutputFile(NULL),
           mRClassDir(NULL), mResourceIntermediatesDir(NULL), mManifestMinSdkVersion(NULL),
@@ -92,6 +93,8 @@
     void setManifestPackageNameOverride(const char * val) { mManifestPackageNameOverride = val; }
     const char* getInstrumentationPackageNameOverride() const { return mInstrumentationPackageNameOverride; }
     void setInstrumentationPackageNameOverride(const char * val) { mInstrumentationPackageNameOverride = val; }
+    bool getIsOverlayPackage() const { return mIsOverlayPackage; }
+    void setIsOverlayPackage(bool val) { mIsOverlayPackage = val; }
     bool getAutoAddOverlay() { return mAutoAddOverlay; }
     void setAutoAddOverlay(bool val) { mAutoAddOverlay = val; }
 
@@ -219,6 +222,7 @@
     const char* mOutputAPKFile;
     const char* mManifestPackageNameOverride;
     const char* mInstrumentationPackageNameOverride;
+    bool        mIsOverlayPackage;
     bool        mAutoAddOverlay;
     const char* mAssetSourceDir;
     const char* mProguardFile;
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 266a02f..1e63131 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -68,6 +68,7 @@
         "        [-S resource-sources [-S resource-sources ...]] "
         "        [-F apk-file] [-J R-file-dir] \\\n"
         "        [--product product1,product2,...] \\\n"
+        "        [-o] \\\n"
         "        [raw-files-dir [raw-files-dir] ...]\n"
         "\n"
         "   Package the android resources.  It will read assets and resources that are\n"
@@ -105,6 +106,7 @@
         "   -j  specify a jar or zip file containing classes to include\n"
         "   -k  junk path of file(s) added\n"
         "   -m  make package directories under location specified by -J\n"
+        "   -o  create overlay package (ie only resources; expects <overlay-package> in manifest)\n"
 #if 0
         "   -p  pseudolocalize the default configuration\n"
 #endif
@@ -275,6 +277,9 @@
             case 'm':
                 bundle.setMakePackageDirs(true);
                 break;
+            case 'o':
+                bundle.setIsOverlayPackage(true);
+                break;
 #if 0
             case 'p':
                 bundle.setPseudolocalize(true);
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 3dcc093..a88476e 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -3696,7 +3696,9 @@
 {
     sp<Package> p = mPackages.valueFor(package);
     if (p == NULL) {
-        if (mIsAppPackage) {
+        if (mBundle->getIsOverlayPackage()) {
+            p = new Package(package, 0x00);
+        } else if (mIsAppPackage) {
             if (mHaveAppPackage) {
                 fprintf(stderr, "Adding multiple application package resources; only one is allowed.\n"
                                 "Use -x to create extended resources.\n");
diff --git a/wifi/java/android/net/wifi/SupplicantStateTracker.java b/wifi/java/android/net/wifi/SupplicantStateTracker.java
index 3cde949..9ae26da 100644
--- a/wifi/java/android/net/wifi/SupplicantStateTracker.java
+++ b/wifi/java/android/net/wifi/SupplicantStateTracker.java
@@ -16,8 +16,8 @@
 
 package android.net.wifi;
 
-import com.android.internal.util.HierarchicalState;
-import com.android.internal.util.HierarchicalStateMachine;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
 
 import android.net.wifi.WifiStateMachine.StateChangeResult;
 import android.content.Context;
@@ -33,7 +33,7 @@
  * - detect a failed WPA handshake that loops indefinitely
  * - authentication failure handling
  */
-class SupplicantStateTracker extends HierarchicalStateMachine {
+class SupplicantStateTracker extends StateMachine {
 
     private static final String TAG = "SupplicantStateTracker";
     private static final boolean DBG = false;
@@ -53,14 +53,14 @@
 
     private Context mContext;
 
-    private HierarchicalState mUninitializedState = new UninitializedState();
-    private HierarchicalState mDefaultState = new DefaultState();
-    private HierarchicalState mInactiveState = new InactiveState();
-    private HierarchicalState mDisconnectState = new DisconnectedState();
-    private HierarchicalState mScanState = new ScanState();
-    private HierarchicalState mHandshakeState = new HandshakeState();
-    private HierarchicalState mCompletedState = new CompletedState();
-    private HierarchicalState mDormantState = new DormantState();
+    private State mUninitializedState = new UninitializedState();
+    private State mDefaultState = new DefaultState();
+    private State mInactiveState = new InactiveState();
+    private State mDisconnectState = new DisconnectedState();
+    private State mScanState = new ScanState();
+    private State mHandshakeState = new HandshakeState();
+    private State mCompletedState = new CompletedState();
+    private State mDormantState = new DormantState();
 
     public SupplicantStateTracker(Context context, WifiStateMachine wsm, Handler target) {
         super(TAG, target.getLooper());
@@ -146,7 +146,7 @@
      * HSM states
      *******************************************************/
 
-    class DefaultState extends HierarchicalState {
+    class DefaultState extends State {
         @Override
          public void enter() {
              if (DBG) Log.d(TAG, getName() + "\n");
@@ -188,21 +188,21 @@
      * or after we have lost the control channel
      * connection to the supplicant
      */
-    class UninitializedState extends HierarchicalState {
+    class UninitializedState extends State {
         @Override
          public void enter() {
              if (DBG) Log.d(TAG, getName() + "\n");
          }
     }
 
-    class InactiveState extends HierarchicalState {
+    class InactiveState extends State {
         @Override
          public void enter() {
              if (DBG) Log.d(TAG, getName() + "\n");
          }
     }
 
-    class DisconnectedState extends HierarchicalState {
+    class DisconnectedState extends State {
         @Override
          public void enter() {
              if (DBG) Log.d(TAG, getName() + "\n");
@@ -221,14 +221,14 @@
          }
     }
 
-    class ScanState extends HierarchicalState {
+    class ScanState extends State {
         @Override
          public void enter() {
              if (DBG) Log.d(TAG, getName() + "\n");
          }
     }
 
-    class HandshakeState extends HierarchicalState {
+    class HandshakeState extends State {
         /**
          * The max number of the WPA supplicant loop iterations before we
          * decide that the loop should be terminated:
@@ -277,7 +277,7 @@
         }
     }
 
-    class CompletedState extends HierarchicalState {
+    class CompletedState extends State {
         @Override
          public void enter() {
              if (DBG) Log.d(TAG, getName() + "\n");
@@ -318,7 +318,7 @@
     }
 
     //TODO: remove after getting rid of the state in supplicant
-    class DormantState extends HierarchicalState {
+    class DormantState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 46c07a3..f7157d4 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -73,8 +73,8 @@
 
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.util.AsyncChannel;
-import com.android.internal.util.HierarchicalState;
-import com.android.internal.util.HierarchicalStateMachine;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
 
 import java.net.InetAddress;
 import java.util.ArrayList;
@@ -88,7 +88,7 @@
  *
  * @hide
  */
-public class WifiStateMachine extends HierarchicalStateMachine {
+public class WifiStateMachine extends StateMachine {
 
     private static final String TAG = "WifiStateMachine";
     private static final String NETWORKTYPE = "WIFI";
@@ -358,50 +358,50 @@
     private static final int MAX_RSSI = 256;
 
     /* Default parent state */
-    private HierarchicalState mDefaultState = new DefaultState();
+    private State mDefaultState = new DefaultState();
     /* Temporary initial state */
-    private HierarchicalState mInitialState = new InitialState();
+    private State mInitialState = new InitialState();
     /* Unloading the driver */
-    private HierarchicalState mDriverUnloadingState = new DriverUnloadingState();
+    private State mDriverUnloadingState = new DriverUnloadingState();
     /* Loading the driver */
-    private HierarchicalState mDriverUnloadedState = new DriverUnloadedState();
+    private State mDriverUnloadedState = new DriverUnloadedState();
     /* Driver load/unload failed */
-    private HierarchicalState mDriverFailedState = new DriverFailedState();
+    private State mDriverFailedState = new DriverFailedState();
     /* Driver loading */
-    private HierarchicalState mDriverLoadingState = new DriverLoadingState();
+    private State mDriverLoadingState = new DriverLoadingState();
     /* Driver loaded */
-    private HierarchicalState mDriverLoadedState = new DriverLoadedState();
+    private State mDriverLoadedState = new DriverLoadedState();
     /* Driver loaded, waiting for supplicant to start */
-    private HierarchicalState mSupplicantStartingState = new SupplicantStartingState();
+    private State mSupplicantStartingState = new SupplicantStartingState();
     /* Driver loaded and supplicant ready */
-    private HierarchicalState mSupplicantStartedState = new SupplicantStartedState();
+    private State mSupplicantStartedState = new SupplicantStartedState();
     /* Waiting for supplicant to stop and monitor to exit */
-    private HierarchicalState mSupplicantStoppingState = new SupplicantStoppingState();
+    private State mSupplicantStoppingState = new SupplicantStoppingState();
     /* Driver start issued, waiting for completed event */
-    private HierarchicalState mDriverStartingState = new DriverStartingState();
+    private State mDriverStartingState = new DriverStartingState();
     /* Driver started */
-    private HierarchicalState mDriverStartedState = new DriverStartedState();
+    private State mDriverStartedState = new DriverStartedState();
     /* Driver stopping */
-    private HierarchicalState mDriverStoppingState = new DriverStoppingState();
+    private State mDriverStoppingState = new DriverStoppingState();
     /* Driver stopped */
-    private HierarchicalState mDriverStoppedState = new DriverStoppedState();
+    private State mDriverStoppedState = new DriverStoppedState();
     /* Scan for networks, no connection will be established */
-    private HierarchicalState mScanModeState = new ScanModeState();
+    private State mScanModeState = new ScanModeState();
     /* Connecting to an access point */
-    private HierarchicalState mConnectModeState = new ConnectModeState();
+    private State mConnectModeState = new ConnectModeState();
     /* Fetching IP after network connection (assoc+auth complete) */
-    private HierarchicalState mConnectingState = new ConnectingState();
+    private State mConnectingState = new ConnectingState();
     /* Connected with IP addr */
-    private HierarchicalState mConnectedState = new ConnectedState();
+    private State mConnectedState = new ConnectedState();
     /* disconnect issued, waiting for network disconnect confirmation */
-    private HierarchicalState mDisconnectingState = new DisconnectingState();
+    private State mDisconnectingState = new DisconnectingState();
     /* Network is not connected, supplicant assoc+auth is not complete */
-    private HierarchicalState mDisconnectedState = new DisconnectedState();
+    private State mDisconnectedState = new DisconnectedState();
     /* Waiting for WPS to be completed*/
-    private HierarchicalState mWaitForWpsCompletionState = new WaitForWpsCompletionState();
+    private State mWaitForWpsCompletionState = new WaitForWpsCompletionState();
 
     /* Soft Ap is running */
-    private HierarchicalState mSoftApStartedState = new SoftApStartedState();
+    private State mSoftApStartedState = new SoftApStartedState();
 
 
     /**
@@ -1543,7 +1543,7 @@
      * HSM states
      *******************************************************/
 
-    class DefaultState extends HierarchicalState {
+    class DefaultState extends State {
         @Override
         public boolean processMessage(Message message) {
             if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
@@ -1617,7 +1617,7 @@
         }
     }
 
-    class InitialState extends HierarchicalState {
+    class InitialState extends State {
         @Override
         //TODO: could move logging into a common class
         public void enter() {
@@ -1636,7 +1636,7 @@
         }
     }
 
-    class DriverLoadingState extends HierarchicalState {
+    class DriverLoadingState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -1715,7 +1715,7 @@
         }
     }
 
-    class DriverLoadedState extends HierarchicalState {
+    class DriverLoadedState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -1768,7 +1768,7 @@
         }
     }
 
-    class DriverUnloadingState extends HierarchicalState {
+    class DriverUnloadingState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -1849,7 +1849,7 @@
         }
     }
 
-    class DriverUnloadedState extends HierarchicalState {
+    class DriverUnloadedState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -1870,7 +1870,7 @@
         }
     }
 
-    class DriverFailedState extends HierarchicalState {
+    class DriverFailedState extends State {
         @Override
         public void enter() {
             Log.e(TAG, getName() + "\n");
@@ -1884,7 +1884,7 @@
     }
 
 
-    class SupplicantStartingState extends HierarchicalState {
+    class SupplicantStartingState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -1956,7 +1956,7 @@
         }
     }
 
-    class SupplicantStartedState extends HierarchicalState {
+    class SupplicantStartedState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -2084,7 +2084,7 @@
         }
     }
 
-    class SupplicantStoppingState extends HierarchicalState {
+    class SupplicantStoppingState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -2127,7 +2127,7 @@
         }
     }
 
-    class DriverStartingState extends HierarchicalState {
+    class DriverStartingState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -2168,7 +2168,7 @@
         }
     }
 
-    class DriverStartedState extends HierarchicalState {
+    class DriverStartedState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -2272,7 +2272,7 @@
         }
     }
 
-    class DriverStoppingState extends HierarchicalState {
+    class DriverStoppingState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -2308,7 +2308,7 @@
         }
     }
 
-    class DriverStoppedState extends HierarchicalState {
+    class DriverStoppedState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -2332,7 +2332,7 @@
         }
     }
 
-    class ScanModeState extends HierarchicalState {
+    class ScanModeState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -2369,7 +2369,7 @@
         }
     }
 
-    class ConnectModeState extends HierarchicalState {
+    class ConnectModeState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -2479,7 +2479,7 @@
         }
     }
 
-    class ConnectingState extends HierarchicalState {
+    class ConnectingState extends State {
         boolean mModifiedBluetoothCoexistenceMode;
         int mPowerMode;
         boolean mUseStaticIp;
@@ -2677,7 +2677,7 @@
       }
     }
 
-    class ConnectedState extends HierarchicalState {
+    class ConnectedState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -2789,7 +2789,7 @@
         }
     }
 
-    class DisconnectingState extends HierarchicalState {
+    class DisconnectingState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -2819,7 +2819,7 @@
         }
     }
 
-    class DisconnectedState extends HierarchicalState {
+    class DisconnectedState extends State {
         private boolean mAlarmEnabled = false;
         /* This is set from the overlay config file or from a secure setting.
          * A value of 0 disables scanning in the framework.
@@ -2931,7 +2931,7 @@
         }
     }
 
-    class WaitForWpsCompletionState extends HierarchicalState {
+    class WaitForWpsCompletionState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
@@ -2970,7 +2970,7 @@
         }
     }
 
-    class SoftApStartedState extends HierarchicalState {
+    class SoftApStartedState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
diff --git a/wifi/java/android/net/wifi/WpsStateMachine.java b/wifi/java/android/net/wifi/WpsStateMachine.java
index 32d77a1..120b228 100644
--- a/wifi/java/android/net/wifi/WpsStateMachine.java
+++ b/wifi/java/android/net/wifi/WpsStateMachine.java
@@ -17,8 +17,8 @@
 package android.net.wifi;
 
 import com.android.internal.util.AsyncChannel;
-import com.android.internal.util.HierarchicalState;
-import com.android.internal.util.HierarchicalStateMachine;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
 
 import android.content.Context;
 import android.content.Intent;
@@ -46,7 +46,7 @@
  * reloads the configuration and updates the IP and proxy
  * settings, if any.
  */
-class WpsStateMachine extends HierarchicalStateMachine {
+class WpsStateMachine extends StateMachine {
 
     private static final String TAG = "WpsStateMachine";
     private static final boolean DBG = false;
@@ -58,9 +58,9 @@
     private Context mContext;
     AsyncChannel mReplyChannel = new AsyncChannel();
 
-    private HierarchicalState mDefaultState = new DefaultState();
-    private HierarchicalState mInactiveState = new InactiveState();
-    private HierarchicalState mActiveState = new ActiveState();
+    private State mDefaultState = new DefaultState();
+    private State mInactiveState = new InactiveState();
+    private State mActiveState = new ActiveState();
 
     public WpsStateMachine(Context context, WifiStateMachine wsm, Handler target) {
         super(TAG, target.getLooper());
@@ -82,7 +82,7 @@
      * HSM states
      *******************************************************/
 
-    class DefaultState extends HierarchicalState {
+    class DefaultState extends State {
         @Override
          public void enter() {
              if (DBG) Log.d(TAG, getName() + "\n");
@@ -128,7 +128,7 @@
         }
     }
 
-    class ActiveState extends HierarchicalState {
+    class ActiveState extends State {
         @Override
          public void enter() {
              if (DBG) Log.d(TAG, getName() + "\n");
@@ -182,7 +182,7 @@
         }
     }
 
-    class InactiveState extends HierarchicalState {
+    class InactiveState extends State {
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");