Merge "improve documentation for the ROTATION_VECTOR sensor"
diff --git a/api/current.txt b/api/current.txt
index a98ffae..a7f0368 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -21025,6 +21025,7 @@
 
   public static abstract interface TextureView.SurfaceTextureListener {
     method public abstract void onSurfaceTextureAvailable(android.graphics.SurfaceTexture);
+    method public abstract void onSurfaceTextureSizeChanged(android.graphics.SurfaceTexture, int, int);
   }
 
   public class TouchDelegate {
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 087753b..82186dd 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -931,7 +931,7 @@
 
         // associate search with owner activity
         final ComponentName appName = getAssociatedActivity();
-        if (appName != null) {
+        if (appName != null && searchManager.getSearchableInfo(appName) != null) {
             searchManager.startSearch(null, false, appName, null, false);
             dismiss();
             return true;
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 49db72b..9011f73 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -1179,6 +1179,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_AUTO_EXPOSURE_LOCK = "auto-exposure-lock";
+        private static final String KEY_AUTO_EXPOSURE_LOCK_SUPPORTED = "auto-exposure-lock-supported";
         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";
@@ -1195,6 +1197,7 @@
         private static final String SUPPORTED_VALUES_SUFFIX = "-values";
 
         private static final String TRUE = "true";
+        private static final String FALSE = "false";
 
         // Values for white balance settings.
         public static final String WHITE_BALANCE_AUTO = "auto";
@@ -2463,6 +2466,80 @@
         }
 
         /**
+         * Sets the auto-exposure lock state. Applications should check
+         * {@link #isAutoExposureLockSupported} before using this method.
+         *
+         * If set to true, the camera auto-exposure routine will pause until the
+         * lock is set to false. Exposure compensation settings changes will
+         * still take effect while auto-exposure is locked. Stopping preview
+         * with {@link #stopPreview()}, or triggering still image capture with
+         * {@link #takePicture(Camera.ShutterCallback, Camera.PictureCallback,
+         * Camera.PictureCallback)}, will automatically set the lock to
+         * false. However, the lock can be re-enabled before preview is
+         * re-started to keep the same AE parameters. Exposure compensation, in
+         * conjunction with re-enabling the AE lock after each still capture,
+         * can be used to capture an exposure-bracketed burst of images, for
+         * example. Auto-exposure state, including the lock state, will not be
+         * maintained after camera {@link #release()} is called.  Locking
+         * auto-exposure after {@link #open()} but before the first call to
+         * {@link #startPreview()} will not allow the auto-exposure routine to
+         * run at all, and may result in severely over- or under-exposed images.
+         *
+         * The driver may also independently lock auto-exposure after auto-focus
+         * completes. If this is undesirable, be sure to always set the
+         * auto-exposure lock to false after the
+         * {@link AutoFocusCallback#onAutoFocus(boolean, Camera)} callback is
+         * received. The {@link #getAutoExposureLock()} method can be used after
+         * the callback to determine if the camera has locked auto-exposure
+         * independently.
+         *
+         * @param toggle new state of the auto-exposure lock. True means that
+         *        auto-exposure is locked, false means that the auto-exposure
+         *        routine is free to run normally.
+         *
+         * @hide
+         */
+        public void setAutoExposureLock(boolean toggle) {
+            set(KEY_AUTO_EXPOSURE_LOCK, toggle ? TRUE : FALSE);
+        }
+
+        /**
+         * Gets the state of the auto-exposure lock. Applications should check
+         * {@link #isAutoExposureLockSupported} before using this method. See
+         * {@link #setAutoExposureLock} for details about the lock.
+         *
+         * @return State of the auto-exposure lock. Returns true if
+         *         auto-exposure is currently locked, and false otherwise. The
+         *         auto-exposure lock may be independently enabled by the camera
+         *         subsystem when auto-focus has completed. This method can be
+         *         used after the {@link AutoFocusCallback#onAutoFocus(boolean,
+         *         Camera)} callback to determine if the camera has locked AE.
+         *
+         * @see #setAutoExposureLock(boolean)
+         *
+         * @hide
+         */
+        public boolean getAutoExposureLock() {
+            String str = get(KEY_AUTO_EXPOSURE_LOCK);
+            return TRUE.equals(str);
+        }
+
+        /**
+         * Returns true if auto-exposure locking is supported. Applications
+         * should call this before trying to lock auto-exposure. See
+         * {@link #setAutoExposureLock} for details about the lock.
+         *
+         * @return true if auto-exposure lock is supported.
+         * @see #setAutoExposureLock(boolean)
+         *
+         * @hide
+         */
+        public boolean isAutoExposureLockSupported() {
+            String str = get(KEY_AUTO_EXPOSURE_LOCK_SUPPORTED);
+            return TRUE.equals(str);
+        }
+
+        /**
          * Gets current zoom value. This also works when smooth zoom is in
          * progress. Applications should check {@link #isZoomSupported} before
          * using this method.
diff --git a/core/java/android/net/DhcpInfoInternal.java b/core/java/android/net/DhcpInfoInternal.java
index 7396669..860da0a 100644
--- a/core/java/android/net/DhcpInfoInternal.java
+++ b/core/java/android/net/DhcpInfoInternal.java
@@ -22,6 +22,8 @@
 import java.net.InetAddress;
 import java.net.Inet4Address;
 import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collection;
 
 /**
  * A simple object for retrieving the results of a DHCP request.
@@ -31,7 +33,6 @@
 public class DhcpInfoInternal {
     private final static String TAG = "DhcpInfoInternal";
     public String ipAddress;
-    public String gateway;
     public int prefixLength;
 
     public String dns1;
@@ -40,7 +41,14 @@
     public String serverAddress;
     public int leaseDuration;
 
+    private Collection<RouteInfo> routes;
+
     public DhcpInfoInternal() {
+        routes = new ArrayList<RouteInfo>();
+    }
+
+    public void addRoute(RouteInfo routeInfo) {
+        routes.add(routeInfo);
     }
 
     private int convertToInt(String addr) {
@@ -58,7 +66,12 @@
     public DhcpInfo makeDhcpInfo() {
         DhcpInfo info = new DhcpInfo();
         info.ipAddress = convertToInt(ipAddress);
-        info.gateway = convertToInt(gateway);
+        for (RouteInfo route : routes) {
+            if (route.isDefaultRoute()) {
+                info.gateway = convertToInt(route.getGateway().getHostAddress());
+                break;
+            }
+        }
         try {
             InetAddress inetAddress = NetworkUtils.numericToInetAddress(ipAddress);
             info.netmask = NetworkUtils.prefixLengthToNetmaskInt(prefixLength);
@@ -81,8 +94,8 @@
     public LinkProperties makeLinkProperties() {
         LinkProperties p = new LinkProperties();
         p.addLinkAddress(makeLinkAddress());
-        if (TextUtils.isEmpty(gateway) == false) {
-            p.addGateway(NetworkUtils.numericToInetAddress(gateway));
+        for (RouteInfo route : routes) {
+            p.addRoute(route);
         }
         if (TextUtils.isEmpty(dns1) == false) {
             p.addDns(NetworkUtils.numericToInetAddress(dns1));
@@ -98,8 +111,10 @@
     }
 
     public String toString() {
+        String routeString = "";
+        for (RouteInfo route : routes) routeString += route.toString() + " | ";
         return "addr: " + ipAddress + "/" + prefixLength +
-                " gateway: " + gateway +
+                " routes: " + routeString +
                 " dns: " + dns1 + "," + dns2 +
                 " dhcpServer: " + serverAddress +
                 " leaseDuration: " + leaseDuration;
diff --git a/core/java/android/net/DhcpStateMachine.java b/core/java/android/net/DhcpStateMachine.java
new file mode 100644
index 0000000..eaf087f
--- /dev/null
+++ b/core/java/android/net/DhcpStateMachine.java
@@ -0,0 +1,353 @@
+/*
+ * 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.net;
+
+import com.android.internal.util.Protocol;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.DhcpInfoInternal;
+import android.net.NetworkUtils;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.util.Log;
+
+/**
+ * StateMachine that interacts with the native DHCP client and can talk to
+ * a controller that also needs to be a StateMachine
+ *
+ * The Dhcp state machine provides the following features:
+ * - Wakeup and renewal using the native DHCP client  (which will not renew
+ *   on its own when the device is in suspend state and this can lead to device
+ *   holding IP address beyond expiry)
+ * - A notification right before DHCP request or renewal is started. This
+ *   can be used for any additional setup before DHCP. For example, wifi sets
+ *   BT-Wifi coex settings right before DHCP is initiated
+ *
+ * @hide
+ */
+public class DhcpStateMachine extends StateMachine {
+
+    private static final String TAG = "DhcpStateMachine";
+    private static final boolean DBG = false;
+
+
+    /* A StateMachine that controls the DhcpStateMachine */
+    private StateMachine mController;
+
+    private Context mContext;
+    private BroadcastReceiver mBroadcastReceiver;
+    private AlarmManager mAlarmManager;
+    private PendingIntent mDhcpRenewalIntent;
+    private PowerManager.WakeLock mDhcpRenewWakeLock;
+    private static final String WAKELOCK_TAG = "DHCP";
+
+    private static final int DHCP_RENEW = 0;
+    private static final String ACTION_DHCP_RENEW = "android.net.wifi.DHCP_RENEW";
+
+    private enum DhcpAction {
+        START,
+        RENEW
+    };
+
+    private String mInterfaceName;
+    private boolean mRegisteredForPreDhcpNotification = false;
+
+    private static final int BASE = Protocol.BASE_DHCP;
+
+    /* Commands from controller to start/stop DHCP */
+    public static final int CMD_START_DHCP                  = BASE + 1;
+    public static final int CMD_STOP_DHCP                   = BASE + 2;
+    public static final int CMD_RENEW_DHCP                  = BASE + 3;
+
+    /* Notification from DHCP state machine prior to DHCP discovery/renewal */
+    public static final int CMD_PRE_DHCP_ACTION             = BASE + 4;
+    /* Notification from DHCP state machine post DHCP discovery/renewal. Indicates
+     * success/failure */
+    public static final int CMD_POST_DHCP_ACTION            = BASE + 5;
+
+    /* Command from controller to indicate DHCP discovery/renewal can continue
+     * after pre DHCP action is complete */
+    public static final int CMD_PRE_DHCP_ACTION_COMPLETE    = BASE + 6;
+
+    /* Message.arg1 arguments to CMD_POST_DHCP notification */
+    public static final int DHCP_SUCCESS = 1;
+    public static final int DHCP_FAILURE = 2;
+
+    private State mDefaultState = new DefaultState();
+    private State mStoppedState = new StoppedState();
+    private State mWaitBeforeStartState = new WaitBeforeStartState();
+    private State mRunningState = new RunningState();
+    private State mWaitBeforeRenewalState = new WaitBeforeRenewalState();
+
+    private DhcpStateMachine(Context context, StateMachine controller, String intf) {
+        super(TAG);
+
+        mContext = context;
+        mController = controller;
+        mInterfaceName = intf;
+
+        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+        Intent dhcpRenewalIntent = new Intent(ACTION_DHCP_RENEW, null);
+        mDhcpRenewalIntent = PendingIntent.getBroadcast(mContext, DHCP_RENEW, dhcpRenewalIntent, 0);
+
+        PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+        mDhcpRenewWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
+
+        mBroadcastReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                //DHCP renew
+                if (DBG) Log.d(TAG, "Sending a DHCP renewal " + this);
+                //acquire a 40s wakelock to finish DHCP renewal
+                mDhcpRenewWakeLock.acquire(40000);
+                sendMessage(CMD_RENEW_DHCP);
+            }
+        };
+        mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(ACTION_DHCP_RENEW));
+
+        addState(mDefaultState);
+            addState(mStoppedState, mDefaultState);
+            addState(mWaitBeforeStartState, mDefaultState);
+            addState(mRunningState, mDefaultState);
+            addState(mWaitBeforeRenewalState, mDefaultState);
+
+        setInitialState(mStoppedState);
+    }
+
+    public static DhcpStateMachine makeDhcpStateMachine(Context context, StateMachine controller,
+            String intf) {
+        DhcpStateMachine dsm = new DhcpStateMachine(context, controller, intf);
+        dsm.start();
+        return dsm;
+    }
+
+    /**
+     * This sends a notification right before DHCP request/renewal so that the
+     * controller can do certain actions before DHCP packets are sent out.
+     * When the controller is ready, it sends a CMD_PRE_DHCP_ACTION_COMPLETE message
+     * to indicate DHCP can continue
+     *
+     * This is used by Wifi at this time for the purpose of doing BT-Wifi coex
+     * handling during Dhcp
+     */
+    public void registerForPreDhcpNotification() {
+        mRegisteredForPreDhcpNotification = true;
+    }
+
+    class DefaultState extends State {
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch (message.what) {
+                case CMD_RENEW_DHCP:
+                    Log.e(TAG, "Error! Failed to handle a DHCP renewal on " + mInterfaceName);
+                    break;
+                case SM_QUIT_CMD:
+                    mContext.unregisterReceiver(mBroadcastReceiver);
+                    //let parent kill the state machine
+                    return NOT_HANDLED;
+                default:
+                    Log.e(TAG, "Error! unhandled message  " + message);
+                    break;
+            }
+            return HANDLED;
+        }
+    }
+
+
+    class StoppedState extends State {
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            boolean retValue = HANDLED;
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch (message.what) {
+                case CMD_START_DHCP:
+                    if (mRegisteredForPreDhcpNotification) {
+                        /* Notify controller before starting DHCP */
+                        mController.sendMessage(CMD_PRE_DHCP_ACTION);
+                        transitionTo(mWaitBeforeStartState);
+                    } else {
+                        if (runDhcp(DhcpAction.START)) {
+                            transitionTo(mRunningState);
+                        }
+                    }
+                    break;
+                case CMD_STOP_DHCP:
+                    //ignore
+                    break;
+                default:
+                    retValue = NOT_HANDLED;
+                    break;
+            }
+            return retValue;
+        }
+    }
+
+    class WaitBeforeStartState extends State {
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            boolean retValue = HANDLED;
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch (message.what) {
+                case CMD_PRE_DHCP_ACTION_COMPLETE:
+                    if (runDhcp(DhcpAction.START)) {
+                        transitionTo(mRunningState);
+                    } else {
+                        transitionTo(mStoppedState);
+                    }
+                    break;
+                case CMD_STOP_DHCP:
+                    transitionTo(mStoppedState);
+                    break;
+                case CMD_START_DHCP:
+                    //ignore
+                    break;
+                default:
+                    retValue = NOT_HANDLED;
+                    break;
+            }
+            return retValue;
+        }
+    }
+
+    class RunningState extends State {
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            boolean retValue = HANDLED;
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch (message.what) {
+                case CMD_STOP_DHCP:
+                    mAlarmManager.cancel(mDhcpRenewalIntent);
+                    if (!NetworkUtils.stopDhcp(mInterfaceName)) {
+                        Log.e(TAG, "Failed to stop Dhcp on " + mInterfaceName);
+                    }
+                    transitionTo(mStoppedState);
+                    break;
+                case CMD_RENEW_DHCP:
+                    if (mRegisteredForPreDhcpNotification) {
+                        /* Notify controller before starting DHCP */
+                        mController.sendMessage(CMD_PRE_DHCP_ACTION);
+                        transitionTo(mWaitBeforeRenewalState);
+                    } else {
+                        if (!runDhcp(DhcpAction.RENEW)) {
+                            transitionTo(mStoppedState);
+                        }
+                    }
+                    break;
+                case CMD_START_DHCP:
+                    //ignore
+                    break;
+                default:
+                    retValue = NOT_HANDLED;
+            }
+            return retValue;
+        }
+    }
+
+    class WaitBeforeRenewalState extends State {
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            boolean retValue = HANDLED;
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch (message.what) {
+                case CMD_STOP_DHCP:
+                    mAlarmManager.cancel(mDhcpRenewalIntent);
+                    if (!NetworkUtils.stopDhcp(mInterfaceName)) {
+                        Log.e(TAG, "Failed to stop Dhcp on " + mInterfaceName);
+                    }
+                    transitionTo(mStoppedState);
+                    break;
+                case CMD_PRE_DHCP_ACTION_COMPLETE:
+                    if (runDhcp(DhcpAction.RENEW)) {
+                       transitionTo(mRunningState);
+                    } else {
+                       transitionTo(mStoppedState);
+                    }
+                    break;
+                case CMD_START_DHCP:
+                    //ignore
+                    break;
+                default:
+                    retValue = NOT_HANDLED;
+                    break;
+            }
+            return retValue;
+        }
+    }
+
+    private boolean runDhcp(DhcpAction dhcpAction) {
+        boolean success = false;
+        DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal();
+
+        if (dhcpAction == DhcpAction.START) {
+            Log.d(TAG, "DHCP request on " + mInterfaceName);
+            success = NetworkUtils.runDhcp(mInterfaceName, dhcpInfoInternal);
+        } else if (dhcpAction == DhcpAction.RENEW) {
+            Log.d(TAG, "DHCP renewal on " + mInterfaceName);
+            success = NetworkUtils.runDhcpRenew(mInterfaceName, dhcpInfoInternal);
+        }
+
+        if (success) {
+            Log.d(TAG, "DHCP succeeded on " + mInterfaceName);
+            //Do it a bit earlier than half the lease duration time
+            //to beat the native DHCP client and avoid extra packets
+            //48% for one hour lease time = 29 minutes
+            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                    SystemClock.elapsedRealtime() +
+                    dhcpInfoInternal.leaseDuration * 480, //in milliseconds
+                    mDhcpRenewalIntent);
+
+            mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, dhcpInfoInternal)
+                .sendToTarget();
+        } else {
+            Log.d(TAG, "DHCP failed on " + mInterfaceName + ": " +
+                    NetworkUtils.getDhcpError());
+            NetworkUtils.stopDhcp(mInterfaceName);
+            mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0)
+                .sendToTarget();
+        }
+        return success;
+    }
+}
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index e88292f..61acf2b 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -54,7 +54,7 @@
     String mIfaceName;
     private Collection<LinkAddress> mLinkAddresses;
     private Collection<InetAddress> mDnses;
-    private Collection<InetAddress> mGateways;
+    private Collection<RouteInfo> mRoutes;
     private ProxyProperties mHttpProxy;
 
     public LinkProperties() {
@@ -67,7 +67,7 @@
             mIfaceName = source.getInterfaceName();
             mLinkAddresses = source.getLinkAddresses();
             mDnses = source.getDnses();
-            mGateways = source.getGateways();
+            mRoutes = source.getRoutes();
             mHttpProxy = new ProxyProperties(source.getHttpProxy());
         }
     }
@@ -104,11 +104,11 @@
         return Collections.unmodifiableCollection(mDnses);
     }
 
-    public void addGateway(InetAddress gateway) {
-        if (gateway != null) mGateways.add(gateway);
+    public void addRoute(RouteInfo route) {
+        if (route != null) mRoutes.add(route);
     }
-    public Collection<InetAddress> getGateways() {
-        return Collections.unmodifiableCollection(mGateways);
+    public Collection<RouteInfo> getRoutes() {
+        return Collections.unmodifiableCollection(mRoutes);
     }
 
     public void setHttpProxy(ProxyProperties proxy) {
@@ -122,7 +122,7 @@
         mIfaceName = null;
         mLinkAddresses = new ArrayList<LinkAddress>();
         mDnses = new ArrayList<InetAddress>();
-        mGateways = new ArrayList<InetAddress>();
+        mRoutes = new ArrayList<RouteInfo>();
         mHttpProxy = null;
     }
 
@@ -146,12 +146,12 @@
         for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ",";
         dns += "] ";
 
-        String gateways = "Gateways: [";
-        for (InetAddress gw : mGateways) gateways += gw.getHostAddress() + ",";
-        gateways += "] ";
+        String routes = "Routes: [";
+        for (RouteInfo route : mRoutes) routes += route.toString() + ",";
+        routes += "] ";
         String proxy = (mHttpProxy == null ? "" : "HttpProxy: " + mHttpProxy.toString() + " ");
 
-        return ifaceName + linkAddresses + gateways + dns + proxy;
+        return ifaceName + linkAddresses + routes + dns + proxy;
     }
 
 
@@ -177,7 +177,7 @@
 
         boolean sameAddresses;
         boolean sameDnses;
-        boolean sameGateways;
+        boolean sameRoutes;
 
         LinkProperties target = (LinkProperties) obj;
 
@@ -190,12 +190,12 @@
         sameDnses = (mDnses.size() == targetDnses.size()) ?
                 mDnses.containsAll(targetDnses) : false;
 
-        Collection<InetAddress> targetGateways = target.getGateways();
-        sameGateways = (mGateways.size() == targetGateways.size()) ?
-                mGateways.containsAll(targetGateways) : false;
+        Collection<RouteInfo> targetRoutes = target.getRoutes();
+        sameRoutes = (mRoutes.size() == targetRoutes.size()) ?
+                mRoutes.containsAll(targetRoutes) : false;
 
         return
-            sameAddresses && sameDnses && sameGateways
+            sameAddresses && sameDnses && sameRoutes
             && TextUtils.equals(getInterfaceName(), target.getInterfaceName())
             && (getHttpProxy() == null ? target.getHttpProxy() == null :
                 getHttpProxy().equals(target.getHttpProxy()));
@@ -211,7 +211,7 @@
         return ((null == mIfaceName) ? 0 : mIfaceName.hashCode()
                 + mLinkAddresses.size() * 31
                 + mDnses.size() * 37
-                + mGateways.size() * 41
+                + mRoutes.size() * 41
                 + ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode()));
     }
 
@@ -231,9 +231,9 @@
             dest.writeByteArray(d.getAddress());
         }
 
-        dest.writeInt(mGateways.size());
-        for(InetAddress gw : mGateways) {
-            dest.writeByteArray(gw.getAddress());
+        dest.writeInt(mRoutes.size());
+        for(RouteInfo route : mRoutes) {
+            dest.writeParcelable(route, flags);
         }
 
         if (mHttpProxy != null) {
@@ -272,9 +272,7 @@
                 }
                 addressCount = in.readInt();
                 for (int i=0; i<addressCount; i++) {
-                    try {
-                        netProp.addGateway(InetAddress.getByAddress(in.createByteArray()));
-                    } catch (UnknownHostException e) { }
+                    netProp.addRoute((RouteInfo)in.readParcelable(null));
                 }
                 if (in.readByte() == 1) {
                     netProp.setHttpProxy((ProxyProperties)in.readParcelable(null));
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index b3f3988..823d10f 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -80,6 +80,16 @@
     public native static boolean runDhcp(String interfaceName, DhcpInfoInternal ipInfo);
 
     /**
+     * Initiate renewal on the Dhcp client daemon. This call blocks until it obtains
+     * a result (either success or failure) from the daemon.
+     * @param interfaceName the name of the interface to configure
+     * @param ipInfo if the request succeeds, this object is filled in with
+     * the IP address information.
+     * @return {@code true} for success, {@code false} for failure
+     */
+    public native static boolean runDhcpRenew(String interfaceName, DhcpInfoInternal ipInfo);
+
+    /**
      * Shut down the DHCP client daemon.
      * @param interfaceName the name of the interface for which the daemon
      * should be stopped
diff --git a/core/java/android/net/RouteInfo.aidl b/core/java/android/net/RouteInfo.aidl
new file mode 100644
index 0000000..2296a57
--- /dev/null
+++ b/core/java/android/net/RouteInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.net;
+
+parcelable RouteInfo;
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
new file mode 100644
index 0000000..5b10531
--- /dev/null
+++ b/core/java/android/net/RouteInfo.java
@@ -0,0 +1,162 @@
+/*
+ * 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.net;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.net.UnknownHostException;
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+/**
+ * A simple container for route information.
+ *
+ * @hide
+ */
+public class RouteInfo implements Parcelable {
+    /**
+     * The IP destination address for this route.
+     */
+    private final LinkAddress mDestination;
+
+    /**
+     * The gateway address for this route.
+     */
+    private final InetAddress mGateway;
+
+    private final boolean mIsDefault;
+
+    public RouteInfo(LinkAddress destination, InetAddress gateway) {
+        if (destination == null) {
+            try {
+                if ((gateway != null) && (gateway instanceof Inet4Address)) {
+                    destination = new LinkAddress(InetAddress.getByName("0.0.0.0"), 32);
+                } else {
+                    destination = new LinkAddress(InetAddress.getByName("::0"), 128);
+                }
+            } catch (Exception e) {}
+        }
+        mDestination = destination;
+        mGateway = gateway;
+        mIsDefault = isDefault();
+    }
+
+    public RouteInfo(InetAddress gateway) {
+        LinkAddress destination = null;
+        try {
+            if ((gateway != null) && (gateway instanceof Inet4Address)) {
+                destination = new LinkAddress(InetAddress.getByName("0.0.0.0"), 32);
+            } else {
+                destination = new LinkAddress(InetAddress.getByName("::0"), 128);
+            }
+        } catch (Exception e) {}
+        mDestination = destination;
+        mGateway = gateway;
+        mIsDefault = isDefault();
+    }
+
+    private boolean isDefault() {
+        boolean val = false;
+        if (mGateway != null) {
+            if (mGateway instanceof Inet4Address) {
+                val = (mDestination == null || mDestination.getNetworkPrefixLength() == 32);
+            } else {
+                val = (mDestination == null || mDestination.getNetworkPrefixLength() == 128);
+            }
+        }
+        return val;
+    }
+
+    public LinkAddress getDestination() {
+        return mDestination;
+    }
+
+    public InetAddress getGateway() {
+        return mGateway;
+    }
+
+    public boolean isDefaultRoute() {
+        return mIsDefault;
+    }
+
+    public String toString() {
+        String val = "";
+        if (mDestination != null) val = mDestination.toString();
+        if (mGateway != null) val += " -> " + mGateway.getHostAddress();
+        return val;
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        if (mDestination == null) {
+            dest.writeByte((byte) 0);
+        } else {
+            dest.writeByte((byte) 1);
+            dest.writeByteArray(mDestination.getAddress().getAddress());
+            dest.writeInt(mDestination.getNetworkPrefixLength());
+        }
+
+        if (mGateway == null) {
+            dest.writeByte((byte) 0);
+        } else {
+            dest.writeByte((byte) 1);
+            dest.writeByteArray(mGateway.getAddress());
+        }
+    }
+
+    public static final Creator<RouteInfo> CREATOR =
+        new Creator<RouteInfo>() {
+        public RouteInfo createFromParcel(Parcel in) {
+            InetAddress destAddr = null;
+            int prefix = 0;
+            InetAddress gateway = null;
+
+            if (in.readByte() == 1) {
+                byte[] addr = in.createByteArray();
+                prefix = in.readInt();
+
+                try {
+                    destAddr = InetAddress.getByAddress(addr);
+                } catch (UnknownHostException e) {}
+            }
+
+            if (in.readByte() == 1) {
+                byte[] addr = in.createByteArray();
+
+                try {
+                    gateway = InetAddress.getByAddress(addr);
+                } catch (UnknownHostException e) {}
+            }
+
+            LinkAddress dest = null;
+
+            if (destAddr != null) {
+                dest = new LinkAddress(destAddr, prefix);
+            }
+
+            return new RouteInfo(dest, gateway);
+        }
+
+        public RouteInfo[] newArray(int size) {
+            return new RouteInfo[size];
+        }
+    };
+}
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 1e4cca9..4107c5a 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1170,7 +1170,7 @@
         if (h2 < 0.5f)
             h2 = 0.5f;
 
-        if (h1 == h2) {
+        if (Float.compare(h1, h2) == 0) {
             dest.moveTo(h1, top);
             dest.lineTo(h1, bottom);
         } else {
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index ac5db62..6741059 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -629,10 +629,16 @@
         public CharSequence createFromParcel(Parcel p) {
             int kind = p.readInt();
 
-            if (kind == 1)
-                return p.readString();
+            String string = p.readString();
+            if (string == null) {
+                return null;
+            }
 
-            SpannableString sp = new SpannableString(p.readString());
+            if (kind == 1) {
+                return string;
+            }
+
+            SpannableString sp = new SpannableString(string);
 
             while (true) {
                 kind = p.readInt();
diff --git a/core/java/android/text/method/MultiTapKeyListener.java b/core/java/android/text/method/MultiTapKeyListener.java
index 6d94788..2a739fa 100644
--- a/core/java/android/text/method/MultiTapKeyListener.java
+++ b/core/java/android/text/method/MultiTapKeyListener.java
@@ -116,7 +116,7 @@
                     content.replace(selStart, selEnd,
                                     String.valueOf(current).toUpperCase());
                     removeTimeouts(content);
-                    Timeout t = new Timeout(content);
+                    new Timeout(content); // for its side effects
 
                     return true;
                 }
@@ -124,7 +124,7 @@
                     content.replace(selStart, selEnd,
                                     String.valueOf(current).toLowerCase());
                     removeTimeouts(content);
-                    Timeout t = new Timeout(content);
+                    new Timeout(content); // for its side effects
 
                     return true;
                 }
@@ -140,7 +140,7 @@
 
                     content.replace(selStart, selEnd, val, ix, ix + 1);
                     removeTimeouts(content);
-                    Timeout t = new Timeout(content);
+                    new Timeout(content); // for its side effects
 
                     return true;
                 }
@@ -206,7 +206,7 @@
             }
 
             removeTimeouts(content);
-            Timeout t = new Timeout(content);
+            new Timeout(content); // for its side effects
 
             // Set up the callback so we can remove the timeout if the
             // cursor moves.
diff --git a/core/java/android/text/style/DrawableMarginSpan.java b/core/java/android/text/style/DrawableMarginSpan.java
index 3c471a5..c2564d5 100644
--- a/core/java/android/text/style/DrawableMarginSpan.java
+++ b/core/java/android/text/style/DrawableMarginSpan.java
@@ -50,9 +50,6 @@
         int dw = mDrawable.getIntrinsicWidth();
         int dh = mDrawable.getIntrinsicHeight();
 
-        if (dir < 0)
-            x -= dw;
-
         // XXX What to do about Paint?
         mDrawable.setBounds(ix, itop, ix+dw, itop+dh);
         mDrawable.draw(c);
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index cdf8954..984102a 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -163,8 +163,7 @@
     static native int nCreateTextureLayer(int[] layerInfo);
     static native int nCreateLayer(int width, int height, boolean isOpaque, int[] layerInfo);
     static native void nResizeLayer(int layerId, int width, int height, int[] layerInfo);
-    static native void nUpdateTextureLayer(int layerId, int width, int height,
-            float[] textureTransform);
+    static native void nUpdateTextureLayer(int layerId, int width, int height, int surface);
     static native void nDestroyLayer(int layerId);
     static native void nDestroyLayerDeferred(int layerId);
 
diff --git a/core/java/android/view/GLES20TextureLayer.java b/core/java/android/view/GLES20TextureLayer.java
index 21fbdfcf6..fcf421b 100644
--- a/core/java/android/view/GLES20TextureLayer.java
+++ b/core/java/android/view/GLES20TextureLayer.java
@@ -70,7 +70,7 @@
         return mSurface;
     }
 
-    void update(int width, int height, float[] textureTransform) {
-        GLES20Canvas.nUpdateTextureLayer(mLayer, width, height, textureTransform);
+    void update(int width, int height, int surface) {
+        GLES20Canvas.nUpdateTextureLayer(mLayer, width, height, surface);
     }
 }
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 5b2983d..7ca6e09 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -202,11 +202,10 @@
      * @param layer The hardware layer to update
      * @param width The layer's width
      * @param height The layer's height
-     * @param textureTransform A 4x4 column-first transform matrix to apply to
-     *        texture coordinates
+     * @param surface The surface to update
      */
     abstract void updateTextureLayer(HardwareLayer layer, int width, int height,
-            float[] textureTransform);    
+            SurfaceTexture surface);    
     
     /**
      * Initializes the hardware renderer for the specified surface and setup the
@@ -289,9 +288,10 @@
     @SuppressWarnings({"deprecation"})
     static abstract class GlRenderer extends HardwareRenderer {
         // These values are not exposed in our EGL APIs
-        private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
-        private static final int EGL_SURFACE_TYPE = 0x3033;
-        private static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400;
+        static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+        static final int EGL_SURFACE_TYPE = 0x3033;
+        static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400;
+        static final int EGL_OPENGL_ES2_BIT = 4;
 
         private static final int SURFACE_STATE_ERROR = 0;
         private static final int SURFACE_STATE_SUCCESS = 1;
@@ -459,13 +459,12 @@
                         getEGLErrorString(sEgl.eglGetError()));
             }
 
-            sEglConfig = getConfigChooser(mGlVersion).chooseConfig(sEgl, sEglDisplay);
+            sEglConfig = chooseEglConfig();
             if (sEglConfig == null) {
                 // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without
                 if (mDirtyRegions) {
                     mDirtyRegions = false;
-
-                    sEglConfig = getConfigChooser(mGlVersion).chooseConfig(sEgl, sEglDisplay);
+                    sEglConfig = chooseEglConfig();
                     if (sEglConfig == null) {
                         throw new RuntimeException("eglConfig not initialized");
                     }
@@ -481,6 +480,21 @@
             sEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
         }
 
+        private EGLConfig chooseEglConfig() {
+            int[] configsCount = new int[1];
+            EGLConfig[] configs = new EGLConfig[1];
+            int[] configSpec = getConfig(mDirtyRegions);
+            if (!sEgl.eglChooseConfig(sEglDisplay, configSpec, configs, 1, configsCount)) {
+                throw new IllegalArgumentException("eglChooseConfig failed " +
+                        getEGLErrorString(sEgl.eglGetError()));
+            } else if (configsCount[0] > 0) {
+                return configs[0];
+            }
+            return null;
+        }
+
+        abstract int[] getConfig(boolean dirtyRegions);
+
         GL createEglSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException {
             // Check preconditions.
             if (sEgl == null) {
@@ -592,15 +606,6 @@
 
         void onPostDraw() {
         }
-        
-        /**
-         * Defines the EGL configuration for this renderer.
-         * 
-         * @return An {@link android.view.HardwareRenderer.GlRenderer.EglConfigChooser}.
-         */
-        EglConfigChooser getConfigChooser(int glVersion) {
-            return new ComponentSizeChooser(glVersion, 8, 8, 8, 8, 0, 0, mDirtyRegions);
-        }
 
         @Override
         void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
@@ -713,134 +718,6 @@
             }
             return SURFACE_STATE_SUCCESS;
         }
-
-        static abstract class EglConfigChooser {
-            final int[] mConfigSpec;
-            private final int mGlVersion;
-
-            EglConfigChooser(int glVersion, int[] configSpec) {
-                mGlVersion = glVersion;
-                mConfigSpec = filterConfigSpec(configSpec);
-            }
-
-            EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
-                int[] index = new int[1];
-                if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, index)) {
-                    throw new IllegalArgumentException("eglChooseConfig failed "
-                            + getEGLErrorString(egl.eglGetError()));
-                }
-
-                int numConfigs = index[0];
-                if (numConfigs <= 0) {
-                    throw new IllegalArgumentException("No configs match configSpec");
-                }
-
-                EGLConfig[] configs = new EGLConfig[numConfigs];
-                if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, index)) {
-                    throw new IllegalArgumentException("eglChooseConfig failed "
-                            + getEGLErrorString(egl.eglGetError()));
-                }
-
-                EGLConfig config = chooseConfig(egl, display, configs);
-                if (config == null) {
-                    throw new IllegalArgumentException("No config chosen");
-                }
-
-                return config;
-            }
-
-            abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs);
-
-            private int[] filterConfigSpec(int[] configSpec) {
-                if (mGlVersion != 2) {
-                    return configSpec;
-                }
-                /* We know none of the subclasses define EGL_RENDERABLE_TYPE.
-                 * And we know the configSpec is well formed.
-                 */
-                int len = configSpec.length;
-                int[] newConfigSpec = new int[len + 2];
-                System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1);
-                newConfigSpec[len - 1] = EGL10.EGL_RENDERABLE_TYPE;
-                newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
-                newConfigSpec[len + 1] = EGL10.EGL_NONE;
-                return newConfigSpec;
-            }
-        }
-
-        /**
-         * Choose a configuration with exactly the specified r,g,b,a sizes,
-         * and at least the specified depth and stencil sizes.
-         */
-        static class ComponentSizeChooser extends EglConfigChooser {
-            private int[] mValue;
-
-            private final int mRedSize;
-            private final int mGreenSize;
-            private final int mBlueSize;
-            private final int mAlphaSize;
-            private final int mDepthSize;
-            private final int mStencilSize;
-            private final boolean mDirtyRegions;
-
-            ComponentSizeChooser(int glVersion, int redSize, int greenSize, int blueSize,
-                    int alphaSize, int depthSize, int stencilSize, boolean dirtyRegions) {
-                super(glVersion, new int[] {
-                        EGL10.EGL_RED_SIZE, redSize,
-                        EGL10.EGL_GREEN_SIZE, greenSize,
-                        EGL10.EGL_BLUE_SIZE, blueSize,
-                        EGL10.EGL_ALPHA_SIZE, alphaSize,
-                        EGL10.EGL_DEPTH_SIZE, depthSize,
-                        EGL10.EGL_STENCIL_SIZE, stencilSize,
-                        EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT |
-                                (dirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0),
-                        EGL10.EGL_NONE });
-                mValue = new int[1];
-                mRedSize = redSize;
-                mGreenSize = greenSize;
-                mBlueSize = blueSize;
-                mAlphaSize = alphaSize;
-                mDepthSize = depthSize;
-                mStencilSize = stencilSize;
-                mDirtyRegions = dirtyRegions;
-            }
-
-            @Override
-            EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) {
-                for (EGLConfig config : configs) {
-                    int d = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0);
-                    int s = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0);
-                    if (d >= mDepthSize && s >= mStencilSize) {
-                        int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);
-                        int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
-                        int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
-                        int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
-                        boolean backBuffer;
-                        if (mDirtyRegions) {
-                            int surfaceType = findConfigAttrib(egl, display, config,
-                                    EGL_SURFACE_TYPE, 0);
-                            backBuffer = (surfaceType & EGL_SWAP_BEHAVIOR_PRESERVED_BIT) != 0;
-                        } else {
-                            backBuffer = true;
-                        }
-                        if (r >= mRedSize && g >= mGreenSize && b >= mBlueSize && a >= mAlphaSize
-                                && backBuffer) {
-                            return config;
-                        }
-                    }
-                }
-                return null;
-            }
-
-            private int findConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config,
-                    int attribute, int defaultValue) {
-                if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
-                    return mValue[0];
-                }
-
-                return defaultValue;
-            }
-        }
     }
 
     /**
@@ -857,7 +734,23 @@
         GLES20Canvas createCanvas() {
             return mGlCanvas = new GLES20Canvas(mTranslucent);
         }
-        
+
+        @Override
+        int[] getConfig(boolean dirtyRegions) {
+            return new int[] {
+                    EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+                    EGL10.EGL_RED_SIZE, 8,
+                    EGL10.EGL_GREEN_SIZE, 8,
+                    EGL10.EGL_BLUE_SIZE, 8,
+                    EGL10.EGL_ALPHA_SIZE, 8,
+                    EGL10.EGL_DEPTH_SIZE, 0,
+                    EGL10.EGL_STENCIL_SIZE, 0,
+                    EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT |
+                            (dirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0),
+                    EGL10.EGL_NONE
+            };
+        }
+
         @Override
         boolean canDraw() {
             return super.canDraw() && mGlCanvas != null;
@@ -906,8 +799,8 @@
 
         @Override
         void updateTextureLayer(HardwareLayer layer, int width, int height,
-                float[] textureTransform) {
-            ((GLES20TextureLayer) layer).update(width, height, textureTransform);
+                SurfaceTexture surface) {
+            ((GLES20TextureLayer) layer).update(width, height, surface.mSurfaceTexture);
         }
 
         static HardwareRenderer create(boolean translucent) {
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 6380e1b..755ecf5 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -73,6 +73,10 @@
  *              // Something bad happened
  *          }
  *      }
+ *      
+ *      public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+ *          // Ignored, Camera does all the work for us
+ *      }
  *  }
  * </pre>
  * 
@@ -90,8 +94,6 @@
     private HardwareLayer mLayer;
     private SurfaceTexture mSurface;
     private SurfaceTextureListener mListener;
-    
-    private final float[] mTextureTransform = new float[16];
 
     private final Runnable mUpdateLayerAction = new Runnable() {
         @Override
@@ -99,6 +101,7 @@
             updateLayer();
         }
     };
+    private SurfaceTexture.OnFrameAvailableListener mUpdateListener;
 
     /**
      * Creates a new TextureView.
@@ -210,6 +213,14 @@
     }
 
     @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        if (mSurface != null) {
+            nSetDefaultBufferSize(mSurface.mSurfaceTexture, getWidth(), getHeight());
+        }
+    }
+
+    @Override
     HardwareLayer getHardwareLayer() {
         if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
             return null;
@@ -218,15 +229,17 @@
         if (mLayer == null) {
             mLayer = mAttachInfo.mHardwareRenderer.createHardwareLayer();
             mSurface = mAttachInfo.mHardwareRenderer.createSuraceTexture(mLayer);
+            nSetDefaultBufferSize(mSurface.mSurfaceTexture, getWidth(), getHeight());
 
-            mSurface.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {
+            mUpdateListener = new SurfaceTexture.OnFrameAvailableListener() {
                 @Override
                 public void onFrameAvailable(SurfaceTexture surfaceTexture) {
                     // Per SurfaceTexture's documentation, the callback may be invoked
                     // from an arbitrary thread
                     post(mUpdateLayerAction);
                 }
-            });
+            };
+            mSurface.setOnFrameAvailableListener(mUpdateListener);
 
             if (mListener != null) {
                 mListener.onSurfaceTextureAvailable(mSurface);
@@ -236,16 +249,29 @@
         return mLayer;
     }
 
+    @Override
+    protected void onVisibilityChanged(View changedView, int visibility) {
+        super.onVisibilityChanged(changedView, visibility);
+
+        if (mSurface != null) {
+            // When the view becomes invisible, stop updating it, it's a waste of CPU
+            // To cancel updates, the easiest thing to do is simply to remove the
+            // updates listener
+            if (visibility == VISIBLE) {
+                mSurface.setOnFrameAvailableListener(mUpdateListener);
+                updateLayer();
+            } else {
+                mSurface.setOnFrameAvailableListener(null);
+            }
+        }
+    }
+
     private void updateLayer() {
         if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
             return;
         }
 
-        mSurface.updateTexImage();
-        mSurface.getTransformMatrix(mTextureTransform);
-
-        mAttachInfo.mHardwareRenderer.updateTextureLayer(mLayer, getWidth(), getHeight(),
-                mTextureTransform);
+        mAttachInfo.mHardwareRenderer.updateTextureLayer(mLayer, getWidth(), getHeight(), mSurface);
 
         invalidate();
     }
@@ -292,5 +318,17 @@
          *                {@link android.view.TextureView#getSurfaceTexture()}
          */
         public void onSurfaceTextureAvailable(SurfaceTexture surface);
+
+        /**
+         * Invoked when the {@link SurfaceTexture}'s buffers size changed.
+         * 
+         * @param surface The surface returned by
+         *                {@link android.view.TextureView#getSurfaceTexture()}
+         * @param width The new width of the surface
+         * @param height The new height of the surface
+         */
+        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height);
     }
+
+    private static native void nSetDefaultBufferSize(int surfaceTexture, int width, int height);
 }
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 11c9392..5e18f55 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -253,7 +253,7 @@
     public static final int TYPES_ALL_MASK = 0xFFFFFFFF;
 
     private static final int MAX_POOL_SIZE = 10;
-    private static final Object mPoolLock = new Object();
+    private static final Object sPoolLock = new Object();
     private static AccessibilityEvent sPool;
     private static int sPoolSize;
 
@@ -375,7 +375,7 @@
      * @return An instance.
      */
     public static AccessibilityEvent obtain() {
-        synchronized (mPoolLock) {
+        synchronized (sPoolLock) {
             if (sPool != null) {
                 AccessibilityEvent event = sPool;
                 sPool = sPool.mNext;
@@ -392,14 +392,16 @@
      * Return an instance back to be reused.
      * <p>
      * <b>Note: You must not touch the object after calling this function.</b>
+     *
+     * @throws IllegalStateException If the event is already recycled.
      */
     @Override
     public void recycle() {
         if (mIsInPool) {
-            return;
+            throw new IllegalStateException("Event already recycled!");
         }
         clear();
-        synchronized (mPoolLock) {
+        synchronized (sPoolLock) {
             if (sPoolSize <= MAX_POOL_SIZE) {
                 mNext = sPool;
                 sPool = this;
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index e095f43..fecf9df 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -39,7 +39,7 @@
     private static final int PROPERTY_FULL_SCREEN = 0x00000080;
 
     private static final int MAX_POOL_SIZE = 10;
-    private static final Object mPoolLock = new Object();
+    private static final Object sPoolLock = new Object();
     private static AccessibilityRecord sPool;
     private static int sPoolSize;
 
@@ -342,7 +342,7 @@
      * @return An instance.
      */
     protected static AccessibilityRecord obtain() {
-        synchronized (mPoolLock) {
+        synchronized (sPoolLock) {
             if (sPool != null) {
                 AccessibilityRecord record = sPool;
                 sPool = sPool.mNext;
@@ -359,13 +359,15 @@
      * Return an instance back to be reused.
      * <p>
      * <b>Note: You must not touch the object after calling this function.</b>
+     *
+     * @throws IllegalStateException If the record is already recycled.
      */
     public void recycle() {
         if (mIsInPool) {
-            return;
+            throw new IllegalStateException("Record already recycled!");
         }
         clear();
-        synchronized (mPoolLock) {
+        synchronized (sPoolLock) {
             if (sPoolSize <= MAX_POOL_SIZE) {
                 mNext = sPool;
                 sPool = this;
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index 060f1a9..7fbfcbb 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -899,6 +899,8 @@
 
     @Override
     public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+        super.onPopulateAccessibilityEvent(event);
+
         View selectedView = getSelectedView();
         if (selectedView != null) {
             event.setEnabled(selectedView.isEnabled());
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index dbe9288..7838ec0 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -254,6 +254,15 @@
         return mDividerPadding;
     }
 
+    /**
+     * Get the width of the current divider drawable.
+     *
+     * @hide Used internally by framework.
+     */
+    public int getDividerWidth() {
+        return mDividerWidth;
+    }
+
     @Override
     protected void onDraw(Canvas canvas) {
         if (mDivider == null) {
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 5618dbe..d115364 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -2009,16 +2009,12 @@
         ListAdapter adapter = getAdapter();
         if (adapter != null) {
             final int count = adapter.getCount();
-            if (count < 15) {
-                for (int i = 0; i < count; i++) {
-                    if (adapter.isEnabled(i)) {
-                        itemCount++;
-                    } else if (i <= currentItemIndex) {
-                        currentItemIndex--;
-                    }
+            for (int i = 0; i < count; i++) {
+                if (adapter.isEnabled(i)) {
+                    itemCount++;
+                } else if (i <= currentItemIndex) {
+                    currentItemIndex--;
                 }
-            } else {
-                itemCount = count;
             }
         }
 
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 72b70bc..de32c2b 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1489,6 +1489,10 @@
         @Override
         public boolean dispatchKeyEvent(KeyEvent event) {
             if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+                if (getKeyDispatcherState() == null) {
+                    return super.dispatchKeyEvent(event);
+                }
+
                 if (event.getAction() == KeyEvent.ACTION_DOWN
                         && event.getRepeatCount() == 0) {
                     KeyEvent.DispatcherState state = getKeyDispatcherState();
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 96d41a0..c4ba270 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -66,15 +66,16 @@
  *
  * <p>
  * A progress bar can also be made indeterminate. In indeterminate mode, the
- * progress bar shows a cyclic animation. This mode is used by applications
- * when the length of the task is unknown. 
+ * progress bar shows a cyclic animation without an indication of progress. This mode is used by
+ * applications when the length of the task is unknown. The indeterminate progress bar can be either
+ * a spinning wheel or a horizontal bar.
  * </p>
  *
  * <p>The following code example shows how a progress bar can be used from
  * a worker thread to update the user interface to notify the user of progress:
  * </p>
  * 
- * <pre class="prettyprint">
+ * <pre>
  * public class MyActivity extends Activity {
  *     private static final int PROGRESS = 0x1;
  *
@@ -93,7 +94,7 @@
  *         // Start lengthy operation in a background thread
  *         new Thread(new Runnable() {
  *             public void run() {
- *                 while (mProgressStatus < 100) {
+ *                 while (mProgressStatus &lt; 100) {
  *                     mProgressStatus = doWork();
  *
  *                     // Update the progress bar
@@ -106,8 +107,61 @@
  *             }
  *         }).start();
  *     }
- * }
- * </pre>
+ * }</pre>
+ *
+ * <p>To add a progress bar to a layout file, you can use the {@code &lt;ProgressBar&gt;} element.
+ * By default, the progress bar is a spinning wheel (an indeterminate indicator). To change to a
+ * horizontal progress bar, apply the {@link android.R.style#Widget_ProgressBar_Horizontal
+ * Widget.ProgressBar.Horizontal} style, like so:</p>
+ *
+ * <pre>
+ * &lt;ProgressBar
+ *     style="@android:style/Widget.ProgressBar.Horizontal"
+ *     ... /&gt;</pre>
+ *
+ * <p>If you will use the progress bar to show real progress, you must use the horizontal bar. You
+ * can then increment the  progress with {@link #incrementProgressBy incrementProgressBy()} or
+ * {@link #setProgress setProgress()}. By default, the progress bar is full when it reaches 100. If
+ * necessary, you can adjust the maximum value (the value for a full bar) using the {@link
+ * android.R.styleable#ProgressBar_max android:max} attribute. Other attributes available are listed
+ * below.</p>
+ *
+ * <p>Another common style to apply to the progress bar is {@link
+ * android.R.style#Widget_ProgressBar_Small Widget.ProgressBar.Small}, which shows a smaller
+ * version of the spinning wheel&mdash;useful when waiting for content to load.
+ * For example, you can insert this kind of progress bar into your default layout for
+ * a view that will be populated by some content fetched from the Internet&mdash;the spinning wheel
+ * appears immediately and when your application receives the content, it replaces the progress bar
+ * with the loaded content. For example:</p>
+ *
+ * <pre>
+ * &lt;LinearLayout
+ *     android:orientation="horizontal"
+ *     ... &gt;
+ *     &lt;ProgressBar
+ *         android:layout_width="wrap_content"
+ *         android:layout_height="wrap_content"
+ *         style="@android:style/Widget.ProgressBar.Small"
+ *         android:layout_marginRight="5dp" /&gt;
+ *     &lt;TextView
+ *         android:layout_width="wrap_content"
+ *         android:layout_height="wrap_content"
+ *         android:text="@string/loading" /&gt;
+ * &lt;/LinearLayout&gt;</pre>
+ *
+ * <p>Other progress bar styles provided by the system include:</p>
+ * <ul>
+ * <li>{@link android.R.style#Widget_ProgressBar_Horizontal Widget.ProgressBar.Horizontal}</li>
+ * <li>{@link android.R.style#Widget_ProgressBar_Small Widget.ProgressBar.Small}</li>
+ * <li>{@link android.R.style#Widget_ProgressBar_Large Widget.ProgressBar.Large}</li>
+ * <li>{@link android.R.style#Widget_ProgressBar_Inverse Widget.ProgressBar.Inverse}</li>
+ * <li>{@link android.R.style#Widget_ProgressBar_Small_Inverse
+ * Widget.ProgressBar.Small.Inverse}</li>
+ * <li>{@link android.R.style#Widget_ProgressBar_Large_Inverse
+ * Widget.ProgressBar.Large.Inverse}</li>
+ * </ul>
+ * <p>The "inverse" styles provide an inverse color scheme for the spinner, which may be necessary
+ * if your application uses a light colored theme (a white background).</p>
  *  
  * <p><strong>XML attributes</b></strong> 
  * <p> 
@@ -115,13 +169,21 @@
  * {@link android.R.styleable#View View Attributes}
  * </p>
  * 
- * <p><strong>Styles</b></strong> 
- * <p> 
- * @attr ref android.R.styleable#Theme_progressBarStyle
- * @attr ref android.R.styleable#Theme_progressBarStyleSmall
- * @attr ref android.R.styleable#Theme_progressBarStyleLarge
- * @attr ref android.R.styleable#Theme_progressBarStyleHorizontal
- * </p>
+ * @attr ref android.R.styleable#ProgressBar_animationResolution
+ * @attr ref android.R.styleable#ProgressBar_indeterminate
+ * @attr ref android.R.styleable#ProgressBar_indeterminateBehavior
+ * @attr ref android.R.styleable#ProgressBar_indeterminateDrawable
+ * @attr ref android.R.styleable#ProgressBar_indeterminateDuration
+ * @attr ref android.R.styleable#ProgressBar_indeterminateOnly
+ * @attr ref android.R.styleable#ProgressBar_interpolator
+ * @attr ref android.R.styleable#ProgressBar_max
+ * @attr ref android.R.styleable#ProgressBar_maxHeight
+ * @attr ref android.R.styleable#ProgressBar_maxWidth
+ * @attr ref android.R.styleable#ProgressBar_minHeight
+ * @attr ref android.R.styleable#ProgressBar_minWidth
+ * @attr ref android.R.styleable#ProgressBar_progress
+ * @attr ref android.R.styleable#ProgressBar_progressDrawable
+ * @attr ref android.R.styleable#ProgressBar_secondaryProgress
  */
 @RemoteView
 public class ProgressBar extends View {
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 4b4f5f2..ade3a0a 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -873,7 +873,7 @@
             int count = getChildCount();
             if (count > 0) {
                 View view = getChildAt(count - 1);
-                mTempRect.bottom = view.getBottom();
+                mTempRect.bottom = view.getBottom() + mPaddingBottom;
                 mTempRect.top = mTempRect.bottom - height;
             }
         }
@@ -949,9 +949,7 @@
             } else if (direction == View.FOCUS_DOWN) {
                 if (getChildCount() > 0) {
                     int daBottom = getChildAt(0).getBottom();
-
-                    int screenBottom = getScrollY() + getHeight();
-
+                    int screenBottom = getScrollY() + getHeight() - mPaddingBottom;
                     if (daBottom - screenBottom < maxJump) {
                         scrollDelta = daBottom - screenBottom;
                     }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 578c3df..24b176d 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -7851,6 +7851,8 @@
 
     @Override
     public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+        super.onPopulateAccessibilityEvent(event);
+
         if (!isShown()) {
             return;
         }
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index 57df259..1e576ce 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -19,6 +19,7 @@
 import com.android.internal.view.menu.MenuBuilder;
 import com.android.internal.view.menu.MenuPopupHelper;
 import com.android.internal.view.menu.SubMenuBuilder;
+import com.android.internal.widget.AbsActionBarView;
 import com.android.internal.widget.ActionBarContainer;
 import com.android.internal.widget.ActionBarContextView;
 import com.android.internal.widget.ActionBarView;
@@ -46,7 +47,6 @@
 import android.view.Window;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.HorizontalScrollView;
-import android.widget.LinearLayout;
 import android.widget.SpinnerAdapter;
 
 import java.lang.ref.WeakReference;
@@ -61,8 +61,6 @@
  */
 public class ActionBarImpl extends ActionBar {
     private static final String TAG = "ActionBarImpl";
-    private static final int NORMAL_VIEW = 0;
-    private static final int CONTEXT_VIEW = 1;
 
     private Context mContext;
     private Activity mActivity;
@@ -70,8 +68,8 @@
 
     private ActionBarContainer mContainerView;
     private ActionBarView mActionView;
-    private ActionBarContextView mUpperContextView;
-    private LinearLayout mLowerView;
+    private ActionBarContextView mContextView;
+    private ActionBarContainer mSplitView;
     private View mContentView;
     private ViewGroup mExternalTabView;
 
@@ -102,26 +100,6 @@
 
     private static final TimeInterpolator sFadeOutInterpolator = new DecelerateInterpolator();
 
-    final AnimatorListener[] mAfterAnimation = new AnimatorListener[] {
-            new AnimatorListenerAdapter() { // NORMAL_VIEW
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    if (mLowerView != null) {
-                        mLowerView.removeAllViews();
-                    }
-                    mCurrentModeAnim = null;
-                    hideAllExcept(NORMAL_VIEW);
-                }
-            },
-            new AnimatorListenerAdapter() { // CONTEXT_VIEW
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mCurrentModeAnim = null;
-                    hideAllExcept(CONTEXT_VIEW);
-                }
-            }
-    };
-
     final AnimatorListener mHideListener = new AnimatorListenerAdapter() {
         @Override
         public void onAnimationEnd(Animator animation) {
@@ -160,19 +138,19 @@
     private void init(View decor) {
         mContext = decor.getContext();
         mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar);
-        mUpperContextView = (ActionBarContextView) decor.findViewById(
+        mContextView = (ActionBarContextView) decor.findViewById(
                 com.android.internal.R.id.action_context_bar);
-        mLowerView = (LinearLayout) decor.findViewById(
-                com.android.internal.R.id.lower_action_context_bar);
         mContainerView = (ActionBarContainer) decor.findViewById(
                 com.android.internal.R.id.action_bar_container);
+        mSplitView = (ActionBarContainer) decor.findViewById(
+                com.android.internal.R.id.split_action_bar);
 
-        if (mActionView == null || mUpperContextView == null || mContainerView == null) {
+        if (mActionView == null || mContextView == null || mContainerView == null) {
             throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
                     "with a compatible window decor layout");
         }
 
-        mActionView.setContextView(mUpperContextView);
+        mActionView.setContextView(mContextView);
         mContextDisplayMode = mActionView.isSplitActionBar() ?
                 CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL;
 
@@ -341,16 +319,16 @@
             mActionMode.finish();
         }
 
-        mUpperContextView.killMode();
+        mContextView.killMode();
         ActionMode mode = new ActionModeImpl(callback);
         if (callback.onCreateActionMode(mode, mode.getMenu())) {
             mWasHiddenBeforeMode = !isShowing();
             mode.invalidate();
-            mUpperContextView.initForMode(mode);
-            animateTo(CONTEXT_VIEW);
-            if (mLowerView != null) {
+            mContextView.initForMode(mode);
+            animateToMode(true);
+            if (mSplitView != null) {
                 // TODO animate this
-                mLowerView.setVisibility(View.VISIBLE);
+                mSplitView.setVisibility(View.VISIBLE);
             }
             mActionMode = mode;
             return mode;
@@ -418,7 +396,10 @@
         int selectedTabPosition = mSelectedTab != null
                 ? mSelectedTab.getPosition() : mSavedTabPosition;
         mActionView.removeTabAt(position);
-        mTabs.remove(position);
+        TabImpl removedTab = mTabs.remove(position);
+        if (removedTab != null) {
+            removedTab.setPosition(-1);
+        }
 
         final int newTabCount = mTabs.size();
         for (int i = position; i < newTabCount; i++) {
@@ -495,6 +476,10 @@
                 mContainerView.setTranslationY(-mContainerView.getHeight());
                 b.with(ObjectAnimator.ofFloat(mContainerView, "translationY", 0));
             }
+            if (mSplitView != null) {
+                mSplitView.setAlpha(0);
+                b.with(ObjectAnimator.ofFloat(mSplitView, "alpha", 1));
+            }
             anim.addListener(mShowListener);
             mCurrentShowAnim = anim;
             anim.start();
@@ -525,6 +510,10 @@
                 b.with(ObjectAnimator.ofFloat(mContainerView, "translationY",
                         -mContainerView.getHeight()));
             }
+            if (mSplitView != null) {
+                mSplitView.setAlpha(1);
+                b.with(ObjectAnimator.ofFloat(mSplitView, "alpha", 0));
+            }
             anim.addListener(mHideListener);
             mCurrentShowAnim = anim;
             anim.start();
@@ -537,45 +526,14 @@
         return mContainerView.getVisibility() == View.VISIBLE;
     }
 
-    long animateTo(int viewIndex) {
+    void animateToMode(boolean toActionMode) {
         show(false);
         if (mCurrentModeAnim != null) {
             mCurrentModeAnim.end();
         }
 
-        AnimatorSet set = new AnimatorSet();
-
-        final View targetChild = mContainerView.getChildAt(viewIndex);
-        targetChild.setVisibility(View.VISIBLE);
-        targetChild.setAlpha(0);
-        AnimatorSet.Builder b = set.play(ObjectAnimator.ofFloat(targetChild, "alpha", 1));
-
-        final int count = mContainerView.getChildCount();
-        for (int i = 0; i < count; i++) {
-            final View child = mContainerView.getChildAt(i);
-            if (i == viewIndex || child == mContainerView.getTabContainer()) {
-                continue;
-            }
-
-            if (child.getVisibility() != View.GONE) {
-                Animator a = ObjectAnimator.ofFloat(child, "alpha", 0);
-                a.setInterpolator(sFadeOutInterpolator);
-                b.with(a);
-            }
-        }
-
-        set.addListener(mAfterAnimation[viewIndex]);
-
-        mCurrentModeAnim = set;
-        set.start();
-        return set.getDuration();
-    }
-
-    private void hideAllExcept(int viewIndex) {
-        final int count = mContainerView.getChildCount();
-        for (int i = 0; i < count; i++) {
-            mContainerView.getChildAt(i).setVisibility(i == viewIndex ? View.VISIBLE : View.GONE);
-        }
+        mActionView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE);
+        mContextView.animateToVisibility(toActionMode ? View.VISIBLE : View.GONE);
     }
 
     /**
@@ -612,14 +570,10 @@
 
             mCallback.onDestroyActionMode(this);
             mCallback = null;
-            animateTo(NORMAL_VIEW);
+            animateToMode(false);
 
             // Clear out the context mode views after the animation finishes
-            mUpperContextView.closeMode();
-            if (mLowerView != null && mLowerView.getVisibility() != View.GONE) {
-                // TODO Animate this
-                mLowerView.setVisibility(View.GONE);
-            }
+            mContextView.closeMode();
             mActionMode = null;
 
             if (mWasHiddenBeforeMode) {
@@ -636,18 +590,18 @@
 
         @Override
         public void setCustomView(View view) {
-            mUpperContextView.setCustomView(view);
+            mContextView.setCustomView(view);
             mCustomView = new WeakReference<View>(view);
         }
 
         @Override
         public void setSubtitle(CharSequence subtitle) {
-            mUpperContextView.setSubtitle(subtitle);
+            mContextView.setSubtitle(subtitle);
         }
 
         @Override
         public void setTitle(CharSequence title) {
-            mUpperContextView.setTitle(title);
+            mContextView.setTitle(title);
         }
 
         @Override
@@ -662,12 +616,12 @@
 
         @Override
         public CharSequence getTitle() {
-            return mUpperContextView.getTitle();
+            return mContextView.getTitle();
         }
 
         @Override
         public CharSequence getSubtitle() {
-            return mUpperContextView.getSubtitle();
+            return mContextView.getSubtitle();
         }
         
         @Override
@@ -707,7 +661,7 @@
                 return;
             }
             invalidate();
-            mUpperContextView.showOverflowMenu();
+            mContextView.showOverflowMenu();
         }
     }
 
@@ -719,7 +673,7 @@
         private Object mTag;
         private Drawable mIcon;
         private CharSequence mText;
-        private int mPosition;
+        private int mPosition = -1;
         private View mCustomView;
 
         @Override
@@ -751,6 +705,7 @@
         @Override
         public Tab setCustomView(View view) {
             mCustomView = view;
+            if (mPosition >= 0) mActionView.updateTab(mPosition);
             return this;
         }
 
@@ -781,6 +736,7 @@
         @Override
         public Tab setIcon(Drawable icon) {
             mIcon = icon;
+            if (mPosition >= 0) mActionView.updateTab(mPosition);
             return this;
         }
 
@@ -792,6 +748,7 @@
         @Override
         public Tab setText(CharSequence text) {
             mText = text;
+            if (mPosition >= 0) mActionView.updateTab(mPosition);
             return this;
         }
 
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 7cf33fc..12687a1 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -4904,7 +4904,7 @@
     void readOldHistory(Parcel in) {
         mHistory = mHistoryEnd = mHistoryCache = null;
         long time;
-        while ((time=in.readLong()) >= 0) {
+        while (in.dataAvail() > 0 && (time=in.readLong()) >= 0) {
             HistoryItem rec = new HistoryItem(time, in);
             addHistoryRecordLocked(rec);
         }
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
index 4d656c0c..3973344 100644
--- a/core/java/com/android/internal/util/AsyncChannel.java
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -44,16 +44,16 @@
  * In this usage model there is no need for the destination to
  * use the connect methods. The typical sequence of operations is:</p>
  *<ol>
- *   <li>Client calls AsyncChannel#connect</li>
- *   <li>Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li>
+ *   <li>Client calls AsyncChannel#connectSync or Asynchronously:</li>
+ *      <ol>For an asynchronous half connection client calls AsyncChannel#connect.</ol>
+ *          <li>Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li>
+ *      </ol>
  *   <li><code>comm-loop:</code></li>
- *   <li>Client calls AsyncChannel#sendMessage(msgX)</li>
- *   <li>Server receives and processes msgX</li>
- *   <li>Server optionally calls AsyncChannel#replyToMessage(msgY)
- *       and if sent Client receives and processes msgY</li>
+ *   <li>Client calls AsyncChannel#sendMessage</li>
+ *   <li>Server processes messages and optionally replies using AsyncChannel#replyToMessage
  *   <li>Loop to <code>comm-loop</code> until done</li>
- *   <li>When done Client calls {@link AsyncChannel#disconnect(int)}</li>
- *   <li>Client receives CMD_CHANNEL_DISCONNECTED from AsyncChannel</li>
+ *   <li>When done Client calls {@link AsyncChannel#disconnect}</li>
+ *   <li>Client/Server receives CMD_CHANNEL_DISCONNECTED from AsyncChannel</li>
  *</ol>
  *<br/>
  * <p>A second usage model is where the server/destination needs to know
@@ -62,21 +62,26 @@
  * different state for each client. In this model the server will also
  * use the connect methods. The typical sequence of operation is:</p>
  *<ol>
- *   <li>Client calls AsyncChannel#connect</li>
- *   <li>Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li>
- *   <li>Client calls AsyncChannel#sendMessage(CMD_CHANNEL_FULL_CONNECTION)</li>
+ *   <li>Client calls AsyncChannel#fullyConnectSync or Asynchronously:<li>
+ *      <ol>For an asynchronous full connection it calls AsyncChannel#connect</li>
+ *          <li>Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li>
+ *          <li>Client calls AsyncChannel#sendMessage(CMD_CHANNEL_FULL_CONNECTION)</li>
+ *      </ol>
  *   <li>Server receives CMD_CHANNEL_FULL_CONNECTION</li>
- *   <li>Server calls AsyncChannel#connect</li>
- *   <li>Server receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel</li>
+ *   <li>Server calls AsyncChannel#connected</li>
  *   <li>Server sends AsyncChannel#sendMessage(CMD_CHANNEL_FULLY_CONNECTED)</li>
  *   <li>Client receives CMD_CHANNEL_FULLY_CONNECTED</li>
  *   <li><code>comm-loop:</code></li>
  *   <li>Client/Server uses AsyncChannel#sendMessage/replyToMessage
  *       to communicate and perform work</li>
  *   <li>Loop to <code>comm-loop</code> until done</li>
- *   <li>When done Client/Server calls {@link AsyncChannel#disconnect(int)}</li>
+ *   <li>When done Client/Server calls {@link AsyncChannel#disconnect}</li>
  *   <li>Client/Server receives CMD_CHANNEL_DISCONNECTED from AsyncChannel</li>
  *</ol>
+ *
+ * TODO: Consider simplifying where we have connect and fullyConnect with only one response
+ * message RSP_CHANNEL_CONNECT instead of two, CMD_CHANNEL_HALF_CONNECTED and
+ * CMD_CHANNEL_FULLY_CONNECTED. We'd also change CMD_CHANNEL_FULL_CONNECTION to REQ_CHANNEL_CONNECT.
  */
 public class AsyncChannel {
     /** Log tag */
@@ -85,6 +90,8 @@
     /** Enable to turn on debugging */
     private static final boolean DBG = false;
 
+    private static final int BASE = Protocol.BASE_SYSTEM_ASYNC_CHANNEL;
+
     /**
      * Command sent when the channel is half connected. Half connected
      * means that the channel can be used to send commends to the destination
@@ -98,7 +105,7 @@
      * msg.obj  == the AsyncChannel
      * msg.replyTo == dstMessenger if successful
      */
-    public static final int CMD_CHANNEL_HALF_CONNECTED = -1;
+    public static final int CMD_CHANNEL_HALF_CONNECTED = BASE + 0;
 
     /**
      * Command typically sent when after receiving the CMD_CHANNEL_HALF_CONNECTED.
@@ -107,7 +114,7 @@
      *
      * msg.replyTo = srcMessenger.
      */
-    public static final int CMD_CHANNEL_FULL_CONNECTION = -2;
+    public static final int CMD_CHANNEL_FULL_CONNECTION = BASE + 1;
 
     /**
      * Command typically sent after the destination receives a CMD_CHANNEL_FULL_CONNECTION.
@@ -115,20 +122,20 @@
      *
      * msg.arg1 == 0 : Accept connection
      *               : All other values signify the destination rejected the connection
-     *                 and {@link AsyncChannel#disconnect(int)} would typically be called.
+     *                 and {@link AsyncChannel#disconnect} would typically be called.
      */
-    public static final int CMD_CHANNEL_FULLY_CONNECTED = -3;
+    public static final int CMD_CHANNEL_FULLY_CONNECTED = BASE + 2;
 
     /**
      * Command sent when one side or the other wishes to disconnect. The sender
      * may or may not be able to receive a reply depending upon the protocol and
-     * the state of the connection. The receiver should call {@link AsyncChannel#disconnect(int)}
+     * the state of the connection. The receiver should call {@link AsyncChannel#disconnect}
      * to close its side of the channel and it will receive a CMD_CHANNEL_DISCONNECTED
      * when the channel is closed.
      *
      * msg.replyTo = messenger that is disconnecting
      */
-    public static final int CMD_CHANNEL_DISCONNECT = -4;
+    public static final int CMD_CHANNEL_DISCONNECT = BASE + 3;
 
     /**
      * Command sent when the channel becomes disconnected. This is sent when the
@@ -141,7 +148,7 @@
      * msg.obj  == the AsyncChannel
      * msg.replyTo = messenger disconnecting or null if it was never connected.
      */
-    public static final int CMD_CHANNEL_DISCONNECTED = -5;
+    public static final int CMD_CHANNEL_DISCONNECTED = BASE + 4;
 
     /** Successful status always 0, !0 is an unsuccessful status */
     public static final int STATUS_SUCCESSFUL = 0;
@@ -152,6 +159,9 @@
     /** Error attempting to send a message */
     public static final int STATUS_SEND_UNSUCCESSFUL = 2;
 
+    /** CMD_FULLY_CONNECTED refused because a connection already exists*/
+    public static final int STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED = 3;
+
     /** Service connection */
     private AsyncChannelConnection mConnection;
 
@@ -174,9 +184,7 @@
     }
 
     /**
-     * Connect handler to named package/class.
-     *
-     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
+     * Connect handler to named package/class synchronously.
      *
      * @param srcContext is the context of the source
      * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
@@ -184,8 +192,10 @@
      * @param dstPackageName is the destination package name
      * @param dstClassName is the fully qualified class name (i.e. contains
      *            package name)
+     *
+     * @return STATUS_SUCCESSFUL on success any other value is an error.
      */
-    private void connectSrcHandlerToPackage(
+    public int connectSrcHandlerToPackageSync(
             Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName) {
         if (DBG) log("connect srcHandler to dst Package & class E");
 
@@ -207,11 +217,61 @@
         Intent intent = new Intent(Intent.ACTION_MAIN);
         intent.setClassName(dstPackageName, dstClassName);
         boolean result = srcContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
-        if (!result) {
-            replyHalfConnected(STATUS_BINDING_UNSUCCESSFUL);
-        }
-
         if (DBG) log("connect srcHandler to dst Package & class X result=" + result);
+        return result ? STATUS_SUCCESSFUL : STATUS_BINDING_UNSUCCESSFUL;
+    }
+
+    /**
+     * Connect a handler to Messenger synchronously.
+     *
+     * @param srcContext is the context of the source
+     * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
+     *            messages
+     * @param dstMessenger is the hander to send messages to.
+     *
+     * @return STATUS_SUCCESSFUL on success any other value is an error.
+     */
+    public int connectSync(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
+        if (DBG) log("halfConnectSync srcHandler to the dstMessenger  E");
+
+        // We are connected
+        connected(srcContext, srcHandler, dstMessenger);
+
+        if (DBG) log("halfConnectSync srcHandler to the dstMessenger X");
+        return STATUS_SUCCESSFUL;
+    }
+
+    /**
+     * connect two local Handlers synchronously.
+     *
+     * @param srcContext is the context of the source
+     * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
+     *            messages
+     * @param dstHandler is the hander to send messages to.
+     *
+     * @return STATUS_SUCCESSFUL on success any other value is an error.
+     */
+    public int connectSync(Context srcContext, Handler srcHandler, Handler dstHandler) {
+        return connectSync(srcContext, srcHandler, new Messenger(dstHandler));
+    }
+
+    /**
+     * Fully connect two local Handlers synchronously.
+     *
+     * @param srcContext is the context of the source
+     * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
+     *            messages
+     * @param dstHandler is the hander to send messages to.
+     *
+     * @return STATUS_SUCCESSFUL on success any other value is an error.
+     */
+    public int fullyConnectSync(Context srcContext, Handler srcHandler, Handler dstHandler) {
+        int status = connectSync(srcContext, srcHandler, dstHandler);
+        if (status == STATUS_SUCCESSFUL) {
+            Message response = sendMessageSynchronously(CMD_CHANNEL_FULL_CONNECTION);
+            status = response.arg1;
+        }
+        return status;
     }
 
     /**
@@ -246,8 +306,11 @@
                 mDstClassName = dstClassName;
             }
 
+            @Override
             public void run() {
-                connectSrcHandlerToPackage(mSrcCtx, mSrcHdlr, mDstPackageName, mDstClassName);
+                int result = connectSrcHandlerToPackageSync(mSrcCtx, mSrcHdlr, mDstPackageName,
+                        mDstClassName);
+                replyHalfConnected(result);
             }
         }
 
@@ -286,15 +349,8 @@
     public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
         if (DBG) log("connect srcHandler to the dstMessenger  E");
 
-        // Initialize source fields
-        mSrcContext = srcContext;
-        mSrcHandler = srcHandler;
-        mSrcMessenger = new Messenger(mSrcHandler);
-
-        // Initialize destination fields
-        mDstMessenger = dstMessenger;
-
-        if (DBG) log("tell source we are half connected");
+        // We are connected
+        connected(srcContext, srcHandler, dstMessenger);
 
         // Tell source we are half connected
         replyHalfConnected(STATUS_SUCCESSFUL);
@@ -303,11 +359,31 @@
     }
 
     /**
-     * Connect two local Handlers.
+     * Connect handler to messenger. This method is typically called
+     * when a server receives a CMD_CHANNEL_FULL_CONNECTION request
+     * and initializes the internal instance variables to allow communication
+     * with the dstMessenger.
      *
-     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
-     *      msg.arg1 = status
-     *      msg.obj = the AsyncChannel
+     * @param srcContext
+     * @param srcHandler
+     * @param dstMessenger
+     */
+    public void connected(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
+        if (DBG) log("connected srcHandler to the dstMessenger  E");
+
+        // Initialize source fields
+        mSrcContext = srcContext;
+        mSrcHandler = srcHandler;
+        mSrcMessenger = new Messenger(mSrcHandler);
+
+        // Initialize destination fields
+        mDstMessenger = dstMessenger;
+
+        if (DBG) log("connected srcHandler to the dstMessenger X");
+    }
+
+    /**
+     * Connect two local Handlers.
      *
      * @param srcContext is the context of the source
      * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
@@ -336,6 +412,7 @@
      * To close the connection call when handler receives CMD_CHANNEL_DISCONNECTED
      */
     public void disconnected() {
+        mSrcContext = null;
         mSrcHandler = null;
         mSrcMessenger = null;
         mDstMessenger = null;
@@ -346,7 +423,7 @@
      * Disconnect
      */
     public void disconnect() {
-        if (mConnection != null) {
+        if ((mConnection != null) && (mSrcContext != null)) {
             mSrcContext.unbindService(mConnection);
         }
         if (mSrcHandler != null) {
@@ -445,6 +522,7 @@
      */
     public void replyToMessage(Message srcMsg, Message dstMsg) {
         try {
+            dstMsg.replyTo = mSrcMessenger;
             srcMsg.replyTo.send(dstMsg);
         } catch (RemoteException e) {
             log("TODO: handle replyToMessage RemoteException" + e);
@@ -695,10 +773,14 @@
         private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) {
             SyncMessenger sm = SyncMessenger.obtain();
             try {
-                msg.replyTo = sm.mMessenger;
-                dstMessenger.send(msg);
-                synchronized (sm.mHandler.mLockObject) {
-                    sm.mHandler.mLockObject.wait();
+                if (dstMessenger != null && msg != null) {
+                    msg.replyTo = sm.mMessenger;
+                    synchronized (sm.mHandler.mLockObject) {
+                        dstMessenger.send(msg);
+                        sm.mHandler.mLockObject.wait();
+                    }
+                } else {
+                    sm.mHandler.mResultMsg = null;
                 }
             } catch (InterruptedException e) {
                 sm.mHandler.mResultMsg = null;
@@ -747,11 +829,13 @@
         AsyncChannelConnection() {
         }
 
+        @Override
         public void onServiceConnected(ComponentName className, IBinder service) {
             mDstMessenger = new Messenger(service);
             replyHalfConnected(STATUS_SUCCESSFUL);
         }
 
+        @Override
         public void onServiceDisconnected(ComponentName className) {
             replyDisconnected(STATUS_SUCCESSFUL);
         }
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index 2689f09..2e7ec58 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -29,9 +29,18 @@
  * {@hide}
  */
 public class Protocol {
-    public static final int MAX_MESSAGE     =  0x0000FFFF;
+    public static final int MAX_MESSAGE                                             = 0x0000FFFF;
 
-    public static final int BASE_WIFI       =  0x00010000;
-    public static final int BASE_DHCP       =  0x00020000;
+    /** Base reserved for system */
+    public static final int BASE_SYSTEM_RESERVED                                    = 0x00010000;
+    public static final int BASE_SYSTEM_ASYNC_CHANNEL                               = 0x00011000;
+
+    /** Non system protocols */
+    public static final int BASE_WIFI                                               = 0x00020000;
+    public static final int BASE_DHCP                                               = 0x00030000;
+    public static final int BASE_DATA_CONNECTION                                    = 0x00040000;
+    public static final int BASE_DATA_CONNECTION_AC                                 = 0x00041000;
+    public static final int BASE_DATA_CONNECTION_TRACKER                            = 0x00050000;
+
     //TODO: define all used protocols
 }
diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
index e210b78..0051ec3 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
@@ -21,7 +21,6 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.util.Log;
 import android.util.SparseBooleanArray;
 import android.view.MenuItem;
 import android.view.SoundEffectConstants;
@@ -36,11 +35,14 @@
  * MenuPresenter for building action menus as seen in the action bar and action modes.
  */
 public class ActionMenuPresenter extends BaseMenuPresenter {
+    private static final String TAG = "ActionMenuPresenter";
+
     private View mOverflowButton;
     private boolean mReserveOverflow;
     private int mWidthLimit;
     private int mActionItemWidthLimit;
     private int mMaxItems;
+    private boolean mStrictWidthLimit;
 
     // Group IDs that have been added as actions - used temporarily, allocated here for reuse.
     private final SparseBooleanArray mActionButtonGroups = new SparseBooleanArray();
@@ -89,11 +91,12 @@
         mScrapActionButtonView = null;
     }
 
-    public void setWidthLimit(int width) {
+    public void setWidthLimit(int width, boolean strict) {
         if (mReserveOverflow) {
             width -= mOverflowButton.getMeasuredWidth();
         }
         mActionItemWidthLimit = width;
+        mStrictWidthLimit = strict;
     }
 
     public void setItemLimit(int itemCount) {
@@ -131,6 +134,8 @@
         if (mReserveOverflow && mMenu.getNonActionItems().size() > 0) {
             if (mOverflowButton == null) {
                 mOverflowButton = new OverflowMenuButton(mContext);
+                mOverflowButton.setLayoutParams(
+                        ((ActionMenuView) mMenuView).generateOverflowButtonLayoutParams());
             }
             ViewGroup parent = (ViewGroup) mOverflowButton.getParent();
             if (parent != mMenuView) {
@@ -189,7 +194,6 @@
     public boolean showOverflowMenu() {
         if (mReserveOverflow && !isOverflowMenuShowing() && mMenuView != null &&
                 mPostedOpenRunnable == null) {
-            Log.d("ActionMenuPresenter", "showOverflowMenu");
             OverflowPopup popup = new OverflowPopup(mContext, mMenu, mOverflowButton, true);
             mPostedOpenRunnable = new OpenOverflowRunnable(popup);
             // Post this for later; we might still need a layout for the anchor to be right.
@@ -338,9 +342,11 @@
                         firstActionWidth = measuredWidth;
                     }
 
-                    // Did this push the entire first item past halfway?
-                    if (widthLimit + firstActionWidth <= 0) {
-                        isAction = false;
+                    if (mStrictWidthLimit) {
+                        isAction = widthLimit >= 0;
+                    } else {
+                        // Did this push the entire first item past the limit?
+                        isAction = widthLimit + firstActionWidth > 0;
                     }
                 }
 
diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java
index 0ea9c89..290bf08 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuView.java
@@ -20,6 +20,7 @@
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.View;
+import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
@@ -33,6 +34,8 @@
 
     private boolean mReserveOverflow;
     private ActionMenuPresenter mPresenter;
+    private boolean mUpdateContentsBeforeMeasure;
+    private boolean mFormatItems;
 
     public ActionMenuView(Context context) {
         this(context, null);
@@ -59,6 +62,95 @@
     }
 
     @Override
+    public void requestLayout() {
+        // Layout can influence how many action items fit.
+        mUpdateContentsBeforeMeasure = true;
+        super.requestLayout();
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (mUpdateContentsBeforeMeasure && mMenu != null) {
+            mMenu.onItemsChanged(true);
+            mUpdateContentsBeforeMeasure = false;
+        }
+        // If we've been given an exact size to match, apply special formatting during layout.
+        mFormatItems = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY;
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        if (!mFormatItems) {
+            super.onLayout(changed, left, top, right, bottom);
+            return;
+        }
+
+        final int childCount = getChildCount();
+        final int midVertical = (top + bottom) / 2;
+        final int dividerWidth = getDividerWidth();
+        boolean hasOverflow = false;
+        int overflowWidth = 0;
+        int nonOverflowWidth = 0;
+        int nonOverflowCount = 0;
+        int widthRemaining = right - left - getPaddingRight() - getPaddingLeft();
+        for (int i = 0; i < childCount; i++) {
+            final View v = getChildAt(i);
+            if (v.getVisibility() == GONE) {
+                continue;
+            }
+
+            LayoutParams p = (LayoutParams) v.getLayoutParams();
+            if (p.isOverflowButton) {
+                hasOverflow = true;
+                overflowWidth = v.getMeasuredWidth();
+                if (hasDividerBeforeChildAt(i)) {
+                    overflowWidth += dividerWidth;
+                }
+
+                int height = v.getMeasuredHeight();
+                int r = getPaddingRight();
+                int l = r - overflowWidth;
+                int t = midVertical - (height / 2);
+                int b = t + height;
+                v.layout(l, t, r, b);
+
+                widthRemaining -= overflowWidth;
+            } else {
+                nonOverflowWidth += v.getMeasuredWidth() + p.leftMargin + p.rightMargin;
+                if (hasDividerBeforeChildAt(i)) {
+                    nonOverflowWidth += dividerWidth;
+                }
+                nonOverflowCount++;
+            }
+        }
+
+        // Try to center non-overflow items with uniformly spaced padding, including on the edges.
+        // Overflow will always pin to the right edge. If there isn't enough room for that,
+        // center in the remaining space.
+        if (nonOverflowWidth <= widthRemaining - overflowWidth) {
+            widthRemaining -= overflowWidth;
+        }
+
+        final int spacing = (widthRemaining - nonOverflowWidth) / (nonOverflowCount + 1);
+        int startLeft = getPaddingLeft() + overflowWidth + spacing;
+        for (int i = 0; i < childCount; i++) {
+            final View v = getChildAt(i);
+            final LayoutParams lp = (LayoutParams) v.getLayoutParams();
+            if (v.getVisibility() == GONE || lp.isOverflowButton) {
+                continue;
+            }
+
+            startLeft += lp.leftMargin;
+            int width = v.getMeasuredWidth();
+            int height = v.getMeasuredHeight();
+            int t = midVertical - (height / 2);
+            v.layout(startLeft, t, startLeft + width, t + height);
+            startLeft += width + lp.rightMargin + spacing;
+        }
+    }
+
+    @Override
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         mPresenter.dismissPopupMenus();
@@ -97,6 +189,12 @@
         return p instanceof LayoutParams;
     }
 
+    public LayoutParams generateOverflowButtonLayoutParams() {
+        LayoutParams result = generateDefaultLayoutParams();
+        result.isOverflowButton = true;
+        return result;
+    }
+
     public boolean invokeItem(MenuItemImpl item) {
         return mMenu.performItemAction(item, 0);
     }
@@ -127,4 +225,28 @@
         public boolean needsDividerBefore();
         public boolean needsDividerAfter();
     }
+
+    public static class LayoutParams extends LinearLayout.LayoutParams {
+        @ViewDebug.ExportedProperty(category = "layout")
+        public boolean isOverflowButton;
+
+        public LayoutParams(Context c, AttributeSet attrs) {
+            super(c, attrs);
+        }
+
+        public LayoutParams(LayoutParams other) {
+            super((LinearLayout.LayoutParams) other);
+            isOverflowButton = other.isOverflowButton;
+        }
+
+        public LayoutParams(int width, int height) {
+            super(width, height);
+            isOverflowButton = false;
+        }
+
+        public LayoutParams(int width, int height, boolean isOverflowButton) {
+            super(width, height);
+            this.isOverflowButton = isOverflowButton;
+        }
+    }
 }
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index 7fba5ca..e9fcb23 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -226,6 +226,7 @@
     private void dispatchPresenterUpdate(boolean cleared) {
         if (mPresenters.isEmpty()) return;
 
+        stopDispatchingItemsChanged();
         for (WeakReference<MenuPresenter> ref : mPresenters) {
             final MenuPresenter presenter = ref.get();
             if (presenter == null) {
@@ -234,6 +235,7 @@
                 presenter.updateMenuView(cleared);
             }
         }
+        startDispatchingItemsChanged();
     }
     
     private boolean dispatchSubMenuSelected(SubMenuBuilder subMenu) {
diff --git a/core/java/com/android/internal/widget/AbsActionBarView.java b/core/java/com/android/internal/widget/AbsActionBarView.java
new file mode 100644
index 0000000..3979eab
--- /dev/null
+++ b/core/java/com/android/internal/widget/AbsActionBarView.java
@@ -0,0 +1,211 @@
+/*
+ * 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.internal.widget;
+
+import com.android.internal.view.menu.ActionMenuPresenter;
+import com.android.internal.view.menu.ActionMenuView;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.DecelerateInterpolator;
+
+public abstract class AbsActionBarView extends ViewGroup {
+    protected ActionMenuView mMenuView;
+    protected ActionMenuPresenter mMenuPresenter;
+    protected ActionBarContainer mSplitView;
+
+    protected Animator mVisibilityAnim;
+    protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener();
+
+    private static final TimeInterpolator sAlphaInterpolator = new DecelerateInterpolator();
+
+    private static final int FADE_DURATION = 200;
+
+    public AbsActionBarView(Context context) {
+        super(context);
+    }
+
+    public AbsActionBarView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public AbsActionBarView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public void setSplitView(ActionBarContainer splitView) {
+        mSplitView = splitView;
+    }
+
+    public void animateToVisibility(int visibility) {
+        if (mVisibilityAnim != null) {
+            mVisibilityAnim.cancel();
+        }
+        if (visibility == VISIBLE) {
+            if (getVisibility() != VISIBLE) {
+                setAlpha(0);
+                if (mSplitView != null && mMenuView != null) {
+                    mMenuView.setAlpha(0);
+                }
+            }
+            ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 1);
+            anim.setDuration(FADE_DURATION);
+            anim.setInterpolator(sAlphaInterpolator);
+            if (mSplitView != null && mMenuView != null) {
+                AnimatorSet set = new AnimatorSet();
+                ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, "alpha", 1);
+                splitAnim.setDuration(FADE_DURATION);
+                set.addListener(mVisAnimListener.withFinalVisibility(visibility));
+                set.play(anim).with(splitAnim);
+            } else {
+                anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
+                anim.start();
+            }
+        } else {
+            ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 0);
+            anim.setDuration(FADE_DURATION);
+            anim.setInterpolator(sAlphaInterpolator);
+            if (mSplitView != null && mMenuView != null) {
+                AnimatorSet set = new AnimatorSet();
+                ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, "alpha", 0);
+                splitAnim.setDuration(FADE_DURATION);
+                set.addListener(mVisAnimListener.withFinalVisibility(visibility));
+                set.play(anim).with(splitAnim);
+            } else {
+                anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
+                anim.start();
+            }
+        }
+    }
+
+    @Override
+    public void setVisibility(int visibility) {
+        if (mVisibilityAnim != null) {
+            mVisibilityAnim.end();
+        }
+        super.setVisibility(visibility);
+    }
+
+    public boolean showOverflowMenu() {
+        if (mMenuPresenter != null) {
+            return mMenuPresenter.showOverflowMenu();
+        }
+        return false;
+    }
+
+    public void postShowOverflowMenu() {
+        post(new Runnable() {
+            public void run() {
+                showOverflowMenu();
+            }
+        });
+    }
+
+    public boolean hideOverflowMenu() {
+        if (mMenuPresenter != null) {
+            return mMenuPresenter.hideOverflowMenu();
+        }
+        return false;
+    }
+
+    public boolean isOverflowMenuShowing() {
+        if (mMenuPresenter != null) {
+            return mMenuPresenter.isOverflowMenuShowing();
+        }
+        return false;
+    }
+
+    public boolean isOverflowReserved() {
+        return mMenuPresenter != null && mMenuPresenter.isOverflowReserved();
+    }
+
+    public void dismissPopupMenus() {
+        if (mMenuPresenter != null) {
+            mMenuPresenter.dismissPopupMenus();
+        }
+    }
+
+    protected int measureChildView(View child, int availableWidth, int childSpecHeight,
+            int spacing) {
+        child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
+                childSpecHeight);
+
+        availableWidth -= child.getMeasuredWidth();
+        availableWidth -= spacing;
+
+        return availableWidth;
+    }
+
+    protected int positionChild(View child, int x, int y, int contentHeight) {
+        int childWidth = child.getMeasuredWidth();
+        int childHeight = child.getMeasuredHeight();
+        int childTop = y + (contentHeight - childHeight) / 2;
+
+        child.layout(x, childTop, x + childWidth, childTop + childHeight);
+
+        return childWidth;
+    }
+
+    protected int positionChildInverse(View child, int x, int y, int contentHeight) {
+        int childWidth = child.getMeasuredWidth();
+        int childHeight = child.getMeasuredHeight();
+        int childTop = y + (contentHeight - childHeight) / 2;
+
+        child.layout(x - childWidth, childTop, x, childTop + childHeight);
+
+        return childWidth;
+    }
+
+    protected class VisibilityAnimListener implements Animator.AnimatorListener {
+        private boolean mCanceled = false;
+        private int mFinalVisibility;
+
+        public VisibilityAnimListener withFinalVisibility(int visibility) {
+            mFinalVisibility = visibility;
+            return this;
+        }
+
+        @Override
+        public void onAnimationStart(Animator animation) {
+            setVisibility(VISIBLE);
+            mVisibilityAnim = animation;
+            mCanceled = false;
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            if (mCanceled) return;
+
+            mVisibilityAnim = null;
+            setVisibility(mFinalVisibility);
+        }
+
+        @Override
+        public void onAnimationCancel(Animator animation) {
+            mCanceled = true;
+        }
+
+        @Override
+        public void onAnimationRepeat(Animator animation) {
+        }
+    }
+}
diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java
index 3deb036..c18565d 100644
--- a/core/java/com/android/internal/widget/ActionBarContainer.java
+++ b/core/java/com/android/internal/widget/ActionBarContainer.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
+import android.view.ActionMode;
 import android.view.MotionEvent;
 import android.view.View;
 import android.widget.FrameLayout;
@@ -85,6 +86,12 @@
     }
 
     @Override
+    public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) {
+        // No starting an action mode for an action bar child! (Where would it go?)
+        return null;
+    }
+
+    @Override
     public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         if (mTabContainer != null && mTabContainer.getVisibility() != GONE) {
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index 70fb3b2..f45a3bb 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -30,7 +30,7 @@
 import android.view.ActionMode;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.LinearLayout;
 import android.widget.TextView;
@@ -38,7 +38,7 @@
 /**
  * @hide
  */
-public class ActionBarContextView extends ViewGroup implements AnimatorListener {
+public class ActionBarContextView extends AbsActionBarView implements AnimatorListener {
     private static final String TAG = "ActionBarContextView";
 
     private int mContentHeight;
@@ -53,8 +53,6 @@
     private TextView mSubtitleView;
     private int mTitleStyleRes;
     private int mSubtitleStyleRes;
-    private ActionMenuView mMenuView;
-    private ActionMenuPresenter mPresenter;
 
     private Animator mCurrentAnimation;
     private boolean mAnimateInOnLayout;
@@ -87,12 +85,6 @@
                 com.android.internal.R.styleable.ActionMode_height, 0);
         a.recycle();
     }
-    
-    @Override
-    public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) {
-        // No starting an action mode for an existing action mode UI child! (Where would it go?)
-        return null;
-    }
 
     public void setHeight(int height) {
         mContentHeight = height;
@@ -178,10 +170,25 @@
         });
 
         final MenuBuilder menu = (MenuBuilder) mode.getMenu();
-        mPresenter = new ActionMenuPresenter();
-        menu.addMenuPresenter(mPresenter);
-        mMenuView = (ActionMenuView) mPresenter.getMenuView(this);
-        addView(mMenuView);
+        mMenuPresenter = new ActionMenuPresenter();
+        menu.addMenuPresenter(mMenuPresenter);
+        mMenuView = (ActionMenuView) mMenuPresenter.getMenuView(this);
+
+        final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
+                LayoutParams.MATCH_PARENT);
+        mMenuView.setLayoutParams(layoutParams);
+        if (mSplitView == null) {
+            addView(mMenuView);
+        } else {
+            // Allow full screen width in split mode.
+            mMenuPresenter.setWidthLimit(
+                    getContext().getResources().getDisplayMetrics().widthPixels, true);
+            // No limit to the item count; use whatever will fit.
+            mMenuPresenter.setItemLimit(Integer.MAX_VALUE);
+            // Span the whole width
+            layoutParams.width = LayoutParams.MATCH_PARENT;
+            mSplitView.addView(mMenuView);
+        }
 
         mAnimateInOnLayout = true;
     }
@@ -213,28 +220,31 @@
     public void killMode() {
         finishAnimation();
         removeAllViews();
+        if (mSplitView != null) {
+            mSplitView.removeView(mMenuView);
+        }
         mCustomView = null;
         mMenuView = null;
         mAnimateInOnLayout = false;
     }
 
     public boolean showOverflowMenu() {
-        if (mPresenter != null) {
-            return mPresenter.showOverflowMenu();
+        if (mMenuPresenter != null) {
+            return mMenuPresenter.showOverflowMenu();
         }
         return false;
     }
 
     public boolean hideOverflowMenu() {
-        if (mPresenter != null) {
-            return mPresenter.hideOverflowMenu();
+        if (mMenuPresenter != null) {
+            return mMenuPresenter.hideOverflowMenu();
         }
         return false;
     }
 
     public boolean isOverflowMenuShowing() {
-        if (mPresenter != null) {
-            return mPresenter.isOverflowMenuShowing();
+        if (mMenuPresenter != null) {
+            return mMenuPresenter.isOverflowMenuShowing();
         }
         return false;
     }
@@ -342,7 +352,7 @@
 
     private Animator makeOutAnimation() {
         ObjectAnimator buttonAnimator = ObjectAnimator.ofFloat(mClose, "translationX",
-                0, -mClose.getWidth());
+                -mClose.getWidth());
         buttonAnimator.setDuration(200);
         buttonAnimator.addListener(this);
         buttonAnimator.setInterpolator(new DecelerateInterpolator());
@@ -356,7 +366,7 @@
                 for (int i = 0; i < 0; i++) {
                     View child = mMenuView.getChildAt(i);
                     child.setScaleY(0);
-                    ObjectAnimator a = ObjectAnimator.ofFloat(child, "scaleY", 1, 0);
+                    ObjectAnimator a = ObjectAnimator.ofFloat(child, "scaleY", 0);
                     a.setDuration(100);
                     a.setStartDelay(i * 70);
                     b.with(a);
@@ -383,7 +393,7 @@
                 mAnimateInOnLayout = false;
             }
         }
-        
+
         if (mTitleLayout != null && mCustomView == null) {
             x += positionChild(mTitleLayout, x, y, contentHeight);
         }
@@ -399,36 +409,6 @@
         }
     }
 
-    private int measureChildView(View child, int availableWidth, int childSpecHeight, int spacing) {
-        child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
-                childSpecHeight);
-
-        availableWidth -= child.getMeasuredWidth();
-        availableWidth -= spacing;
-
-        return availableWidth;
-    }
-    
-    private int positionChild(View child, int x, int y, int contentHeight) {
-        int childWidth = child.getMeasuredWidth();
-        int childHeight = child.getMeasuredHeight();
-        int childTop = y + (contentHeight - childHeight) / 2;
-
-        child.layout(x, childTop, x + childWidth, childTop + childHeight);
-
-        return childWidth;
-    }
-    
-    private int positionChildInverse(View child, int x, int y, int contentHeight) {
-        int childWidth = child.getMeasuredWidth();
-        int childHeight = child.getMeasuredHeight();
-        int childTop = y + (contentHeight - childHeight) / 2;
-
-        child.layout(x - childWidth, childTop, x, childTop + childHeight);
-
-        return childWidth;
-    }
-
     @Override
     public void onAnimationStart(Animator animation) {
     }
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 0c13f7b..f321e8d 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -38,7 +38,6 @@
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
-import android.view.ActionMode;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.Menu;
@@ -59,7 +58,7 @@
 /**
  * @hide
  */
-public class ActionBarView extends ViewGroup {
+public class ActionBarView extends AbsActionBarView {
     private static final String TAG = "ActionBarView";
 
     /**
@@ -115,11 +114,8 @@
     private boolean mIncludeTabs;
 
     private MenuBuilder mOptionsMenu;
-    private ActionMenuView mMenuView;
-    private ActionMenuPresenter mActionMenuPresenter;
     
     private ActionBarContextView mContextView;
-    private ViewGroup mSplitView;
 
     private ActionMenuItem mLogoNavItem;
 
@@ -274,12 +270,6 @@
         mTabLayout = tabLayout;
     }
 
-    @Override
-    public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) {
-        // No starting an action mode for an action bar child! (Where would it go?)
-        return null;
-    }
-
     public void setCallback(OnNavigationListener callback) {
         mCallback = callback;
     }
@@ -288,7 +278,7 @@
         if (menu == mOptionsMenu) return;
 
         if (mOptionsMenu != null) {
-            mOptionsMenu.removeMenuPresenter(mActionMenuPresenter);
+            mOptionsMenu.removeMenuPresenter(mMenuPresenter);
         }
 
         MenuBuilder builder = (MenuBuilder) menu;
@@ -296,12 +286,12 @@
         if (mMenuView != null) {
             removeView(mMenuView);
         }
-        if (mActionMenuPresenter == null) {
-            mActionMenuPresenter = new ActionMenuPresenter();
-            mActionMenuPresenter.setCallback(cb);
-            builder.addMenuPresenter(mActionMenuPresenter);
+        if (mMenuPresenter == null) {
+            mMenuPresenter = new ActionMenuPresenter();
+            mMenuPresenter.setCallback(cb);
+            builder.addMenuPresenter(mMenuPresenter);
         }
-        final ActionMenuView menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
+        final ActionMenuView menuView = (ActionMenuView) mMenuPresenter.getMenuView(this);
         final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
                 LayoutParams.MATCH_PARENT);
         menuView.setLayoutParams(layoutParams);
@@ -309,10 +299,12 @@
             addView(menuView);
         } else {
             // Allow full screen width in split mode.
-            mActionMenuPresenter.setWidthLimit(
-                    getContext().getResources().getDisplayMetrics().widthPixels);
+            mMenuPresenter.setWidthLimit(
+                    getContext().getResources().getDisplayMetrics().widthPixels, true);
             // No limit to the item count; use whatever will fit.
-            mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE);
+            mMenuPresenter.setItemLimit(Integer.MAX_VALUE);
+            // Span the whole width
+            layoutParams.width = LayoutParams.MATCH_PARENT;
             if (mSplitView != null) {
                 mSplitView.addView(menuView);
             } // We'll add this later if we missed it this time.
@@ -320,59 +312,6 @@
         mMenuView = menuView;
     }
 
-    public void setSplitView(ViewGroup splitView) {
-        mSplitView = splitView;
-        splitView.setVisibility(VISIBLE);
-        if (mMenuView != null) {
-            splitView.addView(mMenuView);
-        }
-    }
-
-    public boolean showOverflowMenu() {
-        if (mActionMenuPresenter != null) {
-            return mActionMenuPresenter.showOverflowMenu();
-        }
-        return false;
-    }
-
-    public void openOverflowMenu() {
-        if (mActionMenuPresenter != null) {
-            showOverflowMenu();
-        }
-    }
-
-    public void postShowOverflowMenu() {
-        post(new Runnable() {
-            public void run() {
-                showOverflowMenu();
-            }
-        });
-    }
-
-    public boolean hideOverflowMenu() {
-        if (mActionMenuPresenter != null) {
-            return mActionMenuPresenter.hideOverflowMenu();
-        }
-        return false;
-    }
-
-    public boolean isOverflowMenuShowing() {
-        if (mActionMenuPresenter != null) {
-            return mActionMenuPresenter.isOverflowMenuShowing();
-        }
-        return false;
-    }
-
-    public boolean isOverflowReserved() {
-        return mActionMenuPresenter != null && mActionMenuPresenter.isOverflowReserved();
-    }
-
-    public void dismissPopupMenus() {
-        if (mActionMenuPresenter != null) {
-            mActionMenuPresenter.dismissPopupMenus();
-        }
-    }
-
     public void setCustomNavigationView(View view) {
         final boolean showCustom = (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0;
         if (mCustomNavView != null && showCustom) {
@@ -648,6 +587,10 @@
         }
     }
 
+    public void updateTab(int position) {
+        ((TabView) mTabLayout.getChildAt(position)).update();
+    }
+
     public void removeTabAt(int position) {
         if (mTabLayout != null) {
             mTabLayout.removeViewAt(position);
@@ -876,16 +819,6 @@
         }
     }
 
-    private int measureChildView(View child, int availableWidth, int childSpecHeight, int spacing) {
-        child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
-                childSpecHeight);
-
-        availableWidth -= child.getMeasuredWidth();
-        availableWidth -= spacing;
-
-        return availableWidth;
-    }
-
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         int x = getPaddingLeft();
@@ -998,68 +931,78 @@
         }
     }
 
-    private int positionChild(View child, int x, int y, int contentHeight) {
-        int childWidth = child.getMeasuredWidth();
-        int childHeight = child.getMeasuredHeight();
-        int childTop = y + (contentHeight - childHeight) / 2;
-
-        child.layout(x, childTop, x + childWidth, childTop + childHeight);
-
-        return childWidth;
-    }
-    
-    private int positionChildInverse(View child, int x, int y, int contentHeight) {
-        int childWidth = child.getMeasuredWidth();
-        int childHeight = child.getMeasuredHeight();
-        int childTop = y + (contentHeight - childHeight) / 2;
-
-        child.layout(x - childWidth, childTop, x, childTop + childHeight);
-
-        return childWidth;
-    }
-
     private static class TabView extends LinearLayout {
         private ActionBar.Tab mTab;
+        private TextView mTextView;
+        private ImageView mIconView;
+        private View mCustomView;
 
         public TabView(Context context, ActionBar.Tab tab) {
             super(context, null, com.android.internal.R.attr.actionBarTabStyle);
             mTab = tab;
 
+            update();
+
+            setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
+                    LayoutParams.MATCH_PARENT, 1));
+        }
+
+        public void update() {
+            final ActionBar.Tab tab = mTab;
             final View custom = tab.getCustomView();
             if (custom != null) {
                 addView(custom);
+                mCustomView = custom;
+                if (mTextView != null) mTextView.setVisibility(GONE);
+                if (mIconView != null) {
+                    mIconView.setVisibility(GONE);
+                    mIconView.setImageDrawable(null);
+                }
             } else {
-                // TODO Style tabs based on the theme
+                if (mCustomView != null) {
+                    removeView(mCustomView);
+                    mCustomView = null;
+                }
 
                 final Drawable icon = tab.getIcon();
                 final CharSequence text = tab.getText();
 
                 if (icon != null) {
-                    ImageView iconView = new ImageView(context);
-                    iconView.setImageDrawable(icon);
-                    LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
-                            LayoutParams.WRAP_CONTENT);
-                    lp.gravity = Gravity.CENTER_VERTICAL;
-                    iconView.setLayoutParams(lp);
-                    addView(iconView);
+                    if (mIconView == null) {
+                        ImageView iconView = new ImageView(getContext());
+                        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
+                                LayoutParams.WRAP_CONTENT);
+                        lp.gravity = Gravity.CENTER_VERTICAL;
+                        iconView.setLayoutParams(lp);
+                        addView(iconView, 0);
+                        mIconView = iconView;
+                    }
+                    mIconView.setImageDrawable(icon);
+                    mIconView.setVisibility(VISIBLE);
+                } else if (mIconView != null) {
+                    mIconView.setVisibility(GONE);
+                    mIconView.setImageDrawable(null);
                 }
 
                 if (text != null) {
-                    TextView textView = new TextView(context, null,
-                            com.android.internal.R.attr.actionBarTabTextStyle);
-                    textView.setText(text);
-                    textView.setSingleLine();
-                    textView.setEllipsize(TruncateAt.END);
-                    LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
-                            LayoutParams.WRAP_CONTENT);
-                    lp.gravity = Gravity.CENTER_VERTICAL;
-                    textView.setLayoutParams(lp);
-                    addView(textView);
+                    if (mTextView == null) {
+                        TextView textView = new TextView(getContext(), null,
+                                com.android.internal.R.attr.actionBarTabTextStyle);
+                        textView.setSingleLine();
+                        textView.setEllipsize(TruncateAt.END);
+                        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
+                                LayoutParams.WRAP_CONTENT);
+                        lp.gravity = Gravity.CENTER_VERTICAL;
+                        textView.setLayoutParams(lp);
+                        addView(textView);
+                        mTextView = textView;
+                    }
+                    mTextView.setText(text);
+                    mTextView.setVisibility(VISIBLE);
+                } else {
+                    mTextView.setVisibility(GONE);
                 }
             }
-
-            setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
-                    LayoutParams.MATCH_PARENT, 1));
         }
 
         public ActionBar.Tab getTab() {
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 290f528..95224a4 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -47,6 +47,7 @@
 	android_emoji_EmojiFactory.cpp \
 	android_view_Display.cpp \
 	android_view_Surface.cpp \
+	android_view_TextureView.cpp \
 	android_view_ViewRoot.cpp \
 	android_view_InputChannel.cpp \
 	android_view_InputQueue.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index a4a229a..c915753 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -118,6 +118,7 @@
 extern int register_android_view_Display(JNIEnv* env);
 extern int register_android_view_GLES20Canvas(JNIEnv* env);
 extern int register_android_view_Surface(JNIEnv* env);
+extern int register_android_view_TextureView(JNIEnv* env);
 extern int register_android_view_ViewRoot(JNIEnv* env);
 extern int register_android_database_CursorWindow(JNIEnv* env);
 extern int register_android_database_SQLiteCompiledSql(JNIEnv* env);
@@ -1122,6 +1123,7 @@
     REG_JNI(register_android_graphics_Graphics),
     REG_JNI(register_android_view_GLES20Canvas),
     REG_JNI(register_android_view_Surface),
+    REG_JNI(register_android_view_TextureView),
     REG_JNI(register_android_view_ViewRoot),
     REG_JNI(register_com_google_android_gles_jni_EGLImpl),
     REG_JNI(register_com_google_android_gles_jni_GLImpl),
diff --git a/core/jni/android/graphics/Movie.cpp b/core/jni/android/graphics/Movie.cpp
index c112423..c1acaa3 100644
--- a/core/jni/android/graphics/Movie.cpp
+++ b/core/jni/android/graphics/Movie.cpp
@@ -112,6 +112,10 @@
     return create_jmovie(env, moov);
 }
 
+static void movie_destructor(JNIEnv* env, jobject, SkMovie* movie) {
+    delete movie;
+}
+
 //////////////////////////////////////////////////////////////////////////////////////////////
 
 #include <android_runtime/AndroidRuntime.h>
@@ -126,6 +130,7 @@
                             (void*)movie_draw  },
     { "decodeStream", "(Ljava/io/InputStream;)Landroid/graphics/Movie;",
                             (void*)movie_decodeStream },
+    { "nativeDestructor","(I)V", (void*)movie_destructor },
     { "decodeByteArray", "([BII)Landroid/graphics/Movie;",
                             (void*)movie_decodeByteArray },
 };
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 4a0e68e..548376d 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -40,6 +40,16 @@
                     const char *dns2,
                     const char *server,
                     uint32_t  *lease);
+
+int dhcp_do_request_renew(const char *ifname,
+                    const char *ipaddr,
+                    const char *gateway,
+                    uint32_t  *prefixLength,
+                    const char *dns1,
+                    const char *dns2,
+                    const char *server,
+                    uint32_t  *lease);
+
 int dhcp_stop(const char *ifname);
 int dhcp_release_lease(const char *ifname);
 char *dhcp_get_errmsg();
@@ -57,7 +67,6 @@
 static struct fieldIds {
     jmethodID constructorId;
     jfieldID ipaddress;
-    jfieldID gateway;
     jfieldID prefixLength;
     jfieldID dns1;
     jfieldID dns2;
@@ -145,7 +154,8 @@
     return (jint)result;
 }
 
-static jboolean android_net_utils_runDhcp(JNIEnv* env, jobject clazz, jstring ifname, jobject info)
+static jboolean android_net_utils_runDhcpCommon(JNIEnv* env, jobject clazz, jstring ifname,
+        jobject info, bool renew)
 {
     int result;
     char  ipaddr[PROPERTY_VALUE_MAX];
@@ -159,12 +169,41 @@
     const char *nameStr = env->GetStringUTFChars(ifname, NULL);
     if (nameStr == NULL) return (jboolean)false;
 
-    result = ::dhcp_do_request(nameStr, ipaddr, gateway, &prefixLength,
-                                        dns1, dns2, server, &lease);
+    if (renew) {
+        result = ::dhcp_do_request_renew(nameStr, ipaddr, gateway, &prefixLength,
+                dns1, dns2, server, &lease);
+    } else {
+        result = ::dhcp_do_request(nameStr, ipaddr, gateway, &prefixLength,
+                dns1, dns2, server, &lease);
+    }
+
     env->ReleaseStringUTFChars(ifname, nameStr);
     if (result == 0) {
         env->SetObjectField(info, dhcpInfoInternalFieldIds.ipaddress, env->NewStringUTF(ipaddr));
-        env->SetObjectField(info, dhcpInfoInternalFieldIds.gateway, env->NewStringUTF(gateway));
+
+        // set the gateway
+        jclass cls = env->FindClass("java/net/InetAddress");
+        jmethodID method = env->GetStaticMethodID(cls, "getByName",
+                "(Ljava/lang/String;)Ljava/net/InetAddress;");
+        jvalue args[1];
+        args[0].l = env->NewStringUTF(gateway);
+        jobject inetAddressObject = env->CallStaticObjectMethodA(cls, method, args);
+
+        if (!env->ExceptionOccurred()) {
+            cls = env->FindClass("android/net/RouteInfo");
+            method = env->GetMethodID(cls, "<init>", "(Ljava/net/InetAddress;)V");
+            args[0].l = inetAddressObject;
+            jobject routeInfoObject = env->NewObjectA(cls, method, args);
+
+            cls = env->FindClass("android/net/DhcpInfoInternal");
+            method = env->GetMethodID(cls, "addRoute", "(Landroid/net/RouteInfo;)V");
+            args[0].l = routeInfoObject;
+            env->CallVoidMethodA(info, method, args);
+        } else {
+            // if we have an exception (host not found perhaps), just don't add the route
+            env->ExceptionClear();
+        }
+
         env->SetIntField(info, dhcpInfoInternalFieldIds.prefixLength, prefixLength);
         env->SetObjectField(info, dhcpInfoInternalFieldIds.dns1, env->NewStringUTF(dns1));
         env->SetObjectField(info, dhcpInfoInternalFieldIds.dns2, env->NewStringUTF(dns2));
@@ -175,6 +214,17 @@
     return (jboolean)(result == 0);
 }
 
+static jboolean android_net_utils_runDhcp(JNIEnv* env, jobject clazz, jstring ifname, jobject info)
+{
+    return android_net_utils_runDhcpCommon(env, clazz, ifname, info, false);
+}
+
+static jboolean android_net_utils_runDhcpRenew(JNIEnv* env, jobject clazz, jstring ifname, jobject info)
+{
+    return android_net_utils_runDhcpCommon(env, clazz, ifname, info, true);
+}
+
+
 static jboolean android_net_utils_stopDhcp(JNIEnv* env, jobject clazz, jstring ifname)
 {
     int result;
@@ -218,6 +268,7 @@
     { "removeDefaultRoute", "(Ljava/lang/String;)I",  (void *)android_net_utils_removeDefaultRoute },
     { "resetConnections", "(Ljava/lang/String;)I",  (void *)android_net_utils_resetConnections },
     { "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfoInternal;)Z",  (void *)android_net_utils_runDhcp },
+    { "runDhcpRenew", "(Ljava/lang/String;Landroid/net/DhcpInfoInternal;)Z",  (void *)android_net_utils_runDhcpRenew },
     { "stopDhcp", "(Ljava/lang/String;)Z",  (void *)android_net_utils_stopDhcp },
     { "releaseDhcpLease", "(Ljava/lang/String;)Z",  (void *)android_net_utils_releaseDhcpLease },
     { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_utils_getDhcpError },
@@ -229,7 +280,6 @@
     LOG_FATAL_IF(dhcpInfoInternalClass == NULL, "Unable to find class android/net/DhcpInfoInternal");
     dhcpInfoInternalFieldIds.constructorId = env->GetMethodID(dhcpInfoInternalClass, "<init>", "()V");
     dhcpInfoInternalFieldIds.ipaddress = env->GetFieldID(dhcpInfoInternalClass, "ipAddress", "Ljava/lang/String;");
-    dhcpInfoInternalFieldIds.gateway = env->GetFieldID(dhcpInfoInternalClass, "gateway", "Ljava/lang/String;");
     dhcpInfoInternalFieldIds.prefixLength = env->GetFieldID(dhcpInfoInternalClass, "prefixLength", "I");
     dhcpInfoInternalFieldIds.dns1 = env->GetFieldID(dhcpInfoInternalClass, "dns1", "Ljava/lang/String;");
     dhcpInfoInternalFieldIds.dns2 = env->GetFieldID(dhcpInfoInternalClass, "dns2", "Ljava/lang/String;");
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index af7639a..f929a0e 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -24,6 +24,8 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/ResourceTypes.h>
 
+#include <gui/SurfaceTexture.h>
+
 #include <SkBitmap.h>
 #include <SkCanvas.h>
 #include <SkMatrix.h>
@@ -577,10 +579,13 @@
 }
 
 static void android_view_GLES20Canvas_updateTextureLayer(JNIEnv* env, jobject clazz,
-        Layer* layer, jint width, jint height, jfloatArray texTransform) {
-    jfloat* transform = env->GetFloatArrayElements(texTransform, NULL);
-    LayerRenderer::updateTextureLayer(layer, width, height, transform);
-    env->ReleaseFloatArrayElements(texTransform, transform, 0);
+        Layer* layer, jint width, jint height, SurfaceTexture* surface) {
+    float transform[16];
+    surface->updateTexImage();
+    surface->getTransformMatrix(transform);
+    GLenum renderTarget = surface->getCurrentTextureTarget();
+
+    LayerRenderer::updateTextureLayer(layer, width, height, renderTarget, transform);
 }
 
 static void android_view_GLES20Canvas_destroyLayer(JNIEnv* env, jobject clazz, Layer* layer) {
@@ -717,7 +722,7 @@
     { "nCreateLayer",            "(IIZ[I)I",   (void*) android_view_GLES20Canvas_createLayer },
     { "nResizeLayer",            "(III[I)V" ,  (void*) android_view_GLES20Canvas_resizeLayer },
     { "nCreateTextureLayer",     "([I)I",      (void*) android_view_GLES20Canvas_createTextureLayer },
-    { "nUpdateTextureLayer",     "(III[F)V" ,  (void*) android_view_GLES20Canvas_updateTextureLayer },
+    { "nUpdateTextureLayer",     "(IIII)V",    (void*) android_view_GLES20Canvas_updateTextureLayer },
     { "nDestroyLayer",           "(I)V",       (void*) android_view_GLES20Canvas_destroyLayer },
     { "nDestroyLayerDeferred",   "(I)V",       (void*) android_view_GLES20Canvas_destroyLayerDeferred },
     { "nDrawLayer",              "(IIFFI)V",   (void*) android_view_GLES20Canvas_drawLayer },
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
new file mode 100644
index 0000000..c5d86c8
--- /dev/null
+++ b/core/jni/android_view_TextureView.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+
+#include <gui/SurfaceTexture.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+// Native layer
+// ----------------------------------------------------------------------------
+
+static void android_view_TextureView_setDefaultBufferSize(JNIEnv* env, jobject,
+    jint surfaceTexture, jint width, jint height) {
+
+    sp<SurfaceTexture> surface = reinterpret_cast<SurfaceTexture*>(surfaceTexture);
+    surface->setDefaultBufferSize(width, height);
+}
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+const char* const kClassPathName = "android/view/TextureView";
+
+static JNINativeMethod gMethods[] = {
+    {   "nSetDefaultBufferSize", "(III)V", (void*) android_view_TextureView_setDefaultBufferSize }
+};
+
+int register_android_view_TextureView(JNIEnv* env) {
+    return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
+}
+
+};
diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
index 5f2065a..f777527 100644
--- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
@@ -27,6 +27,9 @@
 #include <SkBitmap.h>
 #include <SkPixelRef.h>
 
+#include <gui/SurfaceTexture.h>
+#include <gui/SurfaceTextureClient.h>
+
 namespace android {
 
 static jclass gConfig_class;
@@ -319,6 +322,35 @@
     return (jint)sur;
 }
 
+static jint jni_eglCreateWindowSurfaceTexture(JNIEnv *_env, jobject _this, jobject display,
+        jobject config, jint native_window, jintArray attrib_list) {
+    if (display == NULL || config == NULL
+        || !validAttribList(_env, attrib_list)) {
+        jniThrowException(_env, "java/lang/IllegalArgumentException", NULL);
+        return JNI_FALSE;
+    }
+    EGLDisplay dpy = getDisplay(_env, display);
+    EGLContext cnf = getConfig(_env, config);
+    sp<ANativeWindow> window;
+    if (native_window == 0) {
+not_valid_surface:
+        jniThrowException(_env, "java/lang/IllegalArgumentException",
+                "Make sure the SurfaceTexture is valid");
+        return 0;
+    }
+    
+    sp<SurfaceTexture> surfaceTexture = reinterpret_cast<SurfaceTexture*>(native_window);
+
+    window = new SurfaceTextureClient(surfaceTexture);
+    if (window == NULL)
+        goto not_valid_surface;
+
+    jint* base = beginNativeAttribList(_env, attrib_list);
+    EGLSurface sur = eglCreateWindowSurface(dpy, cnf, window.get(), base);
+    endNativeAttributeList(_env, attrib_list, base);
+    return (jint)sur;
+}
+
 static jboolean jni_eglGetConfigAttrib(JNIEnv *_env, jobject _this, jobject display,
         jobject config, jint attribute, jintArray value) {
     if (display == NULL || config == NULL
@@ -508,6 +540,7 @@
 {"_eglCreatePbufferSurface","(" DISPLAY CONFIG "[I)I", (void*)jni_eglCreatePbufferSurface },
 {"_eglCreatePixmapSurface", "(" SURFACE DISPLAY CONFIG OBJECT "[I)V", (void*)jni_eglCreatePixmapSurface },
 {"_eglCreateWindowSurface", "(" DISPLAY CONFIG OBJECT "[I)I", (void*)jni_eglCreateWindowSurface },
+{"_eglCreateWindowSurfaceTexture", "(" DISPLAY CONFIG "I[I)I", (void*)jni_eglCreateWindowSurfaceTexture },
 {"eglDestroyContext",      "(" DISPLAY CONTEXT ")Z", (void*)jni_eglDestroyContext },
 {"eglDestroySurface",      "(" DISPLAY SURFACE ")Z", (void*)jni_eglDestroySurface },
 {"eglMakeCurrent",         "(" DISPLAY SURFACE SURFACE CONTEXT")Z", (void*)jni_eglMakeCurrent },
diff --git a/core/res/res/layout/action_bar_title_item.xml b/core/res/res/layout/action_bar_title_item.xml
index 0cf4222..d8b729d 100644
--- a/core/res/res/layout/action_bar_title_item.xml
+++ b/core/res/res/layout/action_bar_title_item.xml
@@ -28,5 +28,6 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:singleLine="true"
-        android:ellipsize="end" />
+        android:ellipsize="end"
+        android:visibility="gone" />
 </LinearLayout>
diff --git a/core/res/res/layout/screen_action_bar.xml b/core/res/res/layout/screen_action_bar.xml
index 14af446..9742b94 100644
--- a/core/res/res/layout/screen_action_bar.xml
+++ b/core/res/res/layout/screen_action_bar.xml
@@ -43,7 +43,7 @@
         android:layout_weight="1"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
-    <LinearLayout android:id="@+id/lower_action_context_bar"
+    <com.android.internal.widget.ActionBarContainer android:id="@+id/split_action_bar"
                   android:layout_width="match_parent"
                   android:layout_height="wrap_content"
                   style="?android:attr/actionBarStyle"
diff --git a/core/res/res/layout/screen_action_bar_overlay.xml b/core/res/res/layout/screen_action_bar_overlay.xml
index aebbe41..086acdd 100644
--- a/core/res/res/layout/screen_action_bar_overlay.xml
+++ b/core/res/res/layout/screen_action_bar_overlay.xml
@@ -46,7 +46,7 @@
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_below="@id/action_bar_container" />
-    <LinearLayout android:id="@+id/lower_action_context_bar"
+    <com.android.internal.widget.ActionBarContainer android:id="@+id/split_action_bar"
                   android:layout_width="match_parent"
                   android:layout_height="wrap_content"
                   android:layout_alignParentBottom="true"
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index fbf4161..62e79f8 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -50,7 +50,7 @@
     <string name="needPuk2" msgid="4526033371987193070">"Escriviu el PUK2 per desbloquejar la targeta SIM."</string>
     <string name="ClipMmi" msgid="6952821216480289285">"Identificació de trucada entrant"</string>
     <string name="ClirMmi" msgid="7784673673446833091">"Identificació de trucada de sortida"</string>
-    <string name="CfMmi" msgid="5123218989141573515">"Desviament de trucades"</string>
+    <string name="CfMmi" msgid="5123218989141573515">"Desviació de trucades"</string>
     <string name="CwMmi" msgid="9129678056795016867">"Trucada en espera"</string>
     <string name="BaMmi" msgid="455193067926770581">"Restricció de trucades"</string>
     <string name="PwdMmi" msgid="7043715687905254199">"Canvi de contrasenya"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 64da78f..acb9821 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -204,8 +204,8 @@
     <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>
-    <string name="permlab_removeTasks" msgid="4802740047161700683">"Aktive Anwendungen beenden"</string>
-    <string name="permdesc_removeTasks" msgid="2000332928514575461">"Ermöglicht einer Anwendung das Entfernen von Aufgaben und Beenden der entsprechenden Anwendungen. Schädliche Anwendungen können das Verhalten anderer Anwendungen stören."</string>
+    <string name="permlab_removeTasks" msgid="4802740047161700683">"Aktive Apps beenden"</string>
+    <string name="permdesc_removeTasks" msgid="2000332928514575461">"Ermöglicht einer App das Entfernen von Aufgaben und Beenden der entsprechenden Apps. Schädliche Apps können das Verhalten anderer Apps stören."</string>
     <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>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 373f76c..2f1a1ad 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -466,7 +466,7 @@
     <string name="permlab_sdcardWrite" product="default" msgid="8079403759001777291">"modificar/eliminar contenido de la tarjeta SD"</string>
     <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6594393334785738252">"Permite escribir en USB"</string>
     <string name="permdesc_sdcardWrite" product="default" msgid="6643963204976471878">"Permite que una aplicación escriba en la tarjeta SD."</string>
-    <string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"Cambiar/borrar almac interno"</string>
+    <string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modificar o eliminar el contenido del almacenamiento de medios interno"</string>
     <string name="permdesc_mediaStorageWrite" product="default" msgid="8232008512478316233">"Permite que una aplicación modifique el contenido del almacenamiento interno de medios."</string>
     <string name="permlab_cache_filesystem" msgid="5656487264819669824">"acceder al sistema de archivos almacenado en caché"</string>
     <string name="permdesc_cache_filesystem" msgid="1624734528435659906">"Permite que una aplicación lea y escriba el sistema de archivos almacenado en caché."</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 1f0a94c..f112c33 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -204,7 +204,7 @@
     <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>
-    <string name="permlab_removeTasks" msgid="4802740047161700683">"berhenti menjalankan aplikasi"</string>
+    <string name="permlab_removeTasks" msgid="4802740047161700683">"menghentikan aplikasi yang berjalan"</string>
     <string name="permdesc_removeTasks" msgid="2000332928514575461">"Memungkinkan aplikasi menghapus tugas dan menghentikan aplikasinya. Aplikasi jahat dapat mengganggu perilaku aplikasi lain."</string>
     <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>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 4d2b6c35..4d36eb3 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -204,8 +204,8 @@
     <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>
-    <string name="permlab_removeTasks" msgid="4802740047161700683">"opriţi aplicaţiile care rulează"</string>
-    <string name="permdesc_removeTasks" msgid="2000332928514575461">"Permite unei aplicaţii să elimine sarcinile şi să închidă aplicaţiile corespunzătoare acestora. Aplicaţiile rău intenţionate pot perturba comportamentul altor aplicaţii."</string>
+    <string name="permlab_removeTasks" msgid="4802740047161700683">"oprirea aplicaţiilor care rulează"</string>
+    <string name="permdesc_removeTasks" msgid="2000332928514575461">"Permite unei aplicaţii să elimine sarcini şi să închidă aplicaţiile corespunzătoare acestora. Aplicaţiile rău intenţionate pot perturba comportamentul altor aplicaţii."</string>
     <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-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 6155536..a81b2ee 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -205,7 +205,7 @@
     <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>
     <string name="permlab_removeTasks" msgid="4802740047161700683">"zastavenie činnosti aplikácií"</string>
-    <string name="permdesc_removeTasks" msgid="2000332928514575461">"Umožňuje aplikácii odstraňovať úlohy a ukončiť čínnosť súvisiacich aplikácií. Škodlivé aplikácie môžu narušovať správanie iných aplikácií."</string>
+    <string name="permdesc_removeTasks" msgid="2000332928514575461">"Umožňuje aplikácii odstraňovať úlohy a ukončiť činnosť súvisiacich aplikácií. Škodlivé aplikácie môžu narušovať správanie iných aplikácií."</string>
     <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-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 9c234a7..545be31 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -205,7 +205,7 @@
     <string name="permlab_reorderTasks" msgid="5669588525059921549">"重新安排執行中的應用程式"</string>
     <string name="permdesc_reorderTasks" msgid="126252774270522835">"允許應用程式將工作移至前端或背景作業。請注意:惡意程式可能使用此功能自行把自己拉到前端。"</string>
     <string name="permlab_removeTasks" msgid="4802740047161700683">"停止執行中的應用程式"</string>
-    <string name="permdesc_removeTasks" msgid="2000332928514575461">"允許應用程式移除工作並且關閉應用程式。惡意應用程式會干擾其他應用程式的行為。"</string>
+    <string name="permdesc_removeTasks" msgid="2000332928514575461">"允許應用程式移除工作並且關閉應用程式。惡意應用程式會干擾其他應用程式的運行。"</string>
     <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/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
index d22356d..27363e8 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
@@ -30,6 +30,7 @@
 import android.net.wifi.WifiConfiguration.ProxySettings;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
+import android.net.RouteInfo;
 import android.util.Log;
 
 import java.io.InputStream;
@@ -301,7 +302,7 @@
                     if (!InetAddress.isNumeric(gwAddr)) {
                         throw new SAXException();
                     }
-                    mLinkProperties.addGateway(InetAddress.getByName(gwAddr));
+                    mLinkProperties.addRoute(new RouteInfo(InetAddress.getByName(gwAddr)));
                 } catch (UnknownHostException e) {
                     throw new SAXException();
                 }
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 010e3c3..fadf1ec 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -421,6 +421,13 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="android.widget.scroll.arrowscroll.MultiPageTextWithPadding" android:label="arrowscrollMultiPageTextWithPadding">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+        </activity>
+
         <activity android:name="android.view.Include" android:label="IncludeTag">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
index 50666b4..e3b6b5f 100644
--- a/core/tests/coretests/src/android/net/LinkPropertiesTest.java
+++ b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.net.LinkProperties;
+import android.net.RouteInfo;
 import android.test.suitebuilder.annotation.SmallTest;
 import junit.framework.TestCase;
 
@@ -55,8 +56,8 @@
             source.addDns(NetworkUtils.numericToInetAddress(DNS1));
             source.addDns(NetworkUtils.numericToInetAddress(DNS2));
             // set 2 gateways
-            source.addGateway(NetworkUtils.numericToInetAddress(GATEWAY1));
-            source.addGateway(NetworkUtils.numericToInetAddress(GATEWAY2));
+            source.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+            source.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
 
             LinkProperties target = new LinkProperties();
 
@@ -68,8 +69,8 @@
                     NetworkUtils.numericToInetAddress(ADDRV6), 128));
             target.addDns(NetworkUtils.numericToInetAddress(DNS1));
             target.addDns(NetworkUtils.numericToInetAddress(DNS2));
-            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY1));
-            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY2));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
 
             assertTrue(source.equals(target));
             assertTrue(source.hashCode() == target.hashCode());
@@ -83,8 +84,8 @@
                     NetworkUtils.numericToInetAddress(ADDRV6), 128));
             target.addDns(NetworkUtils.numericToInetAddress(DNS1));
             target.addDns(NetworkUtils.numericToInetAddress(DNS2));
-            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY1));
-            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY2));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
             assertFalse(source.equals(target));
 
             target.clear();
@@ -96,8 +97,8 @@
                     NetworkUtils.numericToInetAddress(ADDRV6), 128));
             target.addDns(NetworkUtils.numericToInetAddress(DNS1));
             target.addDns(NetworkUtils.numericToInetAddress(DNS2));
-            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY1));
-            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY2));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
             assertFalse(source.equals(target));
 
             target.clear();
@@ -109,8 +110,8 @@
             // change dnses
             target.addDns(NetworkUtils.numericToInetAddress("75.208.7.2"));
             target.addDns(NetworkUtils.numericToInetAddress(DNS2));
-            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY1));
-            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY2));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
             assertFalse(source.equals(target));
 
             target.clear();
@@ -122,8 +123,8 @@
             target.addDns(NetworkUtils.numericToInetAddress(DNS1));
             target.addDns(NetworkUtils.numericToInetAddress(DNS2));
             // change gateway
-            target.addGateway(NetworkUtils.numericToInetAddress("75.208.8.2"));
-            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY2));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress("75.208.8.2")));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
             assertFalse(source.equals(target));
 
         } catch (Exception e) {
@@ -146,8 +147,8 @@
             source.addDns(NetworkUtils.numericToInetAddress(DNS1));
             source.addDns(NetworkUtils.numericToInetAddress(DNS2));
             // set 2 gateways
-            source.addGateway(NetworkUtils.numericToInetAddress(GATEWAY1));
-            source.addGateway(NetworkUtils.numericToInetAddress(GATEWAY2));
+            source.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
+            source.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
 
             LinkProperties target = new LinkProperties();
             // Exchange order
@@ -158,8 +159,8 @@
                     NetworkUtils.numericToInetAddress(ADDRV4), 32));
             target.addDns(NetworkUtils.numericToInetAddress(DNS2));
             target.addDns(NetworkUtils.numericToInetAddress(DNS1));
-            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY2));
-            target.addGateway(NetworkUtils.numericToInetAddress(GATEWAY1));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2)));
+            target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1)));
 
             assertTrue(source.equals(target));
             assertTrue(source.hashCode() == target.hashCode());
diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java
index c82962d..d494c5d 100644
--- a/core/tests/coretests/src/android/text/TextUtilsTest.java
+++ b/core/tests/coretests/src/android/text/TextUtilsTest.java
@@ -19,6 +19,7 @@
 import com.google.android.collect.Lists;
 
 import android.test.MoreAsserts;
+import android.os.Parcel;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.text.style.StyleSpan;
@@ -344,6 +345,51 @@
         assertFalse(TextUtils.delimitedStringContains("network,mock,gpsx", ',', "gps"));
     }
 
+    @SmallTest
+    public void testCharSequenceCreator() {
+        Parcel p = Parcel.obtain();
+        TextUtils.writeToParcel(null, p, 0);
+        CharSequence text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p);
+        assertNull("null CharSequence should generate null from parcel", text);
+        p = Parcel.obtain();
+        TextUtils.writeToParcel("test", p, 0);
+        text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p);
+        assertEquals("conversion to/from parcel failed", "test", text);
+    }
+
+    @SmallTest
+    public void testCharSequenceCreatorNull() {
+        Parcel p;
+        CharSequence text;
+        p = Parcel.obtain();
+        TextUtils.writeToParcel(null, p, 0);
+        p.setDataPosition(0);
+        text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p);
+        assertNull("null CharSequence should generate null from parcel", text);
+    }
+
+    @SmallTest
+    public void testCharSequenceCreatorSpannable() {
+        Parcel p;
+        CharSequence text;
+        p = Parcel.obtain();
+        TextUtils.writeToParcel(new SpannableString("test"), p, 0);
+        p.setDataPosition(0);
+        text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p);
+        assertEquals("conversion to/from parcel failed", "test", text.toString());
+    }
+
+    @SmallTest
+    public void testCharSequenceCreatorString() {
+        Parcel p;
+        CharSequence text;
+        p = Parcel.obtain();
+        TextUtils.writeToParcel("test", p, 0);
+        p.setDataPosition(0);
+        text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p);
+        assertEquals("conversion to/from parcel failed", "test", text.toString());
+    }
+
     /**
      * CharSequence wrapper for testing the cases where text is copied into
      * a char array instead of working from a String or a Spanned.
diff --git a/core/tests/coretests/src/android/util/ScrollViewScenario.java b/core/tests/coretests/src/android/util/ScrollViewScenario.java
index 83afe06..db3d9d0 100644
--- a/core/tests/coretests/src/android/util/ScrollViewScenario.java
+++ b/core/tests/coretests/src/android/util/ScrollViewScenario.java
@@ -61,6 +61,7 @@
 
     /**
      * Partially implement ViewFactory given a height ratio.
+     * A negative height ratio means that WRAP_CONTENT will be used as height
      */
     private static abstract class ViewFactoryBase implements ViewFactory {
 
@@ -87,6 +88,9 @@
 
         List<ViewFactory> mViewFactories = Lists.newArrayList();
 
+        int mTopPadding = 0;
+        int mBottomPadding = 0;
+
         /**
          * Add a text view.
          * @param text The text of the text view.
@@ -186,6 +190,13 @@
             });
             return this;
         }
+
+        public Params addPaddingToScrollView(int topPadding, int bottomPadding) {
+            mTopPadding = topPadding;
+            mBottomPadding = bottomPadding;
+
+            return this;
+        }
     }
 
     /**
@@ -239,13 +250,17 @@
 
         // create views specified by params
         for (ViewFactory viewFactory : params.mViewFactories) {
+            int height = ViewGroup.LayoutParams.WRAP_CONTENT;
+            if (viewFactory.getHeightRatio() >= 0) {
+                height = (int) (viewFactory.getHeightRatio() * screenHeight);
+            }
             final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
-                    ViewGroup.LayoutParams.MATCH_PARENT,
-                    (int) (viewFactory.getHeightRatio() * screenHeight));
+                    ViewGroup.LayoutParams.MATCH_PARENT, height);
             mLinearLayout.addView(viewFactory.create(this), lp);
         }
 
         mScrollView = createScrollView();
+        mScrollView.setPadding(0, params.mTopPadding, 0, params.mBottomPadding);
         mScrollView.addView(mLinearLayout, new ViewGroup.LayoutParams(
                 ViewGroup.LayoutParams.MATCH_PARENT,
                 ViewGroup.LayoutParams.MATCH_PARENT));
diff --git a/core/tests/coretests/src/android/widget/scroll/arrowscroll/MultiPageTextWithPadding.java b/core/tests/coretests/src/android/widget/scroll/arrowscroll/MultiPageTextWithPadding.java
new file mode 100644
index 0000000..7d5a8d8
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/scroll/arrowscroll/MultiPageTextWithPadding.java
@@ -0,0 +1,38 @@
+/*
+ * 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.widget.scroll.arrowscroll;
+
+import android.util.ScrollViewScenario;
+
+/**
+ * One TextView with a text covering several pages. Padding is added
+ * above and below the ScrollView.
+ */
+public class MultiPageTextWithPadding extends ScrollViewScenario {
+
+    @Override
+    protected void init(Params params) {
+
+        String text = "This is a long text.";
+        String longText = "First text.";
+        for (int i = 0; i < 300; i++) {
+            longText = longText + " " + text;
+        }
+        longText = longText + " Last text.";
+        params.addTextView(longText, -1.0f).addPaddingToScrollView(50, 50);
+    }
+}
diff --git a/core/tests/coretests/src/android/widget/scroll/arrowscroll/MultiPageTextWithPaddingTest.java b/core/tests/coretests/src/android/widget/scroll/arrowscroll/MultiPageTextWithPaddingTest.java
new file mode 100644
index 0000000..ddde48f
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/scroll/arrowscroll/MultiPageTextWithPaddingTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2011 Sony Ericsson Mobile Communications AB.
+ *
+ * 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.widget.scroll.arrowscroll;
+
+import android.widget.scroll.arrowscroll.MultiPageTextWithPadding;
+import android.test.ActivityInstrumentationTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.view.KeyEvent;
+import android.widget.TextView;
+import android.widget.ScrollView;
+
+public class MultiPageTextWithPaddingTest extends
+        ActivityInstrumentationTestCase<MultiPageTextWithPadding> {
+
+    private ScrollView mScrollView;
+
+    private TextView mTextView;
+
+    public MultiPageTextWithPaddingTest() {
+        super("com.android.frameworks.coretests", MultiPageTextWithPadding.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mScrollView = getActivity().getScrollView();
+        mTextView = getActivity().getContentChildAt(0);
+    }
+
+    @MediumTest
+    public void testPreconditions() {
+        assertTrue("text should not fit on screen",
+                   mTextView.getHeight() > mScrollView.getHeight());
+    }
+
+    @LargeTest
+    public void testScrollDownToBottom() throws Exception {
+        // Calculate the number of arrow scrolls needed to reach the bottom
+        int scrollsNeeded = (int)Math.ceil(Math.max(0.0f,
+                (mTextView.getHeight() - mScrollView.getHeight()))
+                / mScrollView.getMaxScrollAmount());
+        for (int i = 0; i < scrollsNeeded; i++) {
+            sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        }
+
+        assertEquals(
+                "should be fully scrolled to bottom",
+                getActivity().getLinearLayout().getHeight()
+                        - (mScrollView.getHeight() - mScrollView.getPaddingTop() - mScrollView
+                                .getPaddingBottom()), mScrollView.getScrollY());
+    }
+}
diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd
index 10d25bb..2a551e9 100644
--- a/docs/html/guide/topics/resources/providing-resources.jd
+++ b/docs/html/guide/topics/resources/providing-resources.jd
@@ -264,8 +264,8 @@
 names.</p>
 <table>
     <tr>
-        <th>Qualifier</th>
-        <th>Values</th>
+        <th>Configuration</th>
+        <th>Qualifier Values</th>
         <th>Description</th>
     </tr>
     <tr id="MccQualifier">
diff --git a/graphics/java/android/graphics/Movie.java b/graphics/java/android/graphics/Movie.java
index 95e9946..4a33453 100644
--- a/graphics/java/android/graphics/Movie.java
+++ b/graphics/java/android/graphics/Movie.java
@@ -46,6 +46,8 @@
     public static native Movie decodeByteArray(byte[] data, int offset,
                                                int length);
 
+    private static native void nativeDestructor(int nativeMovie);
+
     public static Movie decodeFile(String pathName) {
         InputStream is;
         try {
@@ -57,6 +59,15 @@
         return decodeTempStream(is);
     }
 
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            nativeDestructor(mNativeMovie);
+        } finally {
+            super.finalize();
+        }
+    }
+
     private static Movie decodeTempStream(InputStream is) {
         Movie moov = null;
         try {
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index cfae0c1..3c43a39 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -34,8 +34,9 @@
  * the stream to be skipped.
  *
  * <p>When sampling from the texture one should first transform the texture coordinates using the
- * matrix queried via {@link #getTransformMatrix}.  The transform matrix may change each time {@link
- * #updateTexImage} is called, so it should be re-queried each time the texture image is updated.
+ * matrix queried via {@link #getTransformMatrix(float[])}.  The transform matrix may change each
+ * time {@link #updateTexImage} is called, so it should be re-queried each time the texture image
+ * is updated.
  * This matrix transforms traditional 2D OpenGL ES texture coordinate column vectors of the form (s,
  * t, 0, 1) where s and t are on the inclusive interval [0, 1] to the proper sampling location in
  * the streamed texture.  This transform compensates for any properties of the image stream source
@@ -63,8 +64,13 @@
     private EventHandler mEventHandler;
     private OnFrameAvailableListener mOnFrameAvailableListener;
 
-    @SuppressWarnings("unused")
-    private int mSurfaceTexture;
+    /**
+     * This field is used by native code, do not access or modify.
+     * 
+     * @hide
+     */
+    @SuppressWarnings({"UnusedDeclaration"})
+    public int mSurfaceTexture;
 
     /**
      * Callback interface for being notified that a new stream frame is available.
@@ -176,10 +182,13 @@
             if (mOnFrameAvailableListener != null) {
                 mOnFrameAvailableListener.onFrameAvailable(SurfaceTexture.this);
             }
-            return;
         }
     }
 
+    /**
+     * This method is invoked from native code only.
+     */
+    @SuppressWarnings({"UnusedDeclaration"})
     private static void postEventFromNative(Object selfRef) {
         WeakReference weakSelf = (WeakReference)selfRef;
         SurfaceTexture st = (SurfaceTexture)weakSelf.get();
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index a278466..c9c9fd7 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -546,10 +546,8 @@
         mBitmapState = state;
         if (res != null) {
             mTargetDensity = res.getDisplayMetrics().densityDpi;
-        } else if (state != null) {
-            mTargetDensity = state.mTargetDensity;
         } else {
-            mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
+            mTargetDensity = state.mTargetDensity;
         }
         setBitmap(state != null ? state.mBitmap : null);
     }
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index 3f6f8c3..256435f 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -381,7 +381,7 @@
     LOG_API("nTypeCreate, con(%p) eid(%p), x(%i), y(%i), z(%i), mips(%i), faces(%i)",
             con, eid, dimx, dimy, dimz, mips, faces);
 
-    jint id = (jint)rsaTypeCreate(con, (RsElement)eid, dimx, dimy, dimz, mips, faces);
+    jint id = (jint)rsTypeCreate(con, (RsElement)eid, dimx, dimy, dimz, mips, faces);
     return (jint)id;
 }
 
@@ -409,7 +409,7 @@
 nAllocationCreateTyped(JNIEnv *_env, jobject _this, RsContext con, jint type, jint mips, jint usage)
 {
     LOG_API("nAllocationCreateTyped, con(%p), type(%p), mip(%i), usage(%i)", con, (RsElement)type, mips, usage);
-    return (jint) rsaAllocationCreateTyped(con, (RsType)type, (RsAllocationMipmapControl)mips, (uint32_t)usage);
+    return (jint) rsAllocationCreateTyped(con, (RsType)type, (RsAllocationMipmapControl)mips, (uint32_t)usage);
 }
 
 static void
@@ -435,7 +435,7 @@
 
     bitmap.lockPixels();
     const void* ptr = bitmap.getPixels();
-    jint id = (jint)rsaAllocationCreateFromBitmap(con,
+    jint id = (jint)rsAllocationCreateFromBitmap(con,
                                                   (RsType)type, (RsAllocationMipmapControl)mip,
                                                   ptr, bitmap.getSize(), usage);
     bitmap.unlockPixels();
@@ -451,7 +451,7 @@
 
     bitmap.lockPixels();
     const void* ptr = bitmap.getPixels();
-    jint id = (jint)rsaAllocationCubeCreateFromBitmap(con,
+    jint id = (jint)rsAllocationCubeCreateFromBitmap(con,
                                                       (RsType)type, (RsAllocationMipmapControl)mip,
                                                       ptr, bitmap.getSize(), usage);
     bitmap.unlockPixels();
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index db81721..513239f 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -309,6 +309,25 @@
     // 0.3333, EV is -2.
     // Example value: "0.333333333" or "0.5". Read only.
     static const char KEY_EXPOSURE_COMPENSATION_STEP[];
+    // The state of the auto-exposure lock. "true" means that auto-exposure is
+    // locked to its current value and will not change. "false" means the
+    // auto-exposure routine is free to change exposure settings. Changing
+    // exposure compensation settings will still affect the exposure settings
+    // while auto-exposure is locked. Stopping preview or taking a still image
+    // will release the lock. However, the lock can be re-enabled prior to
+    // preview being re-started, to keep the exposure values from the previous
+    // lock. In conjunction with exposure compensation, this allows for
+    // capturing multi-exposure brackets with known relative exposure
+    // values. Locking auto-exposure after open but before the first cal to
+    // startPreview may result in severly over- or under-exposed images.  The
+    // driver may independently enable the AE lock after auto-focus
+    // completes. If it does so, this key must have its value updated to reflect
+    // the lock's existence. Applications are free to release such a lock, to
+    // re-enable AE without restarting preview.
+    static const char KEY_AUTO_EXPOSURE_LOCK[];
+    // Whether locking the auto-exposure is supported. "true" means it is, and
+    // "false" or this key not existing means it is not supported.
+    static const char KEY_AUTO_EXPOSURE_LOCK_SUPPORTED[];
     // The maximum number of metering areas supported. This is the maximum
     // length of KEY_METERING_AREAS.
     // Example value: "0" or "2". Read only.
@@ -428,6 +447,7 @@
 
     // Value for KEY_ZOOM_SUPPORTED or KEY_SMOOTH_ZOOM_SUPPORTED.
     static const char TRUE[];
+    static const char FALSE[];
 
     // Value for KEY_FOCUS_DISTANCES.
     static const char FOCUS_DISTANCE_INFINITY[];
diff --git a/include/drm/drm_framework_common.h b/include/drm/drm_framework_common.h
index d2d1d7e..3330ebc 100644
--- a/include/drm/drm_framework_common.h
+++ b/include/drm/drm_framework_common.h
@@ -31,14 +31,17 @@
  * Error code for DRM Frameowrk
  */
 enum {
-    DRM_ERROR_BASE = -2000,
+    // The following constant values should be in sync with
+    // media/stagefright/MediaErrors.h
+    ERROR_BASE = -2000,
 
-    DRM_ERROR_UNKNOWN                       = DRM_ERROR_BASE,
-    DRM_ERROR_LICENSE_EXPIRED               = DRM_ERROR_BASE - 1,
-    DRM_ERROR_SESSION_NOT_OPENED            = DRM_ERROR_BASE - 2,
-    DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED  = DRM_ERROR_BASE - 3,
-    DRM_ERROR_DECRYPT                       = DRM_ERROR_BASE - 4,
-    DRM_ERROR_CANNOT_HANDLE                 = DRM_ERROR_BASE - 5,
+    DRM_ERROR_UNKNOWN                       = ERROR_BASE,
+    DRM_ERROR_NO_LICENSE                    = ERROR_BASE - 1,
+    DRM_ERROR_LICENSE_EXPIRED               = ERROR_BASE - 2,
+    DRM_ERROR_SESSION_NOT_OPENED            = ERROR_BASE - 3,
+    DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED  = ERROR_BASE - 4,
+    DRM_ERROR_DECRYPT                       = ERROR_BASE - 5,
+    DRM_ERROR_CANNOT_HANDLE                 = ERROR_BASE - 6,
 
     DRM_NO_ERROR                            = NO_ERROR
 };
diff --git a/include/media/IMediaPlayer.h b/include/media/IMediaPlayer.h
index 9b1af6b..d552b2e 100644
--- a/include/media/IMediaPlayer.h
+++ b/include/media/IMediaPlayer.h
@@ -51,6 +51,8 @@
     virtual status_t        setVolume(float leftVolume, float rightVolume) = 0;
     virtual status_t        setAuxEffectSendLevel(float level) = 0;
     virtual status_t        attachAuxEffect(int effectId) = 0;
+    virtual status_t        setParameter(int key, const Parcel& request) = 0;
+    virtual status_t        getParameter(int key, Parcel* reply) = 0;
 
     // Invoke a generic method on the player by using opaque parcels
     // for the request and reply.
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index bebecc0..f0401cca 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -132,6 +132,8 @@
     virtual status_t    reset() = 0;
     virtual status_t    setLooping(int loop) = 0;
     virtual player_type playerType() = 0;
+    virtual status_t    setParameter(int key, const Parcel &request) = 0;
+    virtual status_t    getParameter(int key, Parcel *reply) = 0;
 
     // Invoke a generic method on the player by using opaque parcels
     // for the request and reply.
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index 748e489..241626c 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -177,6 +177,9 @@
             int             getAudioSessionId();
             status_t        setAuxEffectSendLevel(float level);
             status_t        attachAuxEffect(int effectId);
+            status_t        setParameter(int key, const Parcel& request);
+            status_t        getParameter(int key, Parcel* reply);
+
 private:
             void            clear_l();
             status_t        seekTo_l(int msec);
diff --git a/include/media/stagefright/MediaErrors.h b/include/media/stagefright/MediaErrors.h
index 1a6d548..7cc993c 100644
--- a/include/media/stagefright/MediaErrors.h
+++ b/include/media/stagefright/MediaErrors.h
@@ -41,7 +41,17 @@
     INFO_FORMAT_CHANGED    = MEDIA_ERROR_BASE - 12,
     INFO_DISCONTINUITY     = MEDIA_ERROR_BASE - 13,
 
-    ERROR_NO_LICENSE       = MEDIA_ERROR_BASE - 14,
+    // The following constant values should be in sync with
+    // drm/drm_framework_common.h
+    DRM_ERROR_BASE = -2000,
+
+    ERROR_DRM_UNKNOWN                       = DRM_ERROR_BASE,
+    ERROR_DRM_NO_LICENSE                    = DRM_ERROR_BASE - 1,
+    ERROR_DRM_LICENSE_EXPIRED               = DRM_ERROR_BASE - 2,
+    ERROR_DRM_SESSION_NOT_OPENED            = DRM_ERROR_BASE - 3,
+    ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED  = DRM_ERROR_BASE - 4,
+    ERROR_DRM_DECRYPT                       = DRM_ERROR_BASE - 5,
+    ERROR_DRM_CANNOT_HANDLE                 = DRM_ERROR_BASE - 6,
 
     // Heartbeat Error Codes
     HEARTBEAT_ERROR_BASE = -3000,
diff --git a/include/private/opengles/gl_context.h b/include/private/opengles/gl_context.h
index 72416c1..6b1fa77 100644
--- a/include/private/opengles/gl_context.h
+++ b/include/private/opengles/gl_context.h
@@ -31,8 +31,6 @@
 #include <GLES/gl.h>
 #include <GLES/glext.h>
 
-struct android_native_buffer_t;
-
 namespace android {
 
 
@@ -603,13 +601,6 @@
     void (*renderTriangle)(GL, vertex_t*, vertex_t*, vertex_t*);
 };
 
-struct copybits_context_t {
-    // A handle to the blit engine, if it exists, else NULL.
-    int32_t                 minScale;
-    int32_t                 maxScale;
-    android_native_buffer_t* drawSurfaceBuffer;
-};
-
 struct ogles_context_t {
     context_t               rasterizer;
     array_machine_t         arrays         __attribute__((aligned(32)));
@@ -634,13 +625,6 @@
     EGLSurfaceManager*      surfaceManager;
     EGLBufferObjectManager* bufferObjectManager;
 
-    // copybits is only used if LIBAGL_USE_GRALLOC_COPYBITS is
-    // defined, but it is always present because ogles_context_t is a public
-    // struct that is used by clients of libagl. We want the size and offsets
-    // to stay the same, whether or not LIBAGL_USE_GRALLOC_COPYBITS is defined.
-
-    copybits_context_t      copybits;
-
     GLenum                  error;
 
     static inline ogles_context_t* get() {
diff --git a/libs/camera/CameraParameters.cpp b/libs/camera/CameraParameters.cpp
index 214cd4d..4f3da40 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_AUTO_EXPOSURE_LOCK[] = "auto-exposure-lock";
+const char CameraParameters::KEY_AUTO_EXPOSURE_LOCK_SUPPORTED[] = "auto-exposure-lock-supported";
 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";
@@ -82,6 +84,7 @@
 const char CameraParameters::KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO[] = "preferred-preview-size-for-video";
 
 const char CameraParameters::TRUE[] = "true";
+const char CameraParameters::FALSE[] = "false";
 const char CameraParameters::FOCUS_DISTANCE_INFINITY[] = "Infinity";
 
 // Values for white balance settings.
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index faecadd..596781e 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -62,8 +62,10 @@
 static const GLsizei gMeshStride = sizeof(TextureVertex);
 static const GLsizei gVertexStride = sizeof(Vertex);
 static const GLsizei gAlphaVertexStride = sizeof(AlphaVertex);
+static const GLsizei gAAVertexStride = sizeof(AAVertex);
 static const GLsizei gMeshTextureOffset = 2 * sizeof(float);
-static const GLsizei gVertexAlphaOffset = 2 * sizeof(float);
+static const GLsizei gVertexAAWidthOffset = 2 * sizeof(float);
+static const GLsizei gVertexAALengthOffset = 3 * sizeof(float);
 static const GLsizei gMeshCount = 4;
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 16566b8..0310bc3 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -47,6 +47,7 @@
         meshElementCount = 0;
         isCacheable = true;
         isTextureLayer = false;
+        renderTarget = GL_TEXTURE_2D;
     }
 
     ~Layer() {
@@ -155,6 +156,11 @@
      * Optional texture coordinates transform.
      */
     mat4 texTransform;
+
+    /**
+     * Indicates the render target.
+     */
+    GLenum renderTarget;
 }; // struct Layer
 
 }; // namespace uirenderer
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index e167336..f316ba7 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -185,17 +185,7 @@
     layer->region.clear();
 
     glActiveTexture(GL_TEXTURE0);
-
     glGenTextures(1, &layer->texture);
-    glBindTexture(GL_TEXTURE_EXTERNAL_OES, layer->texture);
-
-    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
-
-    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
-    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 
     return layer;
 }
@@ -280,7 +270,7 @@
 }
 
 void LayerRenderer::updateTextureLayer(Layer* layer, uint32_t width, uint32_t height,
-        float* transform) {
+        GLenum renderTarget, float* transform) {
     if (layer) {
         layer->width = width;
         layer->height = height;
@@ -288,6 +278,15 @@
         layer->region.set(width, height);
         layer->regionRect.set(0.0f, 0.0f, width, height);
         layer->texTransform.load(transform);
+        layer->renderTarget = renderTarget;
+
+        glBindTexture(layer->renderTarget, layer->texture);
+
+        glTexParameteri(layer->renderTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        glTexParameteri(layer->renderTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+        glTexParameteri(layer->renderTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        glTexParameteri(layer->renderTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
     }
 }
 
diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h
index b3cd5db..59cab96 100644
--- a/libs/hwui/LayerRenderer.h
+++ b/libs/hwui/LayerRenderer.h
@@ -57,7 +57,7 @@
     static Layer* createLayer(uint32_t width, uint32_t height, bool isOpaque = false);
     static bool resizeLayer(Layer* layer, uint32_t width, uint32_t height);
     static void updateTextureLayer(Layer* layer, uint32_t width, uint32_t height,
-            float* transform);
+            GLenum renderTarget, float* transform);
     static void destroyLayer(Layer* layer);
     static void destroyLayerDeferred(Layer* layer);
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 34d8fd3..6f751e8 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -637,7 +637,12 @@
     float alpha = layer->alpha / 255.0f;
 
     setupDraw();
-    setupDrawWithExternalTexture();
+    if (layer->renderTarget == GL_TEXTURE_2D) {
+        setupDrawWithTexture();
+    } else {
+        setupDrawWithExternalTexture();
+    }
+    setupDrawTextureTransform();
     setupDrawColor(alpha, alpha, alpha, alpha);
     setupDrawColorFilter();
     setupDrawBlending(layer->blend, layer->mode);
@@ -645,8 +650,12 @@
     setupDrawModelView(rect.left, rect.top, rect.right, rect.bottom);
     setupDrawPureColorUniforms();
     setupDrawColorFilterUniforms();
-    setupDrawExternalTexture(layer->texture);
-    setupDrawTextureTransform(layer->texTransform);
+    if (layer->renderTarget == GL_TEXTURE_2D) {
+        setupDrawTexture(layer->texture);
+    } else {
+        setupDrawExternalTexture(layer->texture);
+    }
+    setupDrawTextureTransformUniforms(layer->texTransform);
     setupDrawMesh(&mMeshVertices[0].position[0], &mMeshVertices[0].texture[0]);
 
     glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
@@ -915,7 +924,7 @@
 }
 
 void OpenGLRenderer::setupDrawAALine() {
-    mDescription.hasWidth = true;
+    mDescription.isAA = true;
 }
 
 void OpenGLRenderer::setupDrawPoint(float pointSize) {
@@ -1095,7 +1104,11 @@
     glEnableVertexAttribArray(mTexCoordsSlot);
 }
 
-void OpenGLRenderer::setupDrawTextureTransform(mat4& transform) {
+void OpenGLRenderer::setupDrawTextureTransform() {
+    mDescription.hasTextureTransform = true;
+}
+
+void OpenGLRenderer::setupDrawTextureTransformUniforms(mat4& transform) {
     glUniformMatrix4fv(mCaches.currentProgram->getUniform("mainTextureTransform"), 1,
             GL_FALSE, &transform.data[0]);
 }
@@ -1121,25 +1134,30 @@
 
 /**
  * Sets up the shader to draw an AA line. We draw AA lines with quads, where there is an
- * outer boundary that fades out to 0. The variables set in the shader define the width of the
- * core line primitive ("width") and the width of the fading boundary ("boundaryWidth"). The
- * "vtxDistance" attribute (one per vertex) is a value from zero to one that tells the fragment
- * shader where the fragment is in relation to the line width overall; this value is then used
- * to compute the proper color, based on whether the fragment lies in the fading AA region of
- * the line.
+ * outer boundary that fades out to 0. The variables set in the shader define the proportion of
+ * the width and length of the primitive occupied by the AA region. The vtxWidth and vtxLength
+ * attributes (one per vertex) are values from zero to one that tells the fragment
+ * shader where the fragment is in relation to the line width/length overall; these values are
+ * then used to compute the proper color, based on whether the fragment lies in the fading AA
+ * region of the line.
+ * Note that we only pass down the width values in this setup function. The length coordinates
+ * are set up for each individual segment.
  */
-void OpenGLRenderer::setupDrawAALine(GLvoid* vertices, GLvoid* distanceCoords, float strokeWidth) {
+void OpenGLRenderer::setupDrawAALine(GLvoid* vertices, GLvoid* widthCoords,
+        GLvoid* lengthCoords, float strokeWidth) {
     mCaches.unbindMeshBuffer();
     glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
-            gAlphaVertexStride, vertices);
-    int distanceSlot = mCaches.currentProgram->getAttrib("vtxDistance");
-    glEnableVertexAttribArray(distanceSlot);
-    glVertexAttribPointer(distanceSlot, 1, GL_FLOAT, GL_FALSE, gAlphaVertexStride, distanceCoords);
-    int widthSlot = mCaches.currentProgram->getUniform("width");
+            gAAVertexStride, vertices);
+    int widthSlot = mCaches.currentProgram->getAttrib("vtxWidth");
+    glEnableVertexAttribArray(widthSlot);
+    glVertexAttribPointer(widthSlot, 1, GL_FLOAT, GL_FALSE, gAAVertexStride, widthCoords);
+    int lengthSlot = mCaches.currentProgram->getAttrib("vtxLength");
+    glEnableVertexAttribArray(lengthSlot);
+    glVertexAttribPointer(lengthSlot, 1, GL_FLOAT, GL_FALSE, gAAVertexStride, lengthCoords);
     int boundaryWidthSlot = mCaches.currentProgram->getUniform("boundaryWidth");
+    // Setting the inverse value saves computations per-fragment in the shader
     int inverseBoundaryWidthSlot = mCaches.currentProgram->getUniform("inverseBoundaryWidth");
     float boundaryWidth = (1 - strokeWidth) / 2;
-    glUniform1f(widthSlot, strokeWidth);
     glUniform1f(boundaryWidthSlot, boundaryWidth);
     glUniform1f(inverseBoundaryWidthSlot, (1 / boundaryWidth));
 }
@@ -1480,20 +1498,21 @@
     }
     Vertex lines[verticesCount];
     Vertex* vertices = &lines[0];
-    AlphaVertex wLines[verticesCount];
-    AlphaVertex* aaVertices = &wLines[0];
+    AAVertex wLines[verticesCount];
+    AAVertex* aaVertices = &wLines[0];
     if (!isAA) {
         setupDrawVertices(vertices);
     } else {
-        void* alphaCoords = ((GLbyte*) aaVertices) + gVertexAlphaOffset;
+        void* widthCoords = ((GLbyte*) aaVertices) + gVertexAAWidthOffset;
+        void* lengthCoords = ((GLbyte*) aaVertices) + gVertexAALengthOffset;
         // innerProportion is the ratio of the inner (non-AA) port of the line to the total
         // AA stroke width (the base stroke width expanded by a half pixel on either side).
         // This value is used in the fragment shader to determine how to fill fragments.
         float innerProportion = fmax(strokeWidth - 1.0f, 0) / (strokeWidth + .5f);
-        setupDrawAALine((void*) aaVertices, alphaCoords, innerProportion);
+        setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords, innerProportion);
     }
 
-    AlphaVertex *prevAAVertex = NULL;
+    AAVertex *prevAAVertex = NULL;
     Vertex *prevVertex = NULL;
     float inverseScaleX = 1.0f;
     float inverseScaleY = 1.0f;
@@ -1516,15 +1535,17 @@
         }
     }
 
+    int boundaryLengthSlot = -1;
+    int inverseBoundaryLengthSlot = -1;
     for (int i = 0; i < count; i += 4) {
         // a = start point, b = end point
         vec2 a(points[i], points[i + 1]);
         vec2 b(points[i + 2], points[i + 3]);
+        float length = 0;
 
         // Find the normal to the line
         vec2 n = (b - a).copyNormalized() * strokeWidth;
         if (isHairLine) {
-            n *= inverseScaleX;
             if (isAA) {
                 float wideningFactor;
                 if (fabs(n.x) >= fabs(n.y)) {
@@ -1534,27 +1555,35 @@
                 }
                 n *= wideningFactor;
             }
+            n.x *= inverseScaleX;
+            n.y *= inverseScaleY;
         }
         float x = n.x;
         n.x = -n.y;
         n.y = x;
 
+        // aa lines expand the endpoint vertices to encompass the AA boundary
+        if (isAA) {
+            vec2 abVector = (b - a);
+            length = abVector.length();
+            abVector.normalize();
+            a -= abVector;
+            b += abVector;
+        }
+
         // Four corners of the rectangle defining a thick line
         vec2 p1 = a - n;
         vec2 p2 = a + n;
         vec2 p3 = b + n;
         vec2 p4 = b - n;
 
+
         const float left = fmin(p1.x, fmin(p2.x, fmin(p3.x, p4.x)));
         const float right = fmax(p1.x, fmax(p2.x, fmax(p3.x, p4.x)));
         const float top = fmin(p1.y, fmin(p2.y, fmin(p3.y, p4.y)));
         const float bottom = fmax(p1.y, fmax(p2.y, fmax(p3.y, p4.y)));
 
         if (!quickReject(left, top, right, bottom)) {
-            // Draw the line as 2 triangles, could be optimized
-            // by using only 4 vertices and the correct indices
-            // Also we should probably used non textured vertices
-            // when line AA is disabled to save on bandwidth
             if (!isAA) {
                 if (prevVertex != NULL) {
                     // Issue two repeat vertices to create degenerate triangles to bridge
@@ -1572,24 +1601,36 @@
                 prevVertex = vertices - 1;
                 generatedVerticesCount += 4;
             } else {
+                if (boundaryLengthSlot < 0) {
+                    boundaryLengthSlot = mCaches.currentProgram->getUniform("boundaryLength");
+                    inverseBoundaryLengthSlot =
+                            mCaches.currentProgram->getUniform("inverseBoundaryLength");
+                }
+                float innerProportion = (length) / (length + 2);
+                float boundaryLength = (1 - innerProportion) / 2;
+                glUniform1f(boundaryLengthSlot, boundaryLength);
+                glUniform1f(inverseBoundaryLengthSlot, (1 / boundaryLength));
+
                 if (prevAAVertex != NULL) {
                     // Issue two repeat vertices to create degenerate triangles to bridge
                     // between the previous line and the new one. This is necessary because
                     // we are creating a single triangle_strip which will contain
                     // potentially discontinuous line segments.
-                    AlphaVertex::set(aaVertices++,prevAAVertex->position[0],
-                            prevAAVertex->position[1], prevAAVertex->alpha);
-                    AlphaVertex::set(aaVertices++, p4.x, p4.y, 1);
+                    AAVertex::set(aaVertices++,prevAAVertex->position[0],
+                            prevAAVertex->position[1], prevAAVertex->width, prevAAVertex->length);
+                    AAVertex::set(aaVertices++, p4.x, p4.y, 1, 1);
                     generatedVerticesCount += 2;
                 }
-                AlphaVertex::set(aaVertices++, p4.x, p4.y, 1);
-                AlphaVertex::set(aaVertices++, p1.x, p1.y, 1);
-                AlphaVertex::set(aaVertices++, p3.x, p3.y, 0);
-                AlphaVertex::set(aaVertices++, p2.x, p2.y, 0);
+                AAVertex::set(aaVertices++, p4.x, p4.y, 1, 1);
+                AAVertex::set(aaVertices++, p1.x, p1.y, 1, 0);
+                AAVertex::set(aaVertices++, p3.x, p3.y, 0, 1);
+                AAVertex::set(aaVertices++, p2.x, p2.y, 0, 0);
                 prevAAVertex = aaVertices - 1;
                 generatedVerticesCount += 4;
             }
-            dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
+            dirtyLayer(a.x == b.x ? left - 1 : left, a.y == b.y ? top - 1 : top,
+                    a.x == b.x ? right: right, a.y == b.y ? bottom: bottom,
+                    *mSnapshot->transform);
         }
     }
     if (generatedVerticesCount > 0) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 0ffd70b..b5c37c2 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -465,10 +465,12 @@
     void setupDrawSimpleMesh();
     void setupDrawTexture(GLuint texture);
     void setupDrawExternalTexture(GLuint texture);
-    void setupDrawTextureTransform(mat4& transform);
+    void setupDrawTextureTransform();
+    void setupDrawTextureTransformUniforms(mat4& transform);
     void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords = NULL, GLuint vbo = 0);
     void setupDrawVertices(GLvoid* vertices);
-    void setupDrawAALine(GLvoid* vertices, GLvoid* distanceCoords, float strokeWidth);
+    void setupDrawAALine(GLvoid* vertices, GLvoid* distanceCoords, GLvoid* lengthCoords,
+            float strokeWidth);
     void finishDrawTexture();
 
     void drawRegionRects(const Region& region);
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 62ac2ba..d419e3e 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -39,8 +39,9 @@
         "attribute vec4 position;\n";
 const char* gVS_Header_Attributes_TexCoords =
         "attribute vec2 texCoords;\n";
-const char* gVS_Header_Attributes_Distance =
-        "attribute float vtxDistance;\n";
+const char* gVS_Header_Attributes_AAParameters =
+        "attribute float vtxWidth;\n"
+        "attribute float vtxLength;\n";
 const char* gVS_Header_Uniforms_TextureTransform =
         "uniform mat4 mainTextureTransform;\n";
 const char* gVS_Header_Uniforms =
@@ -60,8 +61,9 @@
         "uniform mediump vec2 textureDimension;\n";
 const char* gVS_Header_Varyings_HasTexture =
         "varying vec2 outTexCoords;\n";
-const char* gVS_Header_Varyings_HasWidth =
-        "varying float distance;\n";
+const char* gVS_Header_Varyings_IsAA =
+        "varying float widthProportion;\n"
+        "varying float lengthProportion;\n";
 const char* gVS_Header_Varyings_HasBitmap =
         "varying vec2 outBitmapTexCoords;\n";
 const char* gVS_Header_Varyings_PointHasBitmap =
@@ -96,8 +98,9 @@
         "    gl_Position = transform * position;\n";
 const char* gVS_Main_PointSize =
         "    gl_PointSize = pointSize;\n";
-const char* gVS_Main_Width =
-        "    distance = vtxDistance;\n";
+const char* gVS_Main_AA =
+        "    widthProportion = vtxWidth;\n"
+        "    lengthProportion = vtxLength;\n";
 const char* gVS_Footer =
         "}\n\n";
 
@@ -113,10 +116,11 @@
         "precision mediump float;\n\n";
 const char* gFS_Uniforms_Color =
         "uniform vec4 color;\n";
-const char* gFS_Uniforms_Width =
-        "uniform float width;\n"
+const char* gFS_Uniforms_AA =
         "uniform float boundaryWidth;\n"
-        "uniform float inverseBoundaryWidth;\n";
+        "uniform float inverseBoundaryWidth;\n"
+        "uniform float boundaryLength;\n"
+        "uniform float inverseBoundaryLength;\n";
 const char* gFS_Header_Uniforms_PointHasBitmap =
         "uniform vec2 textureDimension;\n"
         "uniform float pointSize;\n";
@@ -189,11 +193,16 @@
         "    fragColor = color;\n";
 const char* gFS_Main_ModulateColor =
         "    fragColor *= color.a;\n";
-const char* gFS_Main_AccountForWidth =
-        "    if (distance < boundaryWidth) {\n"
-        "        fragColor *= (distance * inverseBoundaryWidth);\n"
-        "    } else if (distance > (1.0 - boundaryWidth)) {\n"
-        "        fragColor *= ((1.0 - distance) * inverseBoundaryWidth);\n"
+const char* gFS_Main_AccountForAA =
+        "    if (widthProportion < boundaryWidth) {\n"
+        "        fragColor *= (widthProportion * inverseBoundaryWidth);\n"
+        "    } else if (widthProportion > (1.0 - boundaryWidth)) {\n"
+        "        fragColor *= ((1.0 - widthProportion) * inverseBoundaryWidth);\n"
+        "    }\n"
+        "    if (lengthProportion < boundaryLength) {\n"
+        "        fragColor *= (lengthProportion * inverseBoundaryLength);\n"
+        "    } else if (lengthProportion > (1.0 - boundaryLength)) {\n"
+        "        fragColor *= ((1.0 - lengthProportion) * inverseBoundaryLength);\n"
         "    }\n";
 const char* gFS_Main_FetchTexture[2] = {
         // Don't modulate
@@ -380,12 +389,12 @@
     if (description.hasTexture || description.hasExternalTexture) {
         shader.append(gVS_Header_Attributes_TexCoords);
     }
-    if (description.hasWidth) {
-        shader.append(gVS_Header_Attributes_Distance);
+    if (description.isAA) {
+        shader.append(gVS_Header_Attributes_AAParameters);
     }
     // Uniforms
     shader.append(gVS_Header_Uniforms);
-    if (description.hasExternalTexture) {
+    if (description.hasTextureTransform) {
         shader.append(gVS_Header_Uniforms_TextureTransform);
     }
     if (description.hasGradient) {
@@ -401,8 +410,8 @@
     if (description.hasTexture || description.hasExternalTexture) {
         shader.append(gVS_Header_Varyings_HasTexture);
     }
-    if (description.hasWidth) {
-        shader.append(gVS_Header_Varyings_HasWidth);
+    if (description.isAA) {
+        shader.append(gVS_Header_Varyings_IsAA);
     }
     if (description.hasGradient) {
         shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]);
@@ -415,14 +424,13 @@
 
     // Begin the shader
     shader.append(gVS_Main); {
-        if (description.hasTexture) {
+        if (description.hasTextureTransform) {
+            shader.append(gVS_Main_OutTransformedTexCoords);
+        } else if (description.hasTexture || description.hasExternalTexture) {
             shader.append(gVS_Main_OutTexCoords);
         }
-        if (description.hasExternalTexture) {
-            shader.append(gVS_Main_OutTransformedTexCoords);
-        }
-        if (description.hasWidth) {
-            shader.append(gVS_Main_Width);
+        if (description.isAA) {
+            shader.append(gVS_Main_AA);
         }
         if (description.hasGradient) {
             shader.append(gVS_Main_OutGradient[description.gradientType]);
@@ -464,8 +472,8 @@
     if (description.hasTexture || description.hasExternalTexture) {
         shader.append(gVS_Header_Varyings_HasTexture);
     }
-    if (description.hasWidth) {
-        shader.append(gVS_Header_Varyings_HasWidth);
+    if (description.isAA) {
+        shader.append(gVS_Header_Varyings_IsAA);
     }
     if (description.hasGradient) {
         shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]);
@@ -487,12 +495,11 @@
     }
     if (description.hasTexture) {
         shader.append(gFS_Uniforms_TextureSampler);
-    }
-    if (description.hasExternalTexture) {
+    } else if (description.hasExternalTexture) {
         shader.append(gFS_Uniforms_ExternalTextureSampler);
     }
-    if (description.hasWidth) {
-        shader.append(gFS_Uniforms_Width);
+    if (description.isAA) {
+        shader.append(gFS_Uniforms_AA);
     }
     if (description.hasGradient) {
         shader.append(gFS_Uniforms_GradientSampler[description.gradientType]);
@@ -502,7 +509,7 @@
     }
 
     // Optimization for common cases
-    if (!description.hasWidth && !blendFramebuffer &&
+    if (!description.isAA && !blendFramebuffer &&
             description.colorOp == ProgramDescription::kColorNone && !description.isPoint) {
         bool fast = false;
 
@@ -587,8 +594,8 @@
                 shader.append(gFS_Main_FetchColor);
             }
         }
-        if (description.hasWidth) {
-            shader.append(gFS_Main_AccountForWidth);
+        if (description.isAA) {
+            shader.append(gFS_Main_AccountForAA);
         }
         if (description.hasGradient) {
             shader.append(gFS_Main_FetchGradient[description.gradientType]);
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index 70909fd..5c7197b 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -75,9 +75,10 @@
 
 #define PROGRAM_IS_POINT_SHIFT 36
 
-#define PROGRAM_HAS_WIDTH_SHIFT 37
+#define PROGRAM_HAS_AA_SHIFT 37
 
 #define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 38
+#define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 39
 
 ///////////////////////////////////////////////////////////////////////////////
 // Types
@@ -116,6 +117,7 @@
     bool hasTexture;
     bool hasAlpha8Texture;
     bool hasExternalTexture;
+    bool hasTextureTransform;
 
     // Modulate, this should only be set when setColor() return true
     bool modulate;
@@ -124,7 +126,7 @@
     bool hasBitmap;
     bool isBitmapNpot;
 
-    bool hasWidth;
+    bool isAA;
 
     bool hasGradient;
     Gradient gradientType;
@@ -155,8 +157,9 @@
         hasTexture = false;
         hasAlpha8Texture = false;
         hasExternalTexture = false;
+        hasTextureTransform = false;
 
-        hasWidth = false;
+        isAA = false;
 
         modulate = false;
 
@@ -243,8 +246,9 @@
         if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST;
         if (modulate) key |= programid(0x1) << PROGRAM_MODULATE_SHIFT;
         if (isPoint) key |= programid(0x1) << PROGRAM_IS_POINT_SHIFT;
-        if (hasWidth) key |= programid(0x1) << PROGRAM_HAS_WIDTH_SHIFT;
+        if (isAA) key |= programid(0x1) << PROGRAM_HAS_AA_SHIFT;
         if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
+        if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
         return key;
     }
 
diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h
index c120428..38455dc 100644
--- a/libs/hwui/Vertex.h
+++ b/libs/hwui/Vertex.h
@@ -68,6 +68,25 @@
     }
 }; // struct AlphaVertex
 
+/**
+ * Simple structure to describe a vertex with a position and an alpha value.
+ */
+struct AAVertex : Vertex {
+    float width;
+    float length;
+
+    static inline void set(AAVertex* vertex, float x, float y, float width, float length) {
+        Vertex::set(vertex, x, y);
+        vertex[0].width = width;
+        vertex[0].length = length;
+    }
+
+    static inline void setColor(AAVertex* vertex, float width, float length) {
+        vertex[0].width = width;
+        vertex[0].length = length;
+    }
+}; // struct AlphaVertex
+
 }; // namespace uirenderer
 }; // namespace android
 
diff --git a/libs/rs/Android.mk b/libs/rs/Android.mk
index a99a599..232ab36 100644
--- a/libs/rs/Android.mk
+++ b/libs/rs/Android.mk
@@ -110,20 +110,23 @@
 	rsScriptC.cpp \
 	rsScriptC_Lib.cpp \
 	rsScriptC_LibGL.cpp \
-	rsShaderCache.cpp \
 	rsSignal.cpp \
 	rsStream.cpp \
 	rsThreadIO.cpp \
 	rsType.cpp \
-	rsVertexArray.cpp \
 	driver/rsdBcc.cpp \
 	driver/rsdCore.cpp \
 	driver/rsdGL.cpp \
+	driver/rsdMesh.cpp \
+	driver/rsdMeshObj.cpp \
+	driver/rsdProgram.cpp \
 	driver/rsdProgramRaster.cpp \
 	driver/rsdProgramStore.cpp \
 	driver/rsdRuntimeMath.cpp \
-	driver/rsdRuntimeStubs.cpp
-
+	driver/rsdRuntimeStubs.cpp \
+	driver/rsdShader.cpp \
+	driver/rsdShaderCache.cpp \
+	driver/rsdVertexArray.cpp
 
 LOCAL_SHARED_LIBRARIES += libz libcutils libutils libEGL libGLESv1_CM libGLESv2 libui libbcc
 
diff --git a/libs/rs/driver/rsdCore.cpp b/libs/rs/driver/rsdCore.cpp
index 5b80439..d5d23c7 100644
--- a/libs/rs/driver/rsdCore.cpp
+++ b/libs/rs/driver/rsdCore.cpp
@@ -19,6 +19,9 @@
 #include "rsdGL.h"
 #include "rsdProgramStore.h"
 #include "rsdProgramRaster.h"
+#include "rsdProgramVertex.h"
+#include "rsdProgramFragment.h"
+#include "rsdMesh.h"
 
 #include <malloc.h>
 #include "rsContext.h"
@@ -69,6 +72,24 @@
         rsdProgramRasterInit,
         rsdProgramRasterSetActive,
         rsdProgramRasterDestroy
+    },
+
+    {
+        rsdProgramVertexInit,
+        rsdProgramVertexSetActive,
+        rsdProgramVertexDestroy
+    },
+
+    {
+        rsdProgramFragmentInit,
+        rsdProgramFragmentSetActive,
+        rsdProgramFragmentDestroy
+    },
+
+    {
+        rsdMeshInit,
+        rsdMeshDraw,
+        rsdMeshDestroy
     }
 
 };
diff --git a/libs/rs/driver/rsdGL.cpp b/libs/rs/driver/rsdGL.cpp
index 26e1bdf..48690d5 100644
--- a/libs/rs/driver/rsdGL.cpp
+++ b/libs/rs/driver/rsdGL.cpp
@@ -40,6 +40,8 @@
 
 #include <malloc.h>
 #include "rsContext.h"
+#include "rsdShaderCache.h"
+#include "rsdVertexArray.h"
 
 using namespace android;
 using namespace android::renderscript;
@@ -128,6 +130,11 @@
 void rsdGLShutdown(const Context *rsc) {
     RsdHal *dc = (RsdHal *)rsc->mHal.drv;
 
+    dc->gl.shaderCache->cleanupAll();
+    delete dc->gl.shaderCache;
+
+    delete dc->gl.vertexArrayState;
+
     LOGV("%p, deinitEGL", rsc);
 
     if (dc->gl.egl.context != EGL_NO_CONTEXT) {
@@ -287,6 +294,10 @@
         DumpDebug(dc);
     }
 
+    dc->gl.shaderCache = new RsdShaderCache();
+    dc->gl.vertexArrayState = new RsdVertexArrayState();
+    dc->gl.vertexArrayState->init(dc->gl.gl.maxVertexAttribs);
+
     LOGV("initGLThread end %p", rsc);
     return true;
 }
diff --git a/libs/rs/driver/rsdGL.h b/libs/rs/driver/rsdGL.h
index 246931f..351b2d5 100644
--- a/libs/rs/driver/rsdGL.h
+++ b/libs/rs/driver/rsdGL.h
@@ -19,7 +19,8 @@
 
 #include <rs_hal.h>
 
-
+class RsdShaderCache;
+class RsdVertexArrayState;
 
 typedef void (* InvokeFunc_t)(void);
 typedef void (*WorkerCallback_t)(void *usr, uint32_t idx);
@@ -64,6 +65,8 @@
     ANativeWindow *wndSurface;
     uint32_t width;
     uint32_t height;
+    RsdShaderCache *shaderCache;
+    RsdVertexArrayState *vertexArrayState;
 } RsdGL;
 
 
diff --git a/libs/rs/driver/rsdMesh.cpp b/libs/rs/driver/rsdMesh.cpp
new file mode 100644
index 0000000..eb62ddb
--- /dev/null
+++ b/libs/rs/driver/rsdMesh.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <rs_hal.h>
+#include <rsContext.h>
+#include <rsMesh.h>
+
+#include "rsdCore.h"
+#include "rsdMesh.h"
+#include "rsdMeshObj.h"
+#include "rsdShaderCache.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+bool rsdMeshInit(const Context *rsc, const Mesh *m) {
+    RsdMeshObj *drv = NULL;
+    if(m->mHal.drv) {
+        drv = (RsdMeshObj*)m->mHal.drv;
+        delete drv;
+    }
+    drv = new RsdMeshObj(rsc, m);
+    m->mHal.drv = drv;
+    return drv->init();
+}
+
+void rsdMeshDraw(const Context *rsc, const Mesh *m, uint32_t primIndex, uint32_t start, uint32_t len) {
+    if(m->mHal.drv) {
+        RsdHal *dc = (RsdHal *)rsc->mHal.drv;
+        if (!dc->gl.shaderCache->setup(rsc)) {
+            return;
+        }
+
+        RsdMeshObj *drv = (RsdMeshObj*)m->mHal.drv;
+        drv->renderPrimitiveRange(rsc, primIndex, start, len);
+    }
+}
+
+void rsdMeshDestroy(const Context *rsc, const Mesh *m) {
+    if(m->mHal.drv) {
+        RsdMeshObj *drv = (RsdMeshObj*)m->mHal.drv;
+        delete drv;
+    }
+}
+
+
diff --git a/libs/rs/driver/rsdMesh.h b/libs/rs/driver/rsdMesh.h
new file mode 100644
index 0000000..d2714fd
--- /dev/null
+++ b/libs/rs/driver/rsdMesh.h
@@ -0,0 +1,32 @@
+/*
+ * 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 RSD_MESH_H
+#define RSD_MESH_H
+
+#include <rs_hal.h>
+
+
+bool rsdMeshInit(const android::renderscript::Context *rsc,
+                 const android::renderscript::Mesh *m);
+void rsdMeshDraw(const android::renderscript::Context *rsc,
+                 const android::renderscript::Mesh *m,
+                 uint32_t primIndex, uint32_t start, uint32_t len);
+void rsdMeshDestroy(const android::renderscript::Context *rsc,
+                    const android::renderscript::Mesh *m);
+
+
+#endif
diff --git a/libs/rs/driver/rsdMeshObj.cpp b/libs/rs/driver/rsdMeshObj.cpp
new file mode 100644
index 0000000..6bb33f7
--- /dev/null
+++ b/libs/rs/driver/rsdMeshObj.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <GLES/gl.h>
+#include <GLES2/gl2.h>
+#include <GLES/glext.h>
+
+#include <rs_hal.h>
+#include <rsContext.h>
+#include <rsMesh.h>
+
+#include "rsdMeshObj.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+RsdMeshObj::RsdMeshObj(const Context *rsc, const Mesh *rsMesh) {
+    mRSMesh = rsMesh;
+
+    mAttribs = NULL;
+    mAttribAllocationIndex = NULL;
+    mGLPrimitives = NULL;
+
+    mAttribCount = 0;
+}
+
+RsdMeshObj::~RsdMeshObj() {
+    if (mAttribs) {
+        delete[] mAttribs;
+        delete[] mAttribAllocationIndex;
+    }
+    if (mGLPrimitives) {
+        delete[] mGLPrimitives;
+    }
+}
+
+bool RsdMeshObj::isValidGLComponent(const Element *elem, uint32_t fieldIdx) {
+    // Do not create attribs for padding
+    if (elem->getFieldName(fieldIdx)[0] == '#') {
+        return false;
+    }
+
+    // Only GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_FIXED, GL_FLOAT are accepted.
+    // Filter rs types accordingly
+    RsDataType dt = elem->getField(fieldIdx)->getComponent().getType();
+    if (dt != RS_TYPE_FLOAT_32 && dt != RS_TYPE_UNSIGNED_8 &&
+        dt != RS_TYPE_UNSIGNED_16 && dt != RS_TYPE_SIGNED_8 &&
+        dt != RS_TYPE_SIGNED_16) {
+        return false;
+    }
+
+    // Now make sure they are not arrays
+    uint32_t arraySize = elem->getFieldArraySize(fieldIdx);
+    if (arraySize != 1) {
+        return false;
+    }
+
+    return true;
+}
+
+bool RsdMeshObj::init() {
+
+    updateGLPrimitives();
+
+    // Count the number of gl attrs to initialize
+    mAttribCount = 0;
+    for (uint32_t ct=0; ct < mRSMesh->mHal.state.vertexBuffersCount; ct++) {
+        const Element *elem = mRSMesh->mHal.state.vertexBuffers[ct]->getType()->getElement();
+        for (uint32_t ct=0; ct < elem->getFieldCount(); ct++) {
+            if (isValidGLComponent(elem, ct)) {
+                mAttribCount ++;
+            }
+        }
+    }
+
+    if (mAttribs) {
+        delete [] mAttribs;
+        delete [] mAttribAllocationIndex;
+        mAttribs = NULL;
+        mAttribAllocationIndex = NULL;
+    }
+    if (!mAttribCount) {
+        return false;
+    }
+
+    mAttribs = new RsdVertexArray::Attrib[mAttribCount];
+    mAttribAllocationIndex = new uint32_t[mAttribCount];
+
+    uint32_t userNum = 0;
+    for (uint32_t ct=0; ct < mRSMesh->mHal.state.vertexBuffersCount; ct++) {
+        const Element *elem = mRSMesh->mHal.state.vertexBuffers[ct]->getType()->getElement();
+        uint32_t stride = elem->getSizeBytes();
+        for (uint32_t fieldI=0; fieldI < elem->getFieldCount(); fieldI++) {
+            const Component &c = elem->getField(fieldI)->getComponent();
+
+            if (!isValidGLComponent(elem, fieldI)) {
+                continue;
+            }
+
+            mAttribs[userNum].size = c.getVectorSize();
+            mAttribs[userNum].offset = elem->getFieldOffsetBytes(fieldI);
+            mAttribs[userNum].type = c.getGLType();
+            mAttribs[userNum].normalized = c.getType() != RS_TYPE_FLOAT_32;//c.getIsNormalized();
+            mAttribs[userNum].stride = stride;
+            String8 tmp(RS_SHADER_ATTR);
+            tmp.append(elem->getFieldName(fieldI));
+            mAttribs[userNum].name.setTo(tmp.string());
+
+            // Remember which allocation this attribute came from
+            mAttribAllocationIndex[userNum] = ct;
+            userNum ++;
+        }
+    }
+
+    return true;
+}
+
+void RsdMeshObj::renderPrimitiveRange(const Context *rsc, uint32_t primIndex, uint32_t start, uint32_t len) const {
+    if (len < 1 || primIndex >= mRSMesh->mHal.state.primitivesCount || mAttribCount == 0) {
+        LOGE("Invalid mesh or parameters");
+        return;
+    }
+
+    rsc->checkError("Mesh::renderPrimitiveRange 1");
+    // update attributes with either buffer information or data ptr based on their current state
+    for (uint32_t ct=0; ct < mAttribCount; ct++) {
+        uint32_t allocIndex = mAttribAllocationIndex[ct];
+        Allocation *alloc = mRSMesh->mHal.state.vertexBuffers[allocIndex].get();
+        if (alloc->getIsBufferObject() && alloc->getBufferObjectID()) {
+            mAttribs[ct].buffer = alloc->getBufferObjectID();
+            mAttribs[ct].ptr = NULL;
+        } else {
+            mAttribs[ct].buffer = 0;
+            mAttribs[ct].ptr = (const uint8_t*)alloc->getPtr();
+        }
+    }
+
+    RsdVertexArray va(mAttribs, mAttribCount);
+    va.setupGL2(rsc);
+
+    rsc->checkError("Mesh::renderPrimitiveRange 2");
+    Mesh::Primitive_t *prim = mRSMesh->mHal.state.primitives[primIndex];
+    if (prim->mIndexBuffer.get()) {
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, prim->mIndexBuffer->getBufferObjectID());
+        glDrawElements(mGLPrimitives[primIndex], len, GL_UNSIGNED_SHORT, (uint16_t *)(start * 2));
+    } else {
+        glDrawArrays(mGLPrimitives[primIndex], start, len);
+    }
+
+    rsc->checkError("Mesh::renderPrimitiveRange");
+}
+
+void RsdMeshObj::updateGLPrimitives() {
+    mGLPrimitives = new uint32_t[mRSMesh->mHal.state.primitivesCount];
+    for (uint32_t i = 0; i < mRSMesh->mHal.state.primitivesCount; i ++) {
+        switch (mRSMesh->mHal.state.primitives[i]->mPrimitive) {
+            case RS_PRIMITIVE_POINT:          mGLPrimitives[i] = GL_POINTS; break;
+            case RS_PRIMITIVE_LINE:           mGLPrimitives[i] = GL_LINES; break;
+            case RS_PRIMITIVE_LINE_STRIP:     mGLPrimitives[i] = GL_LINE_STRIP; break;
+            case RS_PRIMITIVE_TRIANGLE:       mGLPrimitives[i] = GL_TRIANGLES; break;
+            case RS_PRIMITIVE_TRIANGLE_STRIP: mGLPrimitives[i] = GL_TRIANGLE_STRIP; break;
+            case RS_PRIMITIVE_TRIANGLE_FAN:   mGLPrimitives[i] = GL_TRIANGLE_FAN; break;
+        }
+    }
+}
diff --git a/libs/rs/driver/rsdMeshObj.h b/libs/rs/driver/rsdMeshObj.h
new file mode 100644
index 0000000..8b1271b
--- /dev/null
+++ b/libs/rs/driver/rsdMeshObj.h
@@ -0,0 +1,63 @@
+/*
+ * 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 ANDROID_RSD_MESH_OBJ_H
+#define ANDROID_RSD_MESH_OBJ_H
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+    class Context;
+    class Mesh;
+    class Element;
+
+}
+}
+
+#include "driver/rsdVertexArray.h"
+
+// An element is a group of Components that occupies one cell in a structure.
+class RsdMeshObj {
+public:
+    RsdMeshObj(const android::renderscript::Context *,
+            const android::renderscript::Mesh *);
+    ~RsdMeshObj();
+
+    void renderPrimitiveRange(const android::renderscript::Context *, uint32_t primIndex, uint32_t start, uint32_t len) const;
+
+    bool init();
+
+protected:
+    const android::renderscript::Mesh *mRSMesh;
+
+    uint32_t *mGLPrimitives;
+    void updateGLPrimitives();
+
+    bool isValidGLComponent(const android::renderscript::Element *elem, uint32_t fieldIdx);
+    // Attribues that allow us to map to GL
+    RsdVertexArray::Attrib *mAttribs;
+    // This allows us to figure out which allocation the attribute
+    // belongs to. In the event the allocation is uploaded to GL
+    // buffer, it lets us properly map it
+    uint32_t *mAttribAllocationIndex;
+    uint32_t mAttribCount;
+};
+
+#endif //ANDROID_RSD_MESH_OBJ_H
+
+
+
diff --git a/libs/rs/driver/rsdProgram.cpp b/libs/rs/driver/rsdProgram.cpp
new file mode 100644
index 0000000..502c5ee
--- /dev/null
+++ b/libs/rs/driver/rsdProgram.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include "rsdCore.h"
+#include "rsdProgramVertex.h"
+#include "rsdShader.h"
+#include "rsdShaderCache.h"
+
+#include "rsContext.h"
+#include "rsProgramVertex.h"
+#include "rsProgramFragment.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+using namespace android;
+using namespace android::renderscript;
+
+bool rsdProgramVertexInit(const Context *rsc, const ProgramVertex *pv,
+                          const char* shader, uint32_t shaderLen) {
+    RsdShader *drv = new RsdShader(pv, GL_VERTEX_SHADER, shader, shaderLen);
+    pv->mHal.drv = drv;
+
+    return drv->createShader();
+}
+
+void rsdProgramVertexSetActive(const Context *rsc, const ProgramVertex *pv) {
+    RsdHal *dc = (RsdHal *)rsc->mHal.drv;
+
+    dc->gl.shaderCache->setActiveVertex((RsdShader*)pv->mHal.drv);
+}
+
+void rsdProgramVertexDestroy(const Context *rsc, const ProgramVertex *pv) {
+    RsdHal *dc = (RsdHal *)rsc->mHal.drv;
+
+    RsdShader *drv = NULL;
+    if(pv->mHal.drv) {
+        drv = (RsdShader*)pv->mHal.drv;
+        if (rsc->props.mLogShaders) {
+            LOGV("Destroying vertex shader with ID %u", drv->getShaderID());
+        }
+        if (drv->getShaderID()) {
+            dc->gl.shaderCache->cleanupVertex(drv->getShaderID());
+        }
+        delete drv;
+    }
+}
+
+bool rsdProgramFragmentInit(const Context *rsc, const ProgramFragment *pf,
+                          const char* shader, uint32_t shaderLen) {
+    RsdShader *drv = new RsdShader(pf, GL_FRAGMENT_SHADER, shader, shaderLen);
+    pf->mHal.drv = drv;
+
+    return drv->createShader();
+}
+
+void rsdProgramFragmentSetActive(const Context *rsc, const ProgramFragment *pf) {
+    RsdHal *dc = (RsdHal *)rsc->mHal.drv;
+
+    dc->gl.shaderCache->setActiveFragment((RsdShader*)pf->mHal.drv);
+}
+
+void rsdProgramFragmentDestroy(const Context *rsc, const ProgramFragment *pf) {
+    RsdHal *dc = (RsdHal *)rsc->mHal.drv;
+
+    RsdShader *drv = NULL;
+    if(pf->mHal.drv) {
+        drv = (RsdShader*)pf->mHal.drv;
+        if (rsc->props.mLogShaders) {
+            LOGV("Destroying fragment shader with ID %u", drv->getShaderID());
+        }
+        if (drv->getShaderID()) {
+            dc->gl.shaderCache->cleanupFragment(drv->getShaderID());
+        }
+        delete drv;
+    }
+}
+
+
diff --git a/libs/rs/driver/rsdProgramFragment.h b/libs/rs/driver/rsdProgramFragment.h
new file mode 100644
index 0000000..366cb40
--- /dev/null
+++ b/libs/rs/driver/rsdProgramFragment.h
@@ -0,0 +1,32 @@
+/*
+ * 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 RSD_PROGRAM_FRAGMENT_H
+#define RSD_PROGRAM_FRAGMENT_H
+
+#include <rs_hal.h>
+
+
+bool rsdProgramFragmentInit(const android::renderscript::Context *rsc,
+                            const android::renderscript::ProgramFragment *,
+                            const char* shader, uint32_t shaderLen);
+void rsdProgramFragmentSetActive(const android::renderscript::Context *rsc,
+                                 const android::renderscript::ProgramFragment *);
+void rsdProgramFragmentDestroy(const android::renderscript::Context *rsc,
+                               const android::renderscript::ProgramFragment *);
+
+
+#endif //RSD_PROGRAM_Fragment_H
diff --git a/libs/rs/driver/rsdProgramVertex.h b/libs/rs/driver/rsdProgramVertex.h
new file mode 100644
index 0000000..e998572
--- /dev/null
+++ b/libs/rs/driver/rsdProgramVertex.h
@@ -0,0 +1,31 @@
+/*
+ * 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 RSD_PROGRAM_VERTEX_H
+#define RSD_PROGRAM_VERTEX_H
+
+#include <rs_hal.h>
+
+bool rsdProgramVertexInit(const android::renderscript::Context *rsc,
+                          const android::renderscript::ProgramVertex *,
+                          const char* shader, uint32_t shaderLen);
+void rsdProgramVertexSetActive(const android::renderscript::Context *rsc,
+                               const android::renderscript::ProgramVertex *);
+void rsdProgramVertexDestroy(const android::renderscript::Context *rsc,
+                             const android::renderscript::ProgramVertex *);
+
+
+#endif //RSD_PROGRAM_VERTEX_H
diff --git a/libs/rs/driver/rsdRuntimeMath.cpp b/libs/rs/driver/rsdRuntimeMath.cpp
index 093e311..acb990d 100644
--- a/libs/rs/driver/rsdRuntimeMath.cpp
+++ b/libs/rs/driver/rsdRuntimeMath.cpp
@@ -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.
diff --git a/libs/rs/driver/rsdRuntimeStubs.cpp b/libs/rs/driver/rsdRuntimeStubs.cpp
index b70a123..9cbff95 100644
--- a/libs/rs/driver/rsdRuntimeStubs.cpp
+++ b/libs/rs/driver/rsdRuntimeStubs.cpp
@@ -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.
diff --git a/libs/rs/driver/rsdShader.cpp b/libs/rs/driver/rsdShader.cpp
new file mode 100644
index 0000000..fc623d6
--- /dev/null
+++ b/libs/rs/driver/rsdShader.cpp
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <rs_hal.h>
+#include <rsContext.h>
+#include <rsProgram.h>
+
+#include "rsdShader.h"
+#include "rsdShaderCache.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+RsdShader::RsdShader(const Program *p, uint32_t type,
+                       const char * shaderText, uint32_t shaderLength) {
+
+    mUserShader.setTo(shaderText, shaderLength);
+    mRSProgram = p;
+    mType = type;
+    initMemberVars();
+    initAttribAndUniformArray();
+    init();
+}
+
+RsdShader::~RsdShader() {
+    if (mShaderID) {
+        glDeleteShader(mShaderID);
+    }
+
+    delete[] mAttribNames;
+    delete[] mUniformNames;
+    delete[] mUniformArraySizes;
+}
+
+void RsdShader::initMemberVars() {
+    mDirty = true;
+    mShaderID = 0;
+    mAttribCount = 0;
+    mUniformCount = 0;
+
+    mAttribNames = NULL;
+    mUniformNames = NULL;
+    mUniformArraySizes = NULL;
+
+    mIsValid = false;
+}
+
+void RsdShader::init() {
+    uint32_t attribCount = 0;
+    uint32_t uniformCount = 0;
+    for (uint32_t ct=0; ct < mRSProgram->mHal.state.inputElementsCount; ct++) {
+        initAddUserElement(mRSProgram->mHal.state.inputElements[ct].get(), mAttribNames, NULL, &attribCount, RS_SHADER_ATTR);
+    }
+    for (uint32_t ct=0; ct < mRSProgram->mHal.state.constantsCount; ct++) {
+        initAddUserElement(mRSProgram->mHal.state.constantTypes[ct]->getElement(), mUniformNames, mUniformArraySizes, &uniformCount, RS_SHADER_UNI);
+    }
+
+    mTextureUniformIndexStart = uniformCount;
+    char buf[256];
+    for (uint32_t ct=0; ct < mRSProgram->mHal.state.texturesCount; ct++) {
+        snprintf(buf, sizeof(buf), "UNI_Tex%i", ct);
+        mUniformNames[uniformCount].setTo(buf);
+        mUniformArraySizes[uniformCount] = 1;
+        uniformCount++;
+    }
+}
+
+String8 RsdShader::getGLSLInputString() const {
+    String8 s;
+    for (uint32_t ct=0; ct < mRSProgram->mHal.state.inputElementsCount; ct++) {
+        const Element *e = mRSProgram->mHal.state.inputElements[ct].get();
+        for (uint32_t field=0; field < e->getFieldCount(); field++) {
+            const Element *f = e->getField(field);
+
+            // Cannot be complex
+            rsAssert(!f->getFieldCount());
+            switch (f->getComponent().getVectorSize()) {
+            case 1: s.append("attribute float ATTRIB_"); break;
+            case 2: s.append("attribute vec2 ATTRIB_"); break;
+            case 3: s.append("attribute vec3 ATTRIB_"); break;
+            case 4: s.append("attribute vec4 ATTRIB_"); break;
+            default:
+                rsAssert(0);
+            }
+
+            s.append(e->getFieldName(field));
+            s.append(";\n");
+        }
+    }
+    return s;
+}
+
+void RsdShader::appendAttributes() {
+    for (uint32_t ct=0; ct < mRSProgram->mHal.state.inputElementsCount; ct++) {
+        const Element *e = mRSProgram->mHal.state.inputElements[ct].get();
+        for (uint32_t field=0; field < e->getFieldCount(); field++) {
+            const Element *f = e->getField(field);
+            const char *fn = e->getFieldName(field);
+
+            if (fn[0] == '#') {
+                continue;
+            }
+
+            // Cannot be complex
+            rsAssert(!f->getFieldCount());
+            switch (f->getComponent().getVectorSize()) {
+            case 1: mShader.append("attribute float ATTRIB_"); break;
+            case 2: mShader.append("attribute vec2 ATTRIB_"); break;
+            case 3: mShader.append("attribute vec3 ATTRIB_"); break;
+            case 4: mShader.append("attribute vec4 ATTRIB_"); break;
+            default:
+                rsAssert(0);
+            }
+
+            mShader.append(fn);
+            mShader.append(";\n");
+        }
+    }
+}
+
+void RsdShader::appendTextures() {
+    char buf[256];
+    for (uint32_t ct=0; ct < mRSProgram->mHal.state.texturesCount; ct++) {
+        if (mRSProgram->mHal.state.textureTargets[ct] == RS_TEXTURE_2D) {
+            snprintf(buf, sizeof(buf), "uniform sampler2D UNI_Tex%i;\n", ct);
+        } else {
+            snprintf(buf, sizeof(buf), "uniform samplerCube UNI_Tex%i;\n", ct);
+        }
+        mShader.append(buf);
+    }
+}
+
+bool RsdShader::createShader() {
+
+    if (mType == GL_FRAGMENT_SHADER) {
+        mShader.append("precision mediump float;\n");
+    }
+    appendUserConstants();
+    appendAttributes();
+    appendTextures();
+
+    mShader.append(mUserShader);
+
+    return true;
+}
+
+bool RsdShader::loadShader(const Context *rsc) {
+    mShaderID = glCreateShader(mType);
+    rsAssert(mShaderID);
+
+    if (rsc->props.mLogShaders) {
+        LOGV("Loading shader type %x, ID %i", mType, mShaderID);
+        LOGV("%s", mShader.string());
+    }
+
+    if (mShaderID) {
+        const char * ss = mShader.string();
+        glShaderSource(mShaderID, 1, &ss, NULL);
+        glCompileShader(mShaderID);
+
+        GLint compiled = 0;
+        glGetShaderiv(mShaderID, GL_COMPILE_STATUS, &compiled);
+        if (!compiled) {
+            GLint infoLen = 0;
+            glGetShaderiv(mShaderID, GL_INFO_LOG_LENGTH, &infoLen);
+            if (infoLen) {
+                char* buf = (char*) malloc(infoLen);
+                if (buf) {
+                    glGetShaderInfoLog(mShaderID, infoLen, NULL, buf);
+                    LOGE("Could not compile shader \n%s\n", buf);
+                    free(buf);
+                }
+                glDeleteShader(mShaderID);
+                mShaderID = 0;
+                rsc->setError(RS_ERROR_BAD_SHADER, "Error returned from GL driver loading shader text,");
+                return false;
+            }
+        }
+    }
+
+    if (rsc->props.mLogShaders) {
+        LOGV("--Shader load result %x ", glGetError());
+    }
+    mIsValid = true;
+    return true;
+}
+
+void RsdShader::appendUserConstants() {
+    for (uint32_t ct=0; ct < mRSProgram->mHal.state.constantsCount; ct++) {
+        const Element *e = mRSProgram->mHal.state.constantTypes[ct]->getElement();
+        for (uint32_t field=0; field < e->getFieldCount(); field++) {
+            const Element *f = e->getField(field);
+            const char *fn = e->getFieldName(field);
+
+            if (fn[0] == '#') {
+                continue;
+            }
+
+            // Cannot be complex
+            rsAssert(!f->getFieldCount());
+            if (f->getType() == RS_TYPE_MATRIX_4X4) {
+                mShader.append("uniform mat4 UNI_");
+            } else if (f->getType() == RS_TYPE_MATRIX_3X3) {
+                mShader.append("uniform mat3 UNI_");
+            } else if (f->getType() == RS_TYPE_MATRIX_2X2) {
+                mShader.append("uniform mat2 UNI_");
+            } else {
+                switch (f->getComponent().getVectorSize()) {
+                case 1: mShader.append("uniform float UNI_"); break;
+                case 2: mShader.append("uniform vec2 UNI_"); break;
+                case 3: mShader.append("uniform vec3 UNI_"); break;
+                case 4: mShader.append("uniform vec4 UNI_"); break;
+                default:
+                    rsAssert(0);
+                }
+            }
+
+            mShader.append(fn);
+            if (e->getFieldArraySize(field) > 1) {
+                mShader.appendFormat("[%d]", e->getFieldArraySize(field));
+            }
+            mShader.append(";\n");
+        }
+    }
+}
+
+void RsdShader::logUniform(const Element *field, const float *fd, uint32_t arraySize ) {
+    RsDataType dataType = field->getType();
+    uint32_t elementSize = field->getSizeBytes() / sizeof(float);
+    for (uint32_t i = 0; i < arraySize; i ++) {
+        if (arraySize > 1) {
+            LOGV("Array Element [%u]", i);
+        }
+        if (dataType == RS_TYPE_MATRIX_4X4) {
+            LOGV("Matrix4x4");
+            LOGV("{%f, %f, %f, %f",  fd[0], fd[4], fd[8], fd[12]);
+            LOGV(" %f, %f, %f, %f",  fd[1], fd[5], fd[9], fd[13]);
+            LOGV(" %f, %f, %f, %f",  fd[2], fd[6], fd[10], fd[14]);
+            LOGV(" %f, %f, %f, %f}", fd[3], fd[7], fd[11], fd[15]);
+        } else if (dataType == RS_TYPE_MATRIX_3X3) {
+            LOGV("Matrix3x3");
+            LOGV("{%f, %f, %f",  fd[0], fd[3], fd[6]);
+            LOGV(" %f, %f, %f",  fd[1], fd[4], fd[7]);
+            LOGV(" %f, %f, %f}", fd[2], fd[5], fd[8]);
+        } else if (dataType == RS_TYPE_MATRIX_2X2) {
+            LOGV("Matrix2x2");
+            LOGV("{%f, %f",  fd[0], fd[2]);
+            LOGV(" %f, %f}", fd[1], fd[3]);
+        } else {
+            switch (field->getComponent().getVectorSize()) {
+            case 1:
+                LOGV("Uniform 1 = %f", fd[0]);
+                break;
+            case 2:
+                LOGV("Uniform 2 = %f %f", fd[0], fd[1]);
+                break;
+            case 3:
+                LOGV("Uniform 3 = %f %f %f", fd[0], fd[1], fd[2]);
+                break;
+            case 4:
+                LOGV("Uniform 4 = %f %f %f %f", fd[0], fd[1], fd[2], fd[3]);
+                break;
+            default:
+                rsAssert(0);
+            }
+        }
+        LOGE("Element size %u data=%p", elementSize, fd);
+        fd += elementSize;
+        LOGE("New data=%p", fd);
+    }
+}
+
+void RsdShader::setUniform(const Context *rsc, const Element *field, const float *fd,
+                         int32_t slot, uint32_t arraySize ) {
+    RsDataType dataType = field->getType();
+    if (dataType == RS_TYPE_MATRIX_4X4) {
+        glUniformMatrix4fv(slot, arraySize, GL_FALSE, fd);
+    } else if (dataType == RS_TYPE_MATRIX_3X3) {
+        glUniformMatrix3fv(slot, arraySize, GL_FALSE, fd);
+    } else if (dataType == RS_TYPE_MATRIX_2X2) {
+        glUniformMatrix2fv(slot, arraySize, GL_FALSE, fd);
+    } else {
+        switch (field->getComponent().getVectorSize()) {
+        case 1:
+            glUniform1fv(slot, arraySize, fd);
+            break;
+        case 2:
+            glUniform2fv(slot, arraySize, fd);
+            break;
+        case 3:
+            glUniform3fv(slot, arraySize, fd);
+            break;
+        case 4:
+            glUniform4fv(slot, arraySize, fd);
+            break;
+        default:
+            rsAssert(0);
+        }
+    }
+}
+
+void RsdShader::setupTextures(const Context *rsc, RsdShaderCache *sc) {
+    if (mRSProgram->mHal.state.texturesCount == 0) {
+        return;
+    }
+
+    uint32_t numTexturesToBind = mRSProgram->mHal.state.texturesCount;
+    uint32_t numTexturesAvailable = rsc->getMaxFragmentTextures();
+    if (numTexturesToBind >= numTexturesAvailable) {
+        LOGE("Attempting to bind %u textures on shader id %u, but only %u are available",
+             mRSProgram->mHal.state.texturesCount, (uint32_t)this, numTexturesAvailable);
+        rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind more textuers than available");
+        numTexturesToBind = numTexturesAvailable;
+    }
+
+    for (uint32_t ct=0; ct < numTexturesToBind; ct++) {
+        glActiveTexture(GL_TEXTURE0 + ct);
+        if (!mRSProgram->mHal.state.textures[ct].get()) {
+            LOGE("No texture bound for shader id %u, texture unit %u", (uint)this, ct);
+            rsc->setError(RS_ERROR_BAD_SHADER, "No texture bound");
+            continue;
+        }
+
+        GLenum target = (GLenum)mRSProgram->mHal.state.textures[ct]->getGLTarget();
+        if (target != GL_TEXTURE_2D && target != GL_TEXTURE_CUBE_MAP) {
+            LOGE("Attempting to bind unknown texture to shader id %u, texture unit %u", (uint)this, ct);
+            rsc->setError(RS_ERROR_BAD_SHADER, "Non-texture allocation bound to a shader");
+        }
+        glBindTexture(target, mRSProgram->mHal.state.textures[ct]->getTextureID());
+        rsc->checkError("ProgramFragment::setupGL2 tex bind");
+        if (mRSProgram->mHal.state.samplers[ct].get()) {
+            mRSProgram->mHal.state.samplers[ct]->setupGL(rsc, mRSProgram->mHal.state.textures[ct].get());
+        } else {
+            glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+            glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+            glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+            glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+            rsc->checkError("ProgramFragment::setupGL2 tex env");
+        }
+
+        glUniform1i(sc->fragUniformSlot(mTextureUniformIndexStart + ct), ct);
+        rsc->checkError("ProgramFragment::setupGL2 uniforms");
+    }
+
+    glActiveTexture(GL_TEXTURE0);
+    mDirty = false;
+    rsc->checkError("ProgramFragment::setupGL2");
+}
+
+void RsdShader::setupUserConstants(const Context *rsc, RsdShaderCache *sc, bool isFragment) {
+    uint32_t uidx = 0;
+    for (uint32_t ct=0; ct < mRSProgram->mHal.state.constantsCount; ct++) {
+        Allocation *alloc = mRSProgram->mHal.state.constants[ct].get();
+        if (!alloc) {
+            LOGE("Attempting to set constants on shader id %u, but alloc at slot %u is not set", (uint32_t)this, ct);
+            rsc->setError(RS_ERROR_BAD_SHADER, "No constant allocation bound");
+            continue;
+        }
+
+        const uint8_t *data = static_cast<const uint8_t *>(alloc->getPtr());
+        const Element *e = mRSProgram->mHal.state.constantTypes[ct]->getElement();
+        for (uint32_t field=0; field < e->getFieldCount(); field++) {
+            const Element *f = e->getField(field);
+            const char *fieldName = e->getFieldName(field);
+            // If this field is padding, skip it
+            if (fieldName[0] == '#') {
+                continue;
+            }
+
+            uint32_t offset = e->getFieldOffsetBytes(field);
+            const float *fd = reinterpret_cast<const float *>(&data[offset]);
+
+            int32_t slot = -1;
+            uint32_t arraySize = 1;
+            if (!isFragment) {
+                slot = sc->vtxUniformSlot(uidx);
+                arraySize = sc->vtxUniformSize(uidx);
+            } else {
+                slot = sc->fragUniformSlot(uidx);
+                arraySize = sc->fragUniformSize(uidx);
+            }
+            if (rsc->props.mLogShadersUniforms) {
+                LOGV("Uniform  slot=%i, offset=%i, constant=%i, field=%i, uidx=%i, name=%s", slot, offset, ct, field, uidx, fieldName);
+            }
+            uidx ++;
+            if (slot < 0) {
+                continue;
+            }
+
+            if (rsc->props.mLogShadersUniforms) {
+                logUniform(f, fd, arraySize);
+            }
+            setUniform(rsc, f, fd, slot, arraySize);
+        }
+    }
+}
+
+void RsdShader::setup(const android::renderscript::Context *rsc, RsdShaderCache *sc) {
+
+    setupUserConstants(rsc, sc, mType == GL_FRAGMENT_SHADER);
+    setupTextures(rsc, sc);
+}
+
+void RsdShader::initAttribAndUniformArray() {
+    mAttribCount = 0;
+    for (uint32_t ct=0; ct < mRSProgram->mHal.state.inputElementsCount; ct++) {
+        const Element *elem = mRSProgram->mHal.state.inputElements[ct].get();
+        for (uint32_t field=0; field < elem->getFieldCount(); field++) {
+            if (elem->getFieldName(field)[0] != '#') {
+                mAttribCount ++;
+            }
+        }
+    }
+
+    mUniformCount = 0;
+    for (uint32_t ct=0; ct < mRSProgram->mHal.state.constantsCount; ct++) {
+        const Element *elem = mRSProgram->mHal.state.constantTypes[ct]->getElement();
+
+        for (uint32_t field=0; field < elem->getFieldCount(); field++) {
+            if (elem->getFieldName(field)[0] != '#') {
+                mUniformCount ++;
+            }
+        }
+    }
+    mUniformCount += mRSProgram->mHal.state.texturesCount;
+
+    if (mAttribCount) {
+        mAttribNames = new String8[mAttribCount];
+    }
+    if (mUniformCount) {
+        mUniformNames = new String8[mUniformCount];
+        mUniformArraySizes = new uint32_t[mUniformCount];
+    }
+}
+
+void RsdShader::initAddUserElement(const Element *e, String8 *names, uint32_t *arrayLengths, uint32_t *count, const char *prefix) {
+    rsAssert(e->getFieldCount());
+    for (uint32_t ct=0; ct < e->getFieldCount(); ct++) {
+        const Element *ce = e->getField(ct);
+        if (ce->getFieldCount()) {
+            initAddUserElement(ce, names, arrayLengths, count, prefix);
+        } else if (e->getFieldName(ct)[0] != '#') {
+            String8 tmp(prefix);
+            tmp.append(e->getFieldName(ct));
+            names[*count].setTo(tmp.string());
+            if (arrayLengths) {
+                arrayLengths[*count] = e->getFieldArraySize(ct);
+            }
+            (*count)++;
+        }
+    }
+}
diff --git a/libs/rs/driver/rsdShader.h b/libs/rs/driver/rsdShader.h
new file mode 100644
index 0000000..37b1c3d
--- /dev/null
+++ b/libs/rs/driver/rsdShader.h
@@ -0,0 +1,104 @@
+/*
+ * 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 ANDROID_RSD_SHADER_H
+#define ANDROID_RSD_SHADER_H
+
+#include <utils/String8.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+class Element;
+class Context;
+class Program;
+
+}
+}
+
+class RsdShaderCache;
+
+#define RS_SHADER_ATTR "ATTRIB_"
+#define RS_SHADER_UNI "UNI_"
+
+class RsdShader {
+public:
+
+    RsdShader(const android::renderscript::Program *p, uint32_t type,
+               const char * shaderText, uint32_t shaderLength);
+    virtual ~RsdShader();
+
+    bool createShader();
+
+    uint32_t getShaderID() const {return mShaderID;}
+
+    uint32_t getAttribCount() const {return mAttribCount;}
+    uint32_t getUniformCount() const {return mUniformCount;}
+    const android::String8 & getAttribName(uint32_t i) const {return mAttribNames[i];}
+    const android::String8 & getUniformName(uint32_t i) const {return mUniformNames[i];}
+    uint32_t getUniformArraySize(uint32_t i) const {return mUniformArraySizes[i];}
+
+    android::String8 getGLSLInputString() const;
+
+    bool isValid() const {return mIsValid;}
+    void forceDirty() const {mDirty = true;}
+
+    bool loadShader(const android::renderscript::Context *);
+    void setup(const android::renderscript::Context *, RsdShaderCache *sc);
+
+protected:
+
+    const android::renderscript::Program *mRSProgram;
+    bool mIsValid;
+
+    // Applies to vertex and fragment shaders only
+    void appendUserConstants();
+    void setupUserConstants(const android::renderscript::Context *rsc, RsdShaderCache *sc, bool isFragment);
+    void initAddUserElement(const android::renderscript::Element *e, android::String8 *names, uint32_t *arrayLengths, uint32_t *count, const char *prefix);
+    void setupTextures(const android::renderscript::Context *rsc, RsdShaderCache *sc);
+
+    void appendAttributes();
+    void appendTextures();
+
+    void initAttribAndUniformArray();
+
+    mutable bool mDirty;
+    android::String8 mShader;
+    android::String8 mUserShader;
+    uint32_t mShaderID;
+    uint32_t mType;
+
+    uint32_t mTextureCount;
+    uint32_t mAttribCount;
+    uint32_t mUniformCount;
+    android::String8 *mAttribNames;
+    android::String8 *mUniformNames;
+    uint32_t *mUniformArraySizes;
+
+    int32_t mTextureUniformIndexStart;
+
+    void logUniform(const android::renderscript::Element *field, const float *fd, uint32_t arraySize );
+    void setUniform(const android::renderscript::Context *rsc, const android::renderscript::Element *field, const float *fd, int32_t slot, uint32_t arraySize );
+    void initMemberVars();
+    void init();
+};
+
+#endif //ANDROID_RSD_SHADER_H
+
+
+
+
diff --git a/libs/rs/rsShaderCache.cpp b/libs/rs/driver/rsdShaderCache.cpp
similarity index 81%
rename from libs/rs/rsShaderCache.cpp
rename to libs/rs/driver/rsdShaderCache.cpp
index e8d89c2..18a8225 100644
--- a/libs/rs/rsShaderCache.cpp
+++ b/libs/rs/driver/rsdShaderCache.cpp
@@ -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,25 +14,30 @@
  * limitations under the License.
  */
 
-#include "rsContext.h"
-#ifndef ANDROID_RS_SERIALIZE
+#include <rs_hal.h>
+#include <rsContext.h>
+
+#include "rsdShader.h"
+#include "rsdShaderCache.h"
+
 #include <GLES/gl.h>
 #include <GLES2/gl2.h>
-#endif //ANDROID_RS_SERIALIZE
 
 using namespace android;
 using namespace android::renderscript;
 
 
-ShaderCache::ShaderCache() {
+RsdShaderCache::RsdShaderCache() {
     mEntries.setCapacity(16);
+    mVertexDirty = true;
+    mFragmentDirty = true;
 }
 
-ShaderCache::~ShaderCache() {
+RsdShaderCache::~RsdShaderCache() {
     cleanupAll();
 }
 
-void ShaderCache::updateUniformArrayData(Context *rsc, Program *prog, uint32_t linkedID,
+void RsdShaderCache::updateUniformArrayData(const Context *rsc, RsdShader *prog, uint32_t linkedID,
                                          UniformData *data, const char* logTag,
                                          UniformQueryData **uniformList, uint32_t uniListSize) {
 
@@ -54,14 +59,14 @@
     }
 }
 
-void ShaderCache::populateUniformData(Program *prog, uint32_t linkedID, UniformData *data) {
+void RsdShaderCache::populateUniformData(RsdShader *prog, uint32_t linkedID, UniformData *data) {
     for (uint32_t ct=0; ct < prog->getUniformCount(); ct++) {
        data[ct].slot = glGetUniformLocation(linkedID, prog->getUniformName(ct));
        data[ct].arraySize = prog->getUniformArraySize(ct);
     }
 }
 
-bool ShaderCache::hasArrayUniforms(ProgramVertex *vtx, ProgramFragment *frag) {
+bool RsdShaderCache::hasArrayUniforms(RsdShader *vtx, RsdShader *frag) {
     UniformData *data = mCurrent->vtxUniforms;
     for (uint32_t ct=0; ct < vtx->getUniformCount(); ct++) {
         if (data[ct].slot >= 0 && data[ct].arraySize > 1) {
@@ -77,7 +82,31 @@
     return false;
 }
 
-bool ShaderCache::lookup(Context *rsc, ProgramVertex *vtx, ProgramFragment *frag) {
+bool RsdShaderCache::setup(const Context *rsc) {
+    if (!mVertexDirty && !mFragmentDirty) {
+        return true;
+    }
+
+    if (!link(rsc)) {
+        return false;
+    }
+
+    if (mFragmentDirty) {
+        mFragment->setup(rsc, this);
+        mFragmentDirty = false;
+    }
+    if (mVertexDirty) {
+        mVertex->setup(rsc, this);
+        mVertexDirty = false;
+    }
+
+    return true;
+}
+
+bool RsdShaderCache::link(const Context *rsc) {
+
+    RsdShader *vtx = mVertex;
+    RsdShader *frag = mFragment;
     if (!vtx->getShaderID()) {
         vtx->loadShader(rsc);
     }
@@ -89,7 +118,7 @@
     if (!vtx->getShaderID() || !frag->getShaderID()) {
         return false;
     }
-    //LOGV("ShaderCache lookup  vtx %i, frag %i", vtx->getShaderID(), frag->getShaderID());
+    //LOGV("rsdShaderCache lookup  vtx %i, frag %i", vtx->getShaderID(), frag->getShaderID());
     uint32_t entryCount = mEntries.size();
     for (uint32_t ct = 0; ct < entryCount; ct ++) {
         if ((mEntries[ct]->vtx == vtx->getShaderID()) &&
@@ -98,13 +127,13 @@
             //LOGV("SC using program %i", mEntries[ct]->program);
             glUseProgram(mEntries[ct]->program);
             mCurrent = mEntries[ct];
-            //LOGV("ShaderCache hit, using %i", ct);
-            rsc->checkError("ShaderCache::lookup (hit)");
+            //LOGV("RsdShaderCache hit, using %i", ct);
+            rsc->checkError("RsdShaderCache::link (hit)");
             return true;
         }
     }
 
-    //LOGV("ShaderCache miss");
+    //LOGV("RsdShaderCache miss");
     //LOGE("e0 %x", glGetError());
     ProgramEntry *e = new ProgramEntry(vtx->getAttribCount(),
                                        vtx->getUniformCount(),
@@ -120,12 +149,10 @@
         //LOGE("e1 %x", glGetError());
         glAttachShader(pgm, frag->getShaderID());
 
-        if (!vtx->isUserProgram()) {
-            glBindAttribLocation(pgm, 0, "ATTRIB_position");
-            glBindAttribLocation(pgm, 1, "ATTRIB_color");
-            glBindAttribLocation(pgm, 2, "ATTRIB_normal");
-            glBindAttribLocation(pgm, 3, "ATTRIB_texture0");
-        }
+        glBindAttribLocation(pgm, 0, "ATTRIB_position");
+        glBindAttribLocation(pgm, 1, "ATTRIB_color");
+        glBindAttribLocation(pgm, 2, "ATTRIB_normal");
+        glBindAttribLocation(pgm, 3, "ATTRIB_texture0");
 
         //LOGE("e2 %x", glGetError());
         glLinkProgram(pgm);
@@ -203,11 +230,12 @@
 
     //LOGV("SC made program %i", e->program);
     glUseProgram(e->program);
-    rsc->checkError("ShaderCache::lookup (miss)");
+    rsc->checkError("RsdShaderCache::link (miss)");
+
     return true;
 }
 
-int32_t ShaderCache::vtxAttribSlot(const String8 &attrName) const {
+int32_t RsdShaderCache::vtxAttribSlot(const String8 &attrName) const {
     for (uint32_t ct=0; ct < mCurrent->vtxAttrCount; ct++) {
         if (attrName == mCurrent->vtxAttrs[ct].name) {
             return mCurrent->vtxAttrs[ct].slot;
@@ -216,7 +244,7 @@
     return -1;
 }
 
-void ShaderCache::cleanupVertex(uint32_t id) {
+void RsdShaderCache::cleanupVertex(uint32_t id) {
     int32_t numEntries = (int32_t)mEntries.size();
     for (int32_t ct = 0; ct < numEntries; ct ++) {
         if (mEntries[ct]->vtx == id) {
@@ -230,7 +258,7 @@
     }
 }
 
-void ShaderCache::cleanupFragment(uint32_t id) {
+void RsdShaderCache::cleanupFragment(uint32_t id) {
     int32_t numEntries = (int32_t)mEntries.size();
     for (int32_t ct = 0; ct < numEntries; ct ++) {
         if (mEntries[ct]->frag == id) {
@@ -244,7 +272,7 @@
     }
 }
 
-void ShaderCache::cleanupAll() {
+void RsdShaderCache::cleanupAll() {
     for (uint32_t ct=0; ct < mEntries.size(); ct++) {
         glDeleteProgram(mEntries[ct]->program);
         free(mEntries[ct]);
diff --git a/libs/rs/rsShaderCache.h b/libs/rs/driver/rsdShaderCache.h
similarity index 74%
rename from libs/rs/rsShaderCache.h
rename to libs/rs/driver/rsdShaderCache.h
index 3540366..17ee3e8 100644
--- a/libs/rs/rsShaderCache.h
+++ b/libs/rs/driver/rsdShaderCache.h
@@ -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,38 +14,59 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_SHADER_CACHE_H
-#define ANDROID_SHADER_CACHE_H
+#ifndef ANDROID_RSD_SHADER_CACHE_H
+#define ANDROID_RSD_SHADER_CACHE_H
 
-
-#include "rsObjectBase.h"
-#include "rsVertexArray.h"
-
-// ---------------------------------------------------------------------------
 namespace android {
 namespace renderscript {
 
+class Context;
+
+}
+}
+
+#include <utils/String8.h>
+#include <utils/Vector.h>
+class RsdShader;
+
+// ---------------------------------------------------------------------------
 
 // An element is a group of Components that occupies one cell in a structure.
-class ShaderCache {
+class RsdShaderCache {
 public:
-    ShaderCache();
-    virtual ~ShaderCache();
+    RsdShaderCache();
+    virtual ~RsdShaderCache();
 
-    bool lookup(Context *rsc, ProgramVertex *, ProgramFragment *);
+    void setActiveVertex(RsdShader *pv) {
+        mVertexDirty = true;
+        mVertex = pv;
+    }
+
+    void setActiveFragment(RsdShader *pf) {
+        mFragmentDirty = true;
+        mFragment = pf;
+    }
+
+    bool setup(const android::renderscript::Context *rsc);
 
     void cleanupVertex(uint32_t id);
     void cleanupFragment(uint32_t id);
 
     void cleanupAll();
 
-    int32_t vtxAttribSlot(const String8 &attrName) const;
+    int32_t vtxAttribSlot(const android::String8 &attrName) const;
     int32_t vtxUniformSlot(uint32_t a) const {return mCurrent->vtxUniforms[a].slot;}
     uint32_t vtxUniformSize(uint32_t a) const {return mCurrent->vtxUniforms[a].arraySize;}
     int32_t fragUniformSlot(uint32_t a) const {return mCurrent->fragUniforms[a].slot;}
     uint32_t fragUniformSize(uint32_t a) const {return mCurrent->fragUniforms[a].arraySize;}
 
 protected:
+    bool link(const android::renderscript::Context *rsc);
+    bool mFragmentDirty;
+    bool mVertexDirty;
+    RsdShader *mVertex;
+    RsdShader *mFragment;
+
     struct UniformQueryData {
         char *name;
         uint32_t nameLength;
@@ -111,21 +132,19 @@
         UniformData *vtxUniforms;
         UniformData *fragUniforms;
     };
-    Vector<ProgramEntry*> mEntries;
+    android::Vector<ProgramEntry*> mEntries;
     ProgramEntry *mCurrent;
 
-    bool hasArrayUniforms(ProgramVertex *vtx, ProgramFragment *frag);
-    void populateUniformData(Program *prog, uint32_t linkedID, UniformData *data);
-    void updateUniformArrayData(Context *rsc, Program *prog, uint32_t linkedID,
+    bool hasArrayUniforms(RsdShader *vtx, RsdShader *frag);
+    void populateUniformData(RsdShader *prog, uint32_t linkedID, UniformData *data);
+    void updateUniformArrayData(const android::renderscript::Context *rsc,
+                                RsdShader *prog, uint32_t linkedID,
                                 UniformData *data, const char* logTag,
                                 UniformQueryData **uniformList, uint32_t uniListSize);
 };
 
 
-
-}
-}
-#endif //ANDROID_SHADER_CACHE_H
+#endif //ANDROID_RSD_SHADER_CACHE_H
 
 
 
diff --git a/libs/rs/rsVertexArray.cpp b/libs/rs/driver/rsdVertexArray.cpp
similarity index 73%
rename from libs/rs/rsVertexArray.cpp
rename to libs/rs/driver/rsdVertexArray.cpp
index 354ee89..d0a5a54 100644
--- a/libs/rs/rsVertexArray.cpp
+++ b/libs/rs/driver/rsdVertexArray.cpp
@@ -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,28 +14,32 @@
  * limitations under the License.
  */
 
-#include "rsContext.h"
-#ifndef ANDROID_RS_SERIALIZE
+#include <rs_hal.h>
+#include <rsContext.h>
+
 #include <GLES/gl.h>
 #include <GLES2/gl2.h>
-#endif
+
+#include "rsdCore.h"
+#include "rsdVertexArray.h"
+#include "rsdShaderCache.h"
 
 using namespace android;
 using namespace android::renderscript;
 
-VertexArray::VertexArray(const Attrib *attribs, uint32_t numAttribs) {
+RsdVertexArray::RsdVertexArray(const Attrib *attribs, uint32_t numAttribs) {
     mAttribs = attribs;
     mCount = numAttribs;
 }
 
-VertexArray::~VertexArray() {
+RsdVertexArray::~RsdVertexArray() {
 }
 
-VertexArray::Attrib::Attrib() {
+RsdVertexArray::Attrib::Attrib() {
     clear();
 }
 
-void VertexArray::Attrib::clear() {
+void RsdVertexArray::Attrib::clear() {
     buffer = 0;
     offset = 0;
     type = 0;
@@ -46,7 +50,7 @@
     name.setTo("");
 }
 
-void VertexArray::Attrib::set(uint32_t type, uint32_t size, uint32_t stride,
+void RsdVertexArray::Attrib::set(uint32_t type, uint32_t size, uint32_t stride,
                               bool normalized, uint32_t offset,
                               const char *name) {
     clear();
@@ -58,7 +62,7 @@
     this->name.setTo(name);
 }
 
-void VertexArray::logAttrib(uint32_t idx, uint32_t slot) const {
+void RsdVertexArray::logAttrib(uint32_t idx, uint32_t slot) const {
     if (idx == 0) {
         LOGV("Starting vertex attribute binding");
     }
@@ -74,11 +78,15 @@
          mAttribs[idx].offset);
 }
 
-void VertexArray::setupGL2(const Context *rsc,
-                           class VertexArrayState *state,
-                           ShaderCache *sc) const {
-    rsc->checkError("VertexArray::setupGL2 start");
+void RsdVertexArray::setupGL2(const Context *rsc) const {
+
+    RsdHal *dc = (RsdHal *)rsc->mHal.drv;
+    RsdVertexArrayState *state = dc->gl.vertexArrayState;
+    RsdShaderCache *sc = dc->gl.shaderCache;
+
+    rsc->checkError("RsdVertexArray::setupGL2 start");
     uint32_t maxAttrs = state->mAttrsEnabledSize;
+
     for (uint32_t ct=1; ct < maxAttrs; ct++) {
         if(state->mAttrsEnabled[ct]) {
             glDisableVertexAttribArray(ct);
@@ -86,7 +94,7 @@
         }
     }
 
-    rsc->checkError("VertexArray::setupGL2 disabled");
+    rsc->checkError("RsdVertexArray::setupGL2 disabled");
     for (uint32_t ct=0; ct < mCount; ct++) {
         int32_t slot = sc->vtxAttribSlot(mAttribs[ct].name);
         if (rsc->props.mLogShadersAttr) {
@@ -105,22 +113,22 @@
                               mAttribs[ct].stride,
                               mAttribs[ct].ptr + mAttribs[ct].offset);
     }
-    rsc->checkError("VertexArray::setupGL2 done");
+    rsc->checkError("RsdVertexArray::setupGL2 done");
 }
 ////////////////////////////////////////////
-VertexArrayState::VertexArrayState() {
+RsdVertexArrayState::RsdVertexArrayState() {
     mAttrsEnabled = NULL;
     mAttrsEnabledSize = 0;
 }
 
-VertexArrayState::~VertexArrayState() {
+RsdVertexArrayState::~RsdVertexArrayState() {
     if (mAttrsEnabled) {
         delete[] mAttrsEnabled;
         mAttrsEnabled = NULL;
     }
 }
-void VertexArrayState::init(Context *rsc) {
-    mAttrsEnabledSize = rsc->getMaxVertexAttributes();
+void RsdVertexArrayState::init(uint32_t maxAttrs) {
+    mAttrsEnabledSize = maxAttrs;
     mAttrsEnabled = new bool[mAttrsEnabledSize];
     for (uint32_t ct = 0; ct < mAttrsEnabledSize; ct++) {
         mAttrsEnabled[ct] = false;
diff --git a/libs/rs/rsVertexArray.h b/libs/rs/driver/rsdVertexArray.h
similarity index 69%
rename from libs/rs/rsVertexArray.h
rename to libs/rs/driver/rsdVertexArray.h
index 45d9e82..925a6ae 100644
--- a/libs/rs/rsVertexArray.h
+++ b/libs/rs/driver/rsdVertexArray.h
@@ -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,20 +14,21 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_VERTEX_ARRAY_H
-#define ANDROID_VERTEX_ARRAY_H
+#ifndef ANDROID_RSD_VERTEX_ARRAY_H
+#define ANDROID_RSD_VERTEX_ARRAY_H
 
-
-#include "rsObjectBase.h"
-
-// ---------------------------------------------------------------------------
 namespace android {
 namespace renderscript {
 
-class ShaderCache;
+class Context;
+
+}
+}
+
+#include <utils/String8.h>
 
 // An element is a group of Components that occupies one cell in a structure.
-class VertexArray {
+class RsdVertexArray {
 public:
     class Attrib {
     public:
@@ -38,17 +39,17 @@
         uint32_t size;
         uint32_t stride;
         bool normalized;
-        String8 name;
+        android::String8 name;
 
         Attrib();
         void clear();
         void set(uint32_t type, uint32_t size, uint32_t stride, bool normalized, uint32_t offset, const char *name);
     };
 
-    VertexArray(const Attrib *attribs, uint32_t numAttribs);
-    virtual ~VertexArray();
+    RsdVertexArray(const Attrib *attribs, uint32_t numAttribs);
+    virtual ~RsdVertexArray();
 
-    void setupGL2(const Context *rsc, class VertexArrayState *, ShaderCache *) const;
+    void setupGL2(const android::renderscript::Context *rsc) const;
     void logAttrib(uint32_t idx, uint32_t slot) const;
 
 protected:
@@ -61,20 +62,18 @@
 };
 
 
-class VertexArrayState {
+class RsdVertexArrayState {
 public:
-    VertexArrayState();
-    ~VertexArrayState();
-    void init(Context *);
+    RsdVertexArrayState();
+    ~RsdVertexArrayState();
+    void init(uint32_t maxAttrs);
 
     bool *mAttrsEnabled;
     uint32_t mAttrsEnabledSize;
 };
 
 
-}
-}
-#endif //ANDROID_VERTEX_ARRAY_H
+#endif //ANDROID_RSD_VERTEX_ARRAY_H
 
 
 
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
index 6310cf6..0c4e1ed 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -66,7 +66,7 @@
     direct
 }
 
-aTypeCreate {
+TypeCreate {
     direct
     param RsElement e
     param uint32_t dimX
@@ -77,7 +77,7 @@
     ret RsType
 }
 
-aAllocationCreateTyped {
+AllocationCreateTyped {
     direct
     param RsType vtype
     param RsAllocationMipmapControl mips
@@ -85,7 +85,7 @@
     ret RsAllocation
 }
 
-aAllocationCreateFromBitmap {
+AllocationCreateFromBitmap {
     direct
     param RsType vtype
     param RsAllocationMipmapControl mips
@@ -94,7 +94,7 @@
     ret RsAllocation
 }
 
-aAllocationCubeCreateFromBitmap {
+AllocationCubeCreateFromBitmap {
     direct
     param RsType vtype
     param RsAllocationMipmapControl mips
diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
index 743b2c4..b5f6f56 100644
--- a/libs/rs/rsAllocation.cpp
+++ b/libs/rs/rsAllocation.cpp
@@ -649,11 +649,12 @@
 //
 #ifndef ANDROID_RS_SERIALIZE
 
-static void rsaAllocationGenerateScriptMips(RsContext con, RsAllocation va);
 
 namespace android {
 namespace renderscript {
 
+static void AllocationGenerateScriptMips(RsContext con, RsAllocation va);
+
 void rsi_AllocationUploadToTexture(Context *rsc, RsAllocation va, bool genmip, uint32_t baseMipLevel) {
     Allocation *alloc = static_cast<Allocation *>(va);
     alloc->deferredUploadToTexture(rsc);
@@ -740,7 +741,7 @@
 
 void rsi_AllocationGenerateMipmaps(Context *rsc, RsAllocation va) {
     Allocation *texAlloc = static_cast<Allocation *>(va);
-    rsaAllocationGenerateScriptMips(rsc, texAlloc);
+    AllocationGenerateScriptMips(rsc, texAlloc);
 }
 
 void rsi_AllocationCopyToBitmap(Context *rsc, RsAllocation va, void *data, size_t dataLen) {
@@ -795,10 +796,7 @@
     a->resize2D(rsc, dimX, dimY);
 }
 
-}
-}
-
-static void rsaAllocationGenerateScriptMips(RsContext con, RsAllocation va) {
+static void AllocationGenerateScriptMips(RsContext con, RsAllocation va) {
     Context *rsc = static_cast<Context *>(con);
     Allocation *texAlloc = static_cast<Allocation *>(va);
     uint32_t numFaces = texAlloc->getType()->getDimFaces() ? 6 : 1;
@@ -815,29 +813,20 @@
     }
 }
 
-const void * rsaAllocationGetType(RsContext con, RsAllocation va) {
-    Allocation *a = static_cast<Allocation *>(va);
-    a->getType()->incUserRef();
-
-    return a->getType();
-}
-
-RsAllocation rsaAllocationCreateTyped(RsContext con, RsType vtype,
-                                      RsAllocationMipmapControl mips,
-                                      uint32_t usages) {
-    Context *rsc = static_cast<Context *>(con);
+RsAllocation rsi_AllocationCreateTyped(Context *rsc, RsType vtype,
+                                       RsAllocationMipmapControl mips,
+                                       uint32_t usages) {
     Allocation * alloc = new Allocation(rsc, static_cast<Type *>(vtype), usages, mips);
     alloc->incUserRef();
     return alloc;
 }
 
-RsAllocation rsaAllocationCreateFromBitmap(RsContext con, RsType vtype,
-                                           RsAllocationMipmapControl mips,
-                                           const void *data, size_t data_length, uint32_t usages) {
-    Context *rsc = static_cast<Context *>(con);
+RsAllocation rsi_AllocationCreateFromBitmap(Context *rsc, RsType vtype,
+                                            RsAllocationMipmapControl mips,
+                                            const void *data, size_t data_length, uint32_t usages) {
     Type *t = static_cast<Type *>(vtype);
 
-    RsAllocation vTexAlloc = rsaAllocationCreateTyped(rsc, vtype, mips, usages);
+    RsAllocation vTexAlloc = rsi_AllocationCreateTyped(rsc, vtype, mips, usages);
     Allocation *texAlloc = static_cast<Allocation *>(vTexAlloc);
     if (texAlloc == NULL) {
         LOGE("Memory allocation failure");
@@ -846,23 +835,22 @@
 
     memcpy(texAlloc->getPtr(), data, t->getDimX() * t->getDimY() * t->getElementSizeBytes());
     if (mips == RS_ALLOCATION_MIPMAP_FULL) {
-        rsaAllocationGenerateScriptMips(rsc, texAlloc);
+        AllocationGenerateScriptMips(rsc, texAlloc);
     }
 
     texAlloc->deferredUploadToTexture(rsc);
     return texAlloc;
 }
 
-RsAllocation rsaAllocationCubeCreateFromBitmap(RsContext con, RsType vtype,
-                                               RsAllocationMipmapControl mips,
-                                               const void *data, size_t data_length, uint32_t usages) {
-    Context *rsc = static_cast<Context *>(con);
+RsAllocation rsi_AllocationCubeCreateFromBitmap(Context *rsc, RsType vtype,
+                                                RsAllocationMipmapControl mips,
+                                                const void *data, size_t data_length, uint32_t usages) {
     Type *t = static_cast<Type *>(vtype);
 
     // Cubemap allocation's faces should be Width by Width each.
     // Source data should have 6 * Width by Width pixels
     // Error checking is done in the java layer
-    RsAllocation vTexAlloc = rsaAllocationCreateTyped(rsc, t, mips, usages);
+    RsAllocation vTexAlloc = rsi_AllocationCreateTyped(rsc, vtype, mips, usages);
     Allocation *texAlloc = static_cast<Allocation *>(vTexAlloc);
     if (texAlloc == NULL) {
         LOGE("Memory allocation failure");
@@ -887,11 +875,21 @@
     }
 
     if (mips == RS_ALLOCATION_MIPMAP_FULL) {
-        rsaAllocationGenerateScriptMips(rsc, texAlloc);
+        AllocationGenerateScriptMips(rsc, texAlloc);
     }
 
     texAlloc->deferredUploadToTexture(rsc);
     return texAlloc;
 }
 
+}
+}
+
+const void * rsaAllocationGetType(RsContext con, RsAllocation va) {
+    Allocation *a = static_cast<Allocation *>(va);
+    a->getType()->incUserRef();
+
+    return a->getType();
+}
+
 #endif //ANDROID_RS_SERIALIZE
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index 50f5f55..6d63f67 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -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.
@@ -215,15 +215,11 @@
 }
 
 bool Context::setupCheck() {
-    if (!mShaderCache.lookup(this, mVertex.get(), mFragment.get())) {
-        LOGE("Context::setupCheck() 1 fail");
-        return false;
-    }
 
     mFragmentStore->setup(this, &mStateFragmentStore);
-    mFragment->setupGL2(this, &mStateFragment, &mShaderCache);
+    mFragment->setupGL2(this, &mStateFragment);
     mRaster->setup(this, &mStateRaster);
-    mVertex->setupGL2(this, &mStateVertex, &mShaderCache);
+    mVertex->setupGL2(this, &mStateVertex);
     mFBOCache.setupGL2(this);
     return true;
 }
@@ -295,7 +291,6 @@
         rsc->setProgramStore(NULL);
         rsc->mStateFont.init(rsc);
         rsc->setFont(NULL);
-        rsc->mStateVertexArray.init(rsc);
     }
 
     rsc->mRunning = true;
@@ -356,7 +351,6 @@
          mStateFragment.deinit(this);
          mStateFragmentStore.deinit(this);
          mStateFont.deinit(this);
-         mShaderCache.cleanupAll();
     }
     //LOGV("destroyWorkerThreadResources 2");
     mExit = true;
@@ -598,7 +592,7 @@
     *subID = d[0];
 
     //LOGE("getMessageToClient  %i %i", commandID, *subID);
-    if (bufferLen >= bytesData) {
+    if (bufferLen >= (*receiveLen)) {
         memcpy(data, d+1, *receiveLen);
         mIO.mToClient.next();
         return (RsMessageToClientType)commandID;
@@ -740,25 +734,21 @@
     rsc->destroyWorkerThreadResources();;
 }
 
-}
-}
-
-void rsContextDestroy(RsContext vcon) {
-    LOGV("rsContextDestroy %p", vcon);
-    Context *rsc = static_cast<Context *>(vcon);
+void rsi_ContextDestroy(Context *rsc) {
+    LOGV("rsContextDestroy %p", rsc);
     rsContextDestroyWorker(rsc);
     delete rsc;
-    LOGV("rsContextDestroy 2 %p", vcon);
+    LOGV("rsContextDestroy 2 %p", rsc);
 }
 
-RsContext rsContextCreate(RsDevice vdev, uint32_t version) {
+RsContext rsi_ContextCreate(RsDevice vdev, uint32_t version) {
     LOGV("rsContextCreate %p", vdev);
     Device * dev = static_cast<Device *>(vdev);
     Context *rsc = Context::createContext(dev, NULL);
     return rsc;
 }
 
-RsContext rsContextCreateGL(RsDevice vdev, uint32_t version,
+RsContext rsi_ContextCreateGL(RsDevice vdev, uint32_t version,
                             RsSurfaceConfig sc, uint32_t dpi) {
     LOGV("rsContextCreateGL %p", vdev);
     Device * dev = static_cast<Device *>(vdev);
@@ -768,32 +758,31 @@
     return rsc;
 }
 
-RsMessageToClientType rsContextPeekMessage(RsContext vrsc,
+RsMessageToClientType rsi_ContextPeekMessage(Context *rsc,
                                            size_t * receiveLen, size_t receiveLen_length,
                                            uint32_t * subID, size_t subID_length, bool wait) {
-    Context * rsc = static_cast<Context *>(vrsc);
     return rsc->peekMessageToClient(receiveLen, subID, wait);
 }
 
-RsMessageToClientType rsContextGetMessage(RsContext vrsc, void * data, size_t data_length,
+RsMessageToClientType rsi_ContextGetMessage(Context *rsc, void * data, size_t data_length,
                                           size_t * receiveLen, size_t receiveLen_length,
                                           uint32_t * subID, size_t subID_length, bool wait) {
-    Context * rsc = static_cast<Context *>(vrsc);
     rsAssert(subID_length == sizeof(uint32_t));
     rsAssert(receiveLen_length == sizeof(size_t));
     return rsc->getMessageToClient(data, receiveLen, subID, data_length, wait);
 }
 
-void rsContextInitToClient(RsContext vrsc) {
-    Context * rsc = static_cast<Context *>(vrsc);
+void rsi_ContextInitToClient(Context *rsc) {
     rsc->initToClient();
 }
 
-void rsContextDeinitToClient(RsContext vrsc) {
-    Context * rsc = static_cast<Context *>(vrsc);
+void rsi_ContextDeinitToClient(Context *rsc) {
     rsc->deinitToClient();
 }
 
+}
+}
+
 // Only to be called at a3d load time, before object is visible to user
 // not thread safe
 void rsaGetName(RsContext con, void * obj, const char **name) {
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index df85a6b..107f639 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -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.
@@ -37,9 +37,7 @@
 #include "rsProgramStore.h"
 #include "rsProgramRaster.h"
 #include "rsProgramVertex.h"
-#include "rsShaderCache.h"
 #include "rsFBOCache.h"
-#include "rsVertexArray.h"
 
 #include "rsgApiStructs.h"
 #include "rsLocklessFifo.h"
@@ -111,11 +109,9 @@
     ProgramStoreState mStateFragmentStore;
     ProgramRasterState mStateRaster;
     ProgramVertexState mStateVertex;
-    VertexArrayState mStateVertexArray;
     FontState mStateFont;
 
     ScriptCState mScriptC;
-    ShaderCache mShaderCache;
     FBOCache mFBOCache;
 
     void swapBuffers();
diff --git a/libs/rs/rsDevice.cpp b/libs/rs/rsDevice.cpp
index d7d03f6..849fd98 100644
--- a/libs/rs/rsDevice.cpp
+++ b/libs/rs/rsDevice.cpp
@@ -40,17 +40,20 @@
     }
 }
 
-RsDevice rsDeviceCreate() {
+namespace android {
+namespace renderscript {
+
+RsDevice rsi_DeviceCreate() {
     Device * d = new Device();
     return d;
 }
 
-void rsDeviceDestroy(RsDevice dev) {
+void rsi_DeviceDestroy(RsDevice dev) {
     Device * d = static_cast<Device *>(dev);
     delete d;
 }
 
-void rsDeviceSetConfig(RsDevice dev, RsDeviceParam p, int32_t value) {
+void rsi_DeviceSetConfig(RsDevice dev, RsDeviceParam p, int32_t value) {
     Device * d = static_cast<Device *>(dev);
     if (p == RS_DEVICE_PARAM_FORCE_SOFTWARE_GL) {
         d->mForceSW = value != 0;
@@ -59,3 +62,5 @@
     rsAssert(0);
 }
 
+}
+}
diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp
index b7b85b6..5e47ddb 100644
--- a/libs/rs/rsFont.cpp
+++ b/libs/rs/rsFont.cpp
@@ -25,11 +25,6 @@
 #include FT_FREETYPE_H
 #include FT_BITMAP_H
 
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
 using namespace android;
 using namespace android::renderscript;
 
@@ -457,7 +452,7 @@
 
     // This will dirty the texture and the shader so next time
     // we draw it will upload the data
-    mTextTexture->syncAll(mRSC, RS_ALLOCATION_USAGE_SCRIPT);
+    mTextTexture->deferredUploadToTexture(mRSC);
     mFontShaderF->bindTexture(mRSC, 0, mTextTexture.get());
 
     // Some debug code
@@ -568,7 +563,6 @@
     }
 
     indexAlloc->deferredUploadToBufferObject(mRSC);
-    mIndexBuffer.set(indexAlloc);
 
     const Element *posElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 3);
     const Element *texElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 2);
@@ -585,7 +579,10 @@
     Allocation *vertexAlloc = new Allocation(mRSC, vertexDataType, RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_VERTEX);
     mTextMeshPtr = (float*)vertexAlloc->getPtr();
 
-    mVertexArray.set(vertexAlloc);
+    mMesh.set(new Mesh(mRSC, 1, 1));
+    mMesh->setVertexBuffer(vertexAlloc, 0);
+    mMesh->setPrimitive(indexAlloc, RS_PRIMITIVE_TRIANGLE, 0);
+    mMesh->init();
 }
 
 // We don't want to allocate anything unless we actually draw text
@@ -625,18 +622,7 @@
         return;
     }
 
-    float *vtx = (float*)mVertexArray->getPtr();
-    float *tex = vtx + 3;
-
-    VertexArray::Attrib attribs[2];
-    attribs[0].set(GL_FLOAT, 3, 20, false, (uint32_t)vtx, "ATTRIB_position");
-    attribs[1].set(GL_FLOAT, 2, 20, false, (uint32_t)tex, "ATTRIB_texture0");
-    VertexArray va(attribs, 2);
-    va.setupGL2(mRSC, &mRSC->mStateVertexArray, &mRSC->mShaderCache);
-
-    mIndexBuffer->uploadCheck(mRSC);
-    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer->getBufferObjectID());
-    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, (uint16_t *)(0));
+    mMesh->renderPrimitiveRange(mRSC, 0, 0, mCurrentQuadIndex * 6);
 }
 
 void FontState::appendMeshQuad(float x1, float y1, float z1,
@@ -787,8 +773,7 @@
 
     mFontShaderFConstant.clear();
 
-    mIndexBuffer.clear();
-    mVertexArray.clear();
+    mMesh.clear();
 
     mFontShaderF.clear();
     mFontSampler.clear();
diff --git a/libs/rs/rsFont.h b/libs/rs/rsFont.h
index 91a5da9..d18c0d9 100644
--- a/libs/rs/rsFont.h
+++ b/libs/rs/rsFont.h
@@ -230,9 +230,7 @@
     uint32_t mMaxNumberOfQuads;
 
     void initVertexArrayBuffers();
-    ObjectBaseRef<Allocation> mIndexBuffer;
-    ObjectBaseRef<Allocation> mVertexArray;
-
+    ObjectBaseRef<Mesh> mMesh;
 
     bool mInitialized;
 
diff --git a/libs/rs/rsMesh.cpp b/libs/rs/rsMesh.cpp
index e29c800..ed29063 100644
--- a/libs/rs/rsMesh.cpp
+++ b/libs/rs/rsMesh.cpp
@@ -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.
@@ -15,46 +15,53 @@
  */
 
 #include "rsContext.h"
-#ifndef ANDROID_RS_SERIALIZE
-#include <GLES/gl.h>
-#include <GLES2/gl2.h>
-#include <GLES/glext.h>
-#endif
 
 using namespace android;
 using namespace android::renderscript;
 
 Mesh::Mesh(Context *rsc) : ObjectBase(rsc) {
-    mPrimitives = NULL;
-    mPrimitivesCount = 0;
-    mVertexBuffers = NULL;
-    mVertexBufferCount = 0;
+    mHal.drv = NULL;
+    mHal.state.primitives = NULL;
+    mHal.state.primitivesCount = 0;
+    mHal.state.vertexBuffers = NULL;
+    mHal.state.vertexBuffersCount = 0;
+    mInitialized = false;
+}
 
-#ifndef ANDROID_RS_SERIALIZE
-    mAttribs = NULL;
-    mAttribAllocationIndex = NULL;
-
-    mAttribCount = 0;
-#endif
+Mesh::Mesh(Context *rsc,
+           uint32_t vertexBuffersCount,
+           uint32_t primitivesCount) : ObjectBase(rsc) {
+    mHal.drv = NULL;
+    mHal.state.primitivesCount = primitivesCount;
+    mHal.state.primitives = new Primitive_t *[mHal.state.primitivesCount];
+    for (uint32_t i = 0; i < mHal.state.primitivesCount; i ++) {
+        mHal.state.primitives[i] = new Primitive_t;
+    }
+    mHal.state.vertexBuffersCount = vertexBuffersCount;
+    mHal.state.vertexBuffers = new ObjectBaseRef<Allocation>[mHal.state.vertexBuffersCount];
 }
 
 Mesh::~Mesh() {
-    if (mVertexBuffers) {
-        delete[] mVertexBuffers;
-    }
-
-    if (mPrimitives) {
-        for (uint32_t i = 0; i < mPrimitivesCount; i ++) {
-            delete mPrimitives[i];
-        }
-        delete[] mPrimitives;
-    }
-
 #ifndef ANDROID_RS_SERIALIZE
-    if (mAttribs) {
-        delete[] mAttribs;
-        delete[] mAttribAllocationIndex;
+    mRSC->mHal.funcs.mesh.destroy(mRSC, this);
+#endif
+
+    if (mHal.state.vertexBuffers) {
+        delete[] mHal.state.vertexBuffers;
     }
+
+    if (mHal.state.primitives) {
+        for (uint32_t i = 0; i < mHal.state.primitivesCount; i ++) {
+            mHal.state.primitives[i]->mIndexBuffer.clear();
+            delete mHal.state.primitives[i];
+        }
+        delete[] mHal.state.primitives;
+    }
+}
+
+void Mesh::init() {
+#ifndef ANDROID_RS_SERIALIZE
+    mRSC->mHal.funcs.mesh.init(mRSC, this);
 #endif
 }
 
@@ -66,15 +73,15 @@
     stream->addString(&name);
 
     // Store number of vertex streams
-    stream->addU32(mVertexBufferCount);
-    for (uint32_t vCount = 0; vCount < mVertexBufferCount; vCount ++) {
-        mVertexBuffers[vCount]->serialize(stream);
+    stream->addU32(mHal.state.vertexBuffersCount);
+    for (uint32_t vCount = 0; vCount < mHal.state.vertexBuffersCount; vCount ++) {
+        mHal.state.vertexBuffers[vCount]->serialize(stream);
     }
 
-    stream->addU32(mPrimitivesCount);
+    stream->addU32(mHal.state.primitivesCount);
     // Store the primitives
-    for (uint32_t pCount = 0; pCount < mPrimitivesCount; pCount ++) {
-        Primitive_t * prim = mPrimitives[pCount];
+    for (uint32_t pCount = 0; pCount < mHal.state.primitivesCount; pCount ++) {
+        Primitive_t * prim = mHal.state.primitives[pCount];
 
         stream->addU8((uint8_t)prim->mPrimitive);
 
@@ -95,45 +102,60 @@
         return NULL;
     }
 
-    Mesh * mesh = new Mesh(rsc);
-
     String8 name;
     stream->loadString(&name);
-    mesh->setName(name.string(), name.size());
 
-    mesh->mVertexBufferCount = stream->loadU32();
-    if (mesh->mVertexBufferCount) {
-        mesh->mVertexBuffers = new ObjectBaseRef<Allocation>[mesh->mVertexBufferCount];
+    uint32_t vertexBuffersCount = stream->loadU32();
+    ObjectBaseRef<Allocation> *vertexBuffers = NULL;
+    if (vertexBuffersCount) {
+        vertexBuffers = new ObjectBaseRef<Allocation>[vertexBuffersCount];
 
-        for (uint32_t vCount = 0; vCount < mesh->mVertexBufferCount; vCount ++) {
+        for (uint32_t vCount = 0; vCount < vertexBuffersCount; vCount ++) {
             Allocation *vertexAlloc = Allocation::createFromStream(rsc, stream);
-            mesh->mVertexBuffers[vCount].set(vertexAlloc);
+            vertexBuffers[vCount].set(vertexAlloc);
         }
     }
 
-    mesh->mPrimitivesCount = stream->loadU32();
-    if (mesh->mPrimitivesCount) {
-        mesh->mPrimitives = new Primitive_t *[mesh->mPrimitivesCount];
+    uint32_t primitivesCount = stream->loadU32();
+    ObjectBaseRef<Allocation> *indexBuffers = NULL;
+    RsPrimitive *primitives = NULL;
+    if (primitivesCount) {
+        indexBuffers = new ObjectBaseRef<Allocation>[primitivesCount];
+        primitives = new RsPrimitive[primitivesCount];
 
         // load all primitives
-        for (uint32_t pCount = 0; pCount < mesh->mPrimitivesCount; pCount ++) {
-            Primitive_t * prim = new Primitive_t;
-            mesh->mPrimitives[pCount] = prim;
-
-            prim->mPrimitive = (RsPrimitive)stream->loadU8();
+        for (uint32_t pCount = 0; pCount < primitivesCount; pCount ++) {
+            primitives[pCount] = (RsPrimitive)stream->loadU8();
 
             // Check to see if the index buffer was stored
             uint32_t isIndexPresent = stream->loadU32();
             if (isIndexPresent) {
                 Allocation *indexAlloc = Allocation::createFromStream(rsc, stream);
-                prim->mIndexBuffer.set(indexAlloc);
+                indexBuffers[pCount].set(indexAlloc);
             }
         }
     }
 
+    Mesh *mesh = new Mesh(rsc, vertexBuffersCount, primitivesCount);
+    mesh->setName(name.string(), name.size());
+    for (uint32_t vCount = 0; vCount < vertexBuffersCount; vCount ++) {
+        mesh->setVertexBuffer(vertexBuffers[vCount].get(), vCount);
+    }
+    for (uint32_t pCount = 0; pCount < primitivesCount; pCount ++) {
+        mesh->setPrimitive(indexBuffers[pCount].get(), primitives[pCount], pCount);
+    }
+
+    // Cleanup
+    if (vertexBuffersCount) {
+        delete[] vertexBuffers;
+    }
+    if (primitivesCount) {
+        delete[] indexBuffers;
+        delete[] primitives;
+    }
+
 #ifndef ANDROID_RS_SERIALIZE
-    mesh->updateGLPrimitives();
-    mesh->initVertexAttribs();
+    mesh->init();
     mesh->uploadAll(rsc);
 #endif
     return mesh;
@@ -141,167 +163,58 @@
 
 #ifndef ANDROID_RS_SERIALIZE
 
-bool Mesh::isValidGLComponent(const Element *elem, uint32_t fieldIdx) {
-    // Do not create attribs for padding
-    if (elem->getFieldName(fieldIdx)[0] == '#') {
-        return false;
-    }
-
-    // Only GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_FIXED, GL_FLOAT are accepted.
-    // Filter rs types accordingly
-    RsDataType dt = elem->getField(fieldIdx)->getComponent().getType();
-    if (dt != RS_TYPE_FLOAT_32 && dt != RS_TYPE_UNSIGNED_8 &&
-       dt != RS_TYPE_UNSIGNED_16 && dt != RS_TYPE_SIGNED_8 &&
-       dt != RS_TYPE_SIGNED_16) {
-        return false;
-    }
-
-    // Now make sure they are not arrays
-    uint32_t arraySize = elem->getFieldArraySize(fieldIdx);
-    if (arraySize != 1) {
-        return false;
-    }
-
-    return true;
-}
-
-void Mesh::initVertexAttribs() {
-    // Count the number of gl attrs to initialize
-    mAttribCount = 0;
-    for (uint32_t ct=0; ct < mVertexBufferCount; ct++) {
-        const Element *elem = mVertexBuffers[ct]->getType()->getElement();
-        for (uint32_t ct=0; ct < elem->getFieldCount(); ct++) {
-            if (isValidGLComponent(elem, ct)) {
-                mAttribCount ++;
-            }
-        }
-    }
-
-    if (mAttribs) {
-        delete [] mAttribs;
-        delete [] mAttribAllocationIndex;
-        mAttribs = NULL;
-        mAttribAllocationIndex = NULL;
-    }
-    if (!mAttribCount) {
-        return;
-    }
-
-    mAttribs = new VertexArray::Attrib[mAttribCount];
-    mAttribAllocationIndex = new uint32_t[mAttribCount];
-
-    uint32_t userNum = 0;
-    for (uint32_t ct=0; ct < mVertexBufferCount; ct++) {
-        const Element *elem = mVertexBuffers[ct]->getType()->getElement();
-        uint32_t stride = elem->getSizeBytes();
-        for (uint32_t fieldI=0; fieldI < elem->getFieldCount(); fieldI++) {
-            const Component &c = elem->getField(fieldI)->getComponent();
-
-            if (!isValidGLComponent(elem, fieldI)) {
-                continue;
-            }
-
-            mAttribs[userNum].size = c.getVectorSize();
-            mAttribs[userNum].offset = elem->getFieldOffsetBytes(fieldI);
-            mAttribs[userNum].type = c.getGLType();
-            mAttribs[userNum].normalized = c.getType() != RS_TYPE_FLOAT_32;//c.getIsNormalized();
-            mAttribs[userNum].stride = stride;
-            String8 tmp(RS_SHADER_ATTR);
-            tmp.append(elem->getFieldName(fieldI));
-            mAttribs[userNum].name.setTo(tmp.string());
-
-            // Remember which allocation this attribute came from
-            mAttribAllocationIndex[userNum] = ct;
-            userNum ++;
-        }
-    }
-}
-
 void Mesh::render(Context *rsc) const {
-    for (uint32_t ct = 0; ct < mPrimitivesCount; ct ++) {
+    for (uint32_t ct = 0; ct < mHal.state.primitivesCount; ct ++) {
         renderPrimitive(rsc, ct);
     }
 }
 
 void Mesh::renderPrimitive(Context *rsc, uint32_t primIndex) const {
-    if (primIndex >= mPrimitivesCount) {
+    if (primIndex >= mHal.state.primitivesCount) {
         LOGE("Invalid primitive index");
         return;
     }
 
-    Primitive_t *prim = mPrimitives[primIndex];
+    Primitive_t *prim = mHal.state.primitives[primIndex];
 
     if (prim->mIndexBuffer.get()) {
         renderPrimitiveRange(rsc, primIndex, 0, prim->mIndexBuffer->getType()->getDimX());
         return;
     }
 
-    renderPrimitiveRange(rsc, primIndex, 0, mVertexBuffers[0]->getType()->getDimX());
+    renderPrimitiveRange(rsc, primIndex, 0, mHal.state.vertexBuffers[0]->getType()->getDimX());
 }
 
 void Mesh::renderPrimitiveRange(Context *rsc, uint32_t primIndex, uint32_t start, uint32_t len) const {
-    if (len < 1 || primIndex >= mPrimitivesCount || mAttribCount == 0) {
+    if (len < 1 || primIndex >= mHal.state.primitivesCount) {
         LOGE("Invalid mesh or parameters");
         return;
     }
 
-    rsc->checkError("Mesh::renderPrimitiveRange 1");
-    for (uint32_t ct=0; ct < mVertexBufferCount; ct++) {
-        mVertexBuffers[ct]->uploadCheck(rsc);
-    }
-    // update attributes with either buffer information or data ptr based on their current state
-    for (uint32_t ct=0; ct < mAttribCount; ct++) {
-        uint32_t allocIndex = mAttribAllocationIndex[ct];
-        Allocation *alloc = mVertexBuffers[allocIndex].get();
-        if (alloc->getIsBufferObject()) {
-            mAttribs[ct].buffer = alloc->getBufferObjectID();
-            mAttribs[ct].ptr = NULL;
-        } else {
-            mAttribs[ct].buffer = 0;
-            mAttribs[ct].ptr = (const uint8_t*)alloc->getPtr();
-        }
+    for (uint32_t ct=0; ct < mHal.state.vertexBuffersCount; ct++) {
+        mHal.state.vertexBuffers[ct]->uploadCheck(rsc);
     }
 
-    VertexArray va(mAttribs, mAttribCount);
-    va.setupGL2(rsc, &rsc->mStateVertexArray, &rsc->mShaderCache);
-
-    rsc->checkError("Mesh::renderPrimitiveRange 2");
-    Primitive_t *prim = mPrimitives[primIndex];
+    Primitive_t *prim = mHal.state.primitives[primIndex];
     if (prim->mIndexBuffer.get()) {
         prim->mIndexBuffer->uploadCheck(rsc);
-        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, prim->mIndexBuffer->getBufferObjectID());
-        glDrawElements(prim->mGLPrimitive, len, GL_UNSIGNED_SHORT, (uint16_t *)(start * 2));
-    } else {
-        glDrawArrays(prim->mGLPrimitive, start, len);
     }
+    rsc->checkError("Mesh::renderPrimitiveRange upload check");
 
-    rsc->checkError("Mesh::renderPrimitiveRange");
+    mRSC->mHal.funcs.mesh.draw(mRSC, this, primIndex, start, len);
+    rsc->checkError("Mesh::renderPrimitiveRange draw");
 }
 
-
 void Mesh::uploadAll(Context *rsc) {
-    for (uint32_t ct = 0; ct < mVertexBufferCount; ct ++) {
-        if (mVertexBuffers[ct].get()) {
-            mVertexBuffers[ct]->deferredUploadToBufferObject(rsc);
+    for (uint32_t ct = 0; ct < mHal.state.vertexBuffersCount; ct ++) {
+        if (mHal.state.vertexBuffers[ct].get()) {
+            mHal.state.vertexBuffers[ct]->deferredUploadToBufferObject(rsc);
         }
     }
 
-    for (uint32_t ct = 0; ct < mPrimitivesCount; ct ++) {
-        if (mPrimitives[ct]->mIndexBuffer.get()) {
-            mPrimitives[ct]->mIndexBuffer->deferredUploadToBufferObject(rsc);
-        }
-    }
-}
-
-void Mesh::updateGLPrimitives() {
-    for (uint32_t i = 0; i < mPrimitivesCount; i ++) {
-        switch (mPrimitives[i]->mPrimitive) {
-            case RS_PRIMITIVE_POINT:          mPrimitives[i]->mGLPrimitive = GL_POINTS; break;
-            case RS_PRIMITIVE_LINE:           mPrimitives[i]->mGLPrimitive = GL_LINES; break;
-            case RS_PRIMITIVE_LINE_STRIP:     mPrimitives[i]->mGLPrimitive = GL_LINE_STRIP; break;
-            case RS_PRIMITIVE_TRIANGLE:       mPrimitives[i]->mGLPrimitive = GL_TRIANGLES; break;
-            case RS_PRIMITIVE_TRIANGLE_STRIP: mPrimitives[i]->mGLPrimitive = GL_TRIANGLE_STRIP; break;
-            case RS_PRIMITIVE_TRIANGLE_FAN:   mPrimitives[i]->mGLPrimitive = GL_TRIANGLE_FAN; break;
+    for (uint32_t ct = 0; ct < mHal.state.primitivesCount; ct ++) {
+        if (mHal.state.primitives[ct]->mIndexBuffer.get()) {
+            mHal.state.primitives[ct]->mIndexBuffer->deferredUploadToBufferObject(rsc);
         }
     }
 }
@@ -312,8 +225,8 @@
     uint32_t stride = 0;
     uint32_t numVerts = 0;
     // First we need to find the position ptr and stride
-    for (uint32_t ct=0; ct < mVertexBufferCount; ct++) {
-        const Type *bufferType = mVertexBuffers[ct]->getType();
+    for (uint32_t ct=0; ct < mHal.state.vertexBuffersCount; ct++) {
+        const Type *bufferType = mHal.state.vertexBuffers[ct]->getType();
         const Element *bufferElem = bufferType->getElement();
 
         for (uint32_t ct=0; ct < bufferElem->getFieldCount(); ct++) {
@@ -321,7 +234,7 @@
                 vectorSize = bufferElem->getField(ct)->getComponent().getVectorSize();
                 stride = bufferElem->getSizeBytes() / sizeof(float);
                 uint32_t offset = bufferElem->getFieldOffsetBytes(ct);
-                posPtr = (float*)((uint8_t*)mVertexBuffers[ct]->getPtr() + offset);
+                posPtr = (float*)((uint8_t*)mHal.state.vertexBuffers[ct]->getPtr() + offset);
                 numVerts = bufferType->getDimX();
                 break;
             }
@@ -353,73 +266,62 @@
 namespace renderscript {
 
 RsMesh rsi_MeshCreate(Context *rsc, uint32_t vtxCount, uint32_t idxCount) {
-    Mesh *sm = new Mesh(rsc);
+    Mesh *sm = new Mesh(rsc, vtxCount, idxCount);
     sm->incUserRef();
 
-    sm->mPrimitivesCount = idxCount;
-    sm->mPrimitives = new Mesh::Primitive_t *[sm->mPrimitivesCount];
-    for (uint32_t ct = 0; ct < idxCount; ct ++) {
-        sm->mPrimitives[ct] = new Mesh::Primitive_t;
-    }
-
-    sm->mVertexBufferCount = vtxCount;
-    sm->mVertexBuffers = new ObjectBaseRef<Allocation>[vtxCount];
-
     return sm;
 }
 
 void rsi_MeshBindVertex(Context *rsc, RsMesh mv, RsAllocation va, uint32_t slot) {
     Mesh *sm = static_cast<Mesh *>(mv);
-    rsAssert(slot < sm->mVertexBufferCount);
+    rsAssert(slot < sm->mHal.state.vertexBuffersCount);
 
-    sm->mVertexBuffers[slot].set((Allocation *)va);
+    sm->setVertexBuffer((Allocation *)va, slot);
 }
 
 void rsi_MeshBindIndex(Context *rsc, RsMesh mv, RsAllocation va, uint32_t primType, uint32_t slot) {
     Mesh *sm = static_cast<Mesh *>(mv);
-    rsAssert(slot < sm->mPrimitivesCount);
+    rsAssert(slot < sm->mHal.state.primitivesCount);
 
-    sm->mPrimitives[slot]->mIndexBuffer.set((Allocation *)va);
-    sm->mPrimitives[slot]->mPrimitive = (RsPrimitive)primType;
-    sm->updateGLPrimitives();
+    sm->setPrimitive((Allocation *)va, (RsPrimitive)primType, slot);
 }
 
 void rsi_MeshInitVertexAttribs(Context *rsc, RsMesh mv) {
     Mesh *sm = static_cast<Mesh *>(mv);
-    sm->initVertexAttribs();
+    sm->init();
 }
 
 }}
 
 void rsaMeshGetVertexBufferCount(RsContext con, RsMesh mv, int32_t *numVtx) {
     Mesh *sm = static_cast<Mesh *>(mv);
-    *numVtx = sm->mVertexBufferCount;
+    *numVtx = sm->mHal.state.vertexBuffersCount;
 }
 
 void rsaMeshGetIndexCount(RsContext con, RsMesh mv, int32_t *numIdx) {
     Mesh *sm = static_cast<Mesh *>(mv);
-    *numIdx = sm->mPrimitivesCount;
+    *numIdx = sm->mHal.state.primitivesCount;
 }
 
 void rsaMeshGetVertices(RsContext con, RsMesh mv, RsAllocation *vtxData, uint32_t vtxDataCount) {
     Mesh *sm = static_cast<Mesh *>(mv);
-    rsAssert(vtxDataCount == sm->mVertexBufferCount);
+    rsAssert(vtxDataCount == sm->mHal.state.vertexBuffersCount);
 
     for (uint32_t ct = 0; ct < vtxDataCount; ct ++) {
-        vtxData[ct] = sm->mVertexBuffers[ct].get();
-        sm->mVertexBuffers[ct]->incUserRef();
+        vtxData[ct] = sm->mHal.state.vertexBuffers[ct].get();
+        sm->mHal.state.vertexBuffers[ct]->incUserRef();
     }
 }
 
 void rsaMeshGetIndices(RsContext con, RsMesh mv, RsAllocation *va, uint32_t *primType, uint32_t idxDataCount) {
     Mesh *sm = static_cast<Mesh *>(mv);
-    rsAssert(idxDataCount == sm->mPrimitivesCount);
+    rsAssert(idxDataCount == sm->mHal.state.primitivesCount);
 
     for (uint32_t ct = 0; ct < idxDataCount; ct ++) {
-        va[ct] = sm->mPrimitives[ct]->mIndexBuffer.get();
-        primType[ct] = sm->mPrimitives[ct]->mPrimitive;
-        if (sm->mPrimitives[ct]->mIndexBuffer.get()) {
-            sm->mPrimitives[ct]->mIndexBuffer->incUserRef();
+        va[ct] = sm->mHal.state.primitives[ct]->mIndexBuffer.get();
+        primType[ct] = sm->mHal.state.primitives[ct]->mPrimitive;
+        if (sm->mHal.state.primitives[ct]->mIndexBuffer.get()) {
+            sm->mHal.state.primitives[ct]->mIndexBuffer->incUserRef();
         }
     }
 }
diff --git a/libs/rs/rsMesh.h b/libs/rs/rsMesh.h
index 3e080e2..1e279f4 100644
--- a/libs/rs/rsMesh.h
+++ b/libs/rs/rsMesh.h
@@ -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.
@@ -29,57 +29,65 @@
 class Mesh : public ObjectBase {
 public:
     Mesh(Context *);
+    Mesh(Context *, uint32_t vertexBuffersCount, uint32_t primitivesCount);
     ~Mesh();
 
-    // Contains vertex data
-    // Position, normal, texcoord, etc could either be strided in one allocation
-    // of provided separetely in multiple ones
-    ObjectBaseRef<Allocation> *mVertexBuffers;
-    uint32_t mVertexBufferCount;
-
     // Either mIndexBuffer, mPrimitiveBuffer or both could have a NULL reference
     // If both are null, mPrimitive only would be used to render the mesh
-    struct Primitive_t
-    {
+    struct Primitive_t {
         ObjectBaseRef<Allocation> mIndexBuffer;
-
         RsPrimitive mPrimitive;
-        uint32_t mGLPrimitive;
     };
 
+    // compatibility to not break the build
+    ObjectBaseRef<Allocation> *mVertexBuffers;
+    uint32_t mVertexBufferCount;
     Primitive_t ** mPrimitives;
     uint32_t mPrimitivesCount;
+    // end compatibility
 
     virtual void serialize(OStream *stream) const;
     virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_MESH; }
     static Mesh *createFromStream(Context *rsc, IStream *stream);
+    void init();
 
-#ifndef ANDROID_RS_SERIALIZE
+    struct Hal {
+        mutable void *drv;
+
+        struct State {
+            // Contains vertex data
+            // Position, normal, texcoord, etc could either be strided in one allocation
+            // of provided separetely in multiple ones
+            ObjectBaseRef<Allocation> *vertexBuffers;
+            uint32_t vertexBuffersCount;
+
+            Primitive_t ** primitives;
+            uint32_t primitivesCount;
+        };
+        State state;
+    };
+    Hal mHal;
+
+    void setVertexBuffer(Allocation *vb, uint32_t index) {
+        mHal.state.vertexBuffers[index].set(vb);
+    }
+
+    void setPrimitive(Allocation *idx, RsPrimitive prim, uint32_t index) {
+        mHal.state.primitives[index]->mIndexBuffer.set(idx);
+        mHal.state.primitives[index]->mPrimitive = prim;
+    }
+
     void render(Context *) const;
     void renderPrimitive(Context *, uint32_t primIndex) const;
     void renderPrimitiveRange(Context *, uint32_t primIndex, uint32_t start, uint32_t len) const;
     void uploadAll(Context *);
-    void updateGLPrimitives();
-
-
 
     // Bounding volumes
     float mBBoxMin[3];
     float mBBoxMax[3];
     void computeBBox();
-
-    void initVertexAttribs();
-
 protected:
-    bool isValidGLComponent(const Element *elem, uint32_t fieldIdx);
-    // Attribues that allow us to map to GL
-    VertexArray::Attrib *mAttribs;
-    // This allows us to figure out which allocation the attribute
-    // belongs to. In the event the allocation is uploaded to GL
-    // buffer, it lets us properly map it
-    uint32_t *mAttribAllocationIndex;
-    uint32_t mAttribCount;
-#endif
+    bool mInitialized;
 };
 
 class MeshContext {
@@ -92,7 +100,7 @@
 
 }
 }
-#endif //ANDROID_RS_TRIANGLE_MESH_H
+#endif //ANDROID_RS_MESH_H
 
 
 
diff --git a/libs/rs/rsProgram.cpp b/libs/rs/rsProgram.cpp
index 4ef05bf..28fa061 100644
--- a/libs/rs/rsProgram.cpp
+++ b/libs/rs/rsProgram.cpp
@@ -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.
@@ -15,11 +15,6 @@
  */
 
 #include "rsContext.h"
-#ifndef ANDROID_RS_SERIALIZE
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#endif //ANDROID_RS_SERIALIZE
-
 #include "rsProgram.h"
 
 using namespace android;
@@ -36,26 +31,22 @@
     initMemberVars();
     for (uint32_t ct=0; ct < paramLength; ct+=2) {
         if (params[ct] == RS_PROGRAM_PARAM_INPUT) {
-            mInputCount++;
-        }
-        if (params[ct] == RS_PROGRAM_PARAM_OUTPUT) {
-            mOutputCount++;
+            mHal.state.inputElementsCount++;
         }
         if (params[ct] == RS_PROGRAM_PARAM_CONSTANT) {
-            mConstantCount++;
+            mHal.state.constantsCount++;
         }
         if (params[ct] == RS_PROGRAM_PARAM_TEXTURE_TYPE) {
-            mTextureCount++;
+            mHal.state.texturesCount++;
         }
     }
 
-    mTextures = new ObjectBaseRef<Allocation>[mTextureCount];
-    mSamplers = new ObjectBaseRef<Sampler>[mTextureCount];
-    mTextureTargets = new RsTextureTarget[mTextureCount];
-    mInputElements = new ObjectBaseRef<Element>[mInputCount];
-    mOutputElements = new ObjectBaseRef<Element>[mOutputCount];
-    mConstantTypes = new ObjectBaseRef<Type>[mConstantCount];
-    mConstants = new ObjectBaseRef<Allocation>[mConstantCount];
+    mHal.state.textures = new ObjectBaseRef<Allocation>[mHal.state.texturesCount];
+    mHal.state.samplers = new ObjectBaseRef<Sampler>[mHal.state.texturesCount];
+    mHal.state.textureTargets = new RsTextureTarget[mHal.state.texturesCount];
+    mHal.state.inputElements = new ObjectBaseRef<Element>[mHal.state.inputElementsCount];
+    mHal.state.constantTypes = new ObjectBaseRef<Type>[mHal.state.constantsCount];
+    mHal.state.constants = new ObjectBaseRef<Allocation>[mHal.state.constantsCount];
 
     uint32_t input = 0;
     uint32_t output = 0;
@@ -63,16 +54,13 @@
     uint32_t texture = 0;
     for (uint32_t ct=0; ct < paramLength; ct+=2) {
         if (params[ct] == RS_PROGRAM_PARAM_INPUT) {
-            mInputElements[input++].set(reinterpret_cast<Element *>(params[ct+1]));
-        }
-        if (params[ct] == RS_PROGRAM_PARAM_OUTPUT) {
-            mOutputElements[output++].set(reinterpret_cast<Element *>(params[ct+1]));
+            mHal.state.inputElements[input++].set(reinterpret_cast<Element *>(params[ct+1]));
         }
         if (params[ct] == RS_PROGRAM_PARAM_CONSTANT) {
-            mConstantTypes[constant++].set(reinterpret_cast<Type *>(params[ct+1]));
+            mHal.state.constantTypes[constant++].set(reinterpret_cast<Type *>(params[ct+1]));
         }
         if (params[ct] == RS_PROGRAM_PARAM_TEXTURE_TYPE) {
-            mTextureTargets[texture++] = (RsTextureTarget)params[ct+1];
+            mHal.state.textureTargets[texture++] = (RsTextureTarget)params[ct+1];
         }
     }
     mIsInternal = false;
@@ -84,88 +72,69 @@
         shaderLength -= internalTokenLen;
     }
     mUserShader.setTo(shaderText, shaderLength);
-
-    initAttribAndUniformArray();
 }
 
 Program::~Program() {
-    if (mRSC->props.mLogShaders) {
-        LOGV("Program::~Program with shader id %u", mShaderID);
-    }
 
-    if (mShaderID) {
-        glDeleteShader(mShaderID);
-    }
-
-    for (uint32_t ct=0; ct < mConstantCount; ct++) {
+    for (uint32_t ct=0; ct < mHal.state.constantsCount; ct++) {
         bindAllocation(NULL, NULL, ct);
     }
 
-    for (uint32_t ct=0; ct < mTextureCount; ct++) {
+    for (uint32_t ct=0; ct < mHal.state.texturesCount; ct++) {
         bindTexture(NULL, ct, NULL);
         bindSampler(NULL, ct, NULL);
     }
-    delete[] mTextures;
-    delete[] mSamplers;
-    delete[] mTextureTargets;
-    delete[] mInputElements;
-    delete[] mOutputElements;
-    delete[] mConstantTypes;
-    delete[] mConstants;
-    delete[] mAttribNames;
-    delete[] mUniformNames;
-    delete[] mUniformArraySizes;
-    mInputCount = 0;
-    mOutputCount = 0;
-    mConstantCount = 0;
+    delete[] mHal.state.textures;
+    delete[] mHal.state.samplers;
+    delete[] mHal.state.textureTargets;
+    delete[] mHal.state.inputElements;
+    delete[] mHal.state.constantTypes;
+    delete[] mHal.state.constants;
+    mHal.state.inputElementsCount = 0;
+    mHal.state.constantsCount = 0;
+    mHal.state.texturesCount = 0;
 }
 
 void Program::initMemberVars() {
     mDirty = true;
-    mShaderID = 0;
-    mAttribCount = 0;
-    mUniformCount = 0;
-    mTextureCount = 0;
 
-    mTextures = NULL;
-    mSamplers = NULL;
-    mTextureTargets = NULL;
-    mInputElements = NULL;
-    mOutputElements = NULL;
-    mConstantTypes = NULL;
-    mConstants = NULL;
-    mAttribNames = NULL;
-    mUniformNames = NULL;
-    mUniformArraySizes = NULL;
-    mInputCount = 0;
-    mOutputCount = 0;
-    mConstantCount = 0;
-    mIsValid = false;
+    mHal.drv = NULL;
+    mHal.state.textures = NULL;
+    mHal.state.samplers = NULL;
+    mHal.state.textureTargets = NULL;
+    mHal.state.inputElements = NULL;
+    mHal.state.constantTypes = NULL;
+    mHal.state.constants = NULL;
+
+    mHal.state.inputElementsCount = 0;
+    mHal.state.constantsCount = 0;
+    mHal.state.texturesCount = 0;
+
     mIsInternal = false;
 }
 
 void Program::bindAllocation(Context *rsc, Allocation *alloc, uint32_t slot) {
     if (alloc != NULL) {
-        if (slot >= mConstantCount) {
+        if (slot >= mHal.state.constantsCount) {
             LOGE("Attempt to bind alloc at slot %u, on shader id %u, but const count is %u",
-                 slot, (uint32_t)this, mConstantCount);
+                 slot, (uint32_t)this, mHal.state.constantsCount);
             rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind allocation");
             return;
         }
-        if (!alloc->getType()->isEqual(mConstantTypes[slot].get())) {
+        if (!alloc->getType()->isEqual(mHal.state.constantTypes[slot].get())) {
             LOGE("Attempt to bind alloc at slot %u, on shader id %u, but types mismatch",
                  slot, (uint32_t)this);
             rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind allocation");
             return;
         }
     }
-    if (mConstants[slot].get() == alloc) {
+    if (mHal.state.constants[slot].get() == alloc) {
         return;
     }
-    if (mConstants[slot].get()) {
-        mConstants[slot].get()->removeProgramToDirty(this);
+    if (mHal.state.constants[slot].get()) {
+        mHal.state.constants[slot].get()->removeProgramToDirty(this);
     }
-    mConstants[slot].set(alloc);
+    mHal.state.constants[slot].set(alloc);
     if (alloc) {
         alloc->addProgramToDirty(this);
     }
@@ -173,327 +142,38 @@
 }
 
 void Program::bindTexture(Context *rsc, uint32_t slot, Allocation *a) {
-    if (slot >= mTextureCount) {
-        LOGE("Attempt to bind texture to slot %u but tex count is %u", slot, mTextureCount);
+    if (slot >= mHal.state.texturesCount) {
+        LOGE("Attempt to bind texture to slot %u but tex count is %u", slot, mHal.state.texturesCount);
         rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind texture");
         return;
     }
 
-    if (a && a->getType()->getDimFaces() && mTextureTargets[slot] != RS_TEXTURE_CUBE) {
+    if (a && a->getType()->getDimFaces() && mHal.state.textureTargets[slot] != RS_TEXTURE_CUBE) {
         LOGE("Attempt to bind cubemap to slot %u but 2d texture needed", slot);
         rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind cubemap to 2d texture slot");
         return;
     }
 
     //LOGE("bindtex %i %p", slot, a);
-    mTextures[slot].set(a);
+    mHal.state.textures[slot].set(a);
     mDirty = true;
 }
 
 void Program::bindSampler(Context *rsc, uint32_t slot, Sampler *s) {
-    if (slot >= mTextureCount) {
-        LOGE("Attempt to bind sampler to slot %u but tex count is %u", slot, mTextureCount);
+    if (slot >= mHal.state.texturesCount) {
+        LOGE("Attempt to bind sampler to slot %u but tex count is %u", slot, mHal.state.texturesCount);
         rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind sampler");
         return;
     }
 
-    mSamplers[slot].set(s);
+    mHal.state.samplers[slot].set(s);
     mDirty = true;
 }
 
-String8 Program::getGLSLInputString() const {
-    String8 s;
-    for (uint32_t ct=0; ct < mInputCount; ct++) {
-        const Element *e = mInputElements[ct].get();
-        for (uint32_t field=0; field < e->getFieldCount(); field++) {
-            const Element *f = e->getField(field);
-
-            // Cannot be complex
-            rsAssert(!f->getFieldCount());
-            switch (f->getComponent().getVectorSize()) {
-            case 1: s.append("attribute float ATTRIB_"); break;
-            case 2: s.append("attribute vec2 ATTRIB_"); break;
-            case 3: s.append("attribute vec3 ATTRIB_"); break;
-            case 4: s.append("attribute vec4 ATTRIB_"); break;
-            default:
-                rsAssert(0);
-            }
-
-            s.append(e->getFieldName(field));
-            s.append(";\n");
-        }
-    }
-    return s;
-}
-
-String8 Program::getGLSLOutputString() const {
-    return String8();
-}
-
-String8 Program::getGLSLConstantString() const {
-    return String8();
-}
-
-void Program::createShader() {
-}
-
-bool Program::loadShader(Context *rsc, uint32_t type) {
-    mShaderID = glCreateShader(type);
-    rsAssert(mShaderID);
-
-    if (rsc->props.mLogShaders) {
-        LOGV("Loading shader type %x, ID %i", type, mShaderID);
-        LOGV("%s", mShader.string());
-    }
-
-    if (mShaderID) {
-        const char * ss = mShader.string();
-        glShaderSource(mShaderID, 1, &ss, NULL);
-        glCompileShader(mShaderID);
-
-        GLint compiled = 0;
-        glGetShaderiv(mShaderID, GL_COMPILE_STATUS, &compiled);
-        if (!compiled) {
-            GLint infoLen = 0;
-            glGetShaderiv(mShaderID, GL_INFO_LOG_LENGTH, &infoLen);
-            if (infoLen) {
-                char* buf = (char*) malloc(infoLen);
-                if (buf) {
-                    glGetShaderInfoLog(mShaderID, infoLen, NULL, buf);
-                    LOGE("Could not compile shader \n%s\n", buf);
-                    free(buf);
-                }
-                glDeleteShader(mShaderID);
-                mShaderID = 0;
-                rsc->setError(RS_ERROR_BAD_SHADER, "Error returned from GL driver loading shader text,");
-                return false;
-            }
-        }
-    }
-
-    if (rsc->props.mLogShaders) {
-        LOGV("--Shader load result %x ", glGetError());
-    }
-    mIsValid = true;
-    return true;
-}
-
 void Program::setShader(const char *txt, uint32_t len) {
     mUserShader.setTo(txt, len);
 }
 
-void Program::appendUserConstants() {
-    for (uint32_t ct=0; ct < mConstantCount; ct++) {
-        const Element *e = mConstantTypes[ct]->getElement();
-        for (uint32_t field=0; field < e->getFieldCount(); field++) {
-            const Element *f = e->getField(field);
-            const char *fn = e->getFieldName(field);
-
-            if (fn[0] == '#') {
-                continue;
-            }
-
-            // Cannot be complex
-            rsAssert(!f->getFieldCount());
-            if (f->getType() == RS_TYPE_MATRIX_4X4) {
-                mShader.append("uniform mat4 UNI_");
-            } else if (f->getType() == RS_TYPE_MATRIX_3X3) {
-                mShader.append("uniform mat3 UNI_");
-            } else if (f->getType() == RS_TYPE_MATRIX_2X2) {
-                mShader.append("uniform mat2 UNI_");
-            } else {
-                switch (f->getComponent().getVectorSize()) {
-                case 1: mShader.append("uniform float UNI_"); break;
-                case 2: mShader.append("uniform vec2 UNI_"); break;
-                case 3: mShader.append("uniform vec3 UNI_"); break;
-                case 4: mShader.append("uniform vec4 UNI_"); break;
-                default:
-                    rsAssert(0);
-                }
-            }
-
-            mShader.append(fn);
-            if (e->getFieldArraySize(field) > 1) {
-                mShader.appendFormat("[%d]", e->getFieldArraySize(field));
-            }
-            mShader.append(";\n");
-        }
-    }
-}
-
-void Program::logUniform(const Element *field, const float *fd, uint32_t arraySize ) {
-    RsDataType dataType = field->getType();
-    uint32_t elementSize = field->getSizeBytes() / sizeof(float);
-    for (uint32_t i = 0; i < arraySize; i ++) {
-        if (arraySize > 1) {
-            LOGV("Array Element [%u]", i);
-        }
-        if (dataType == RS_TYPE_MATRIX_4X4) {
-            LOGV("Matrix4x4");
-            LOGV("{%f, %f, %f, %f",  fd[0], fd[4], fd[8], fd[12]);
-            LOGV(" %f, %f, %f, %f",  fd[1], fd[5], fd[9], fd[13]);
-            LOGV(" %f, %f, %f, %f",  fd[2], fd[6], fd[10], fd[14]);
-            LOGV(" %f, %f, %f, %f}", fd[3], fd[7], fd[11], fd[15]);
-        } else if (dataType == RS_TYPE_MATRIX_3X3) {
-            LOGV("Matrix3x3");
-            LOGV("{%f, %f, %f",  fd[0], fd[3], fd[6]);
-            LOGV(" %f, %f, %f",  fd[1], fd[4], fd[7]);
-            LOGV(" %f, %f, %f}", fd[2], fd[5], fd[8]);
-        } else if (dataType == RS_TYPE_MATRIX_2X2) {
-            LOGV("Matrix2x2");
-            LOGV("{%f, %f",  fd[0], fd[2]);
-            LOGV(" %f, %f}", fd[1], fd[3]);
-        } else {
-            switch (field->getComponent().getVectorSize()) {
-            case 1:
-                LOGV("Uniform 1 = %f", fd[0]);
-                break;
-            case 2:
-                LOGV("Uniform 2 = %f %f", fd[0], fd[1]);
-                break;
-            case 3:
-                LOGV("Uniform 3 = %f %f %f", fd[0], fd[1], fd[2]);
-                break;
-            case 4:
-                LOGV("Uniform 4 = %f %f %f %f", fd[0], fd[1], fd[2], fd[3]);
-                break;
-            default:
-                rsAssert(0);
-            }
-        }
-        LOGE("Element size %u data=%p", elementSize, fd);
-        fd += elementSize;
-        LOGE("New data=%p", fd);
-    }
-}
-
-void Program::setUniform(Context *rsc, const Element *field, const float *fd,
-                         int32_t slot, uint32_t arraySize ) {
-    RsDataType dataType = field->getType();
-    if (dataType == RS_TYPE_MATRIX_4X4) {
-        glUniformMatrix4fv(slot, arraySize, GL_FALSE, fd);
-    } else if (dataType == RS_TYPE_MATRIX_3X3) {
-        glUniformMatrix3fv(slot, arraySize, GL_FALSE, fd);
-    } else if (dataType == RS_TYPE_MATRIX_2X2) {
-        glUniformMatrix2fv(slot, arraySize, GL_FALSE, fd);
-    } else {
-        switch (field->getComponent().getVectorSize()) {
-        case 1:
-            glUniform1fv(slot, arraySize, fd);
-            break;
-        case 2:
-            glUniform2fv(slot, arraySize, fd);
-            break;
-        case 3:
-            glUniform3fv(slot, arraySize, fd);
-            break;
-        case 4:
-            glUniform4fv(slot, arraySize, fd);
-            break;
-        default:
-            rsAssert(0);
-        }
-    }
-}
-
-void Program::setupUserConstants(Context *rsc, ShaderCache *sc, bool isFragment) {
-    uint32_t uidx = 0;
-    for (uint32_t ct=0; ct < mConstantCount; ct++) {
-        Allocation *alloc = mConstants[ct].get();
-        if (!alloc) {
-            LOGE("Attempting to set constants on shader id %u, but alloc at slot %u is not set", (uint32_t)this, ct);
-            rsc->setError(RS_ERROR_BAD_SHADER, "No constant allocation bound");
-            continue;
-        }
-
-        const uint8_t *data = static_cast<const uint8_t *>(alloc->getPtr());
-        const Element *e = mConstantTypes[ct]->getElement();
-        for (uint32_t field=0; field < e->getFieldCount(); field++) {
-            const Element *f = e->getField(field);
-            const char *fieldName = e->getFieldName(field);
-            // If this field is padding, skip it
-            if (fieldName[0] == '#') {
-                continue;
-            }
-
-            uint32_t offset = e->getFieldOffsetBytes(field);
-            const float *fd = reinterpret_cast<const float *>(&data[offset]);
-
-            int32_t slot = -1;
-            uint32_t arraySize = 1;
-            if (!isFragment) {
-                slot = sc->vtxUniformSlot(uidx);
-                arraySize = sc->vtxUniformSize(uidx);
-            } else {
-                slot = sc->fragUniformSlot(uidx);
-                arraySize = sc->fragUniformSize(uidx);
-            }
-            if (rsc->props.mLogShadersUniforms) {
-                LOGV("Uniform  slot=%i, offset=%i, constant=%i, field=%i, uidx=%i, name=%s", slot, offset, ct, field, uidx, fieldName);
-            }
-            uidx ++;
-            if (slot < 0) {
-                continue;
-            }
-
-            if (rsc->props.mLogShadersUniforms) {
-                logUniform(f, fd, arraySize);
-            }
-            setUniform(rsc, f, fd, slot, arraySize);
-        }
-    }
-}
-
-void Program::initAttribAndUniformArray() {
-    mAttribCount = 0;
-    for (uint32_t ct=0; ct < mInputCount; ct++) {
-        const Element *elem = mInputElements[ct].get();
-        for (uint32_t field=0; field < elem->getFieldCount(); field++) {
-            if (elem->getFieldName(field)[0] != '#') {
-                mAttribCount ++;
-            }
-        }
-    }
-
-    mUniformCount = 0;
-    for (uint32_t ct=0; ct < mConstantCount; ct++) {
-        const Element *elem = mConstantTypes[ct]->getElement();
-
-        for (uint32_t field=0; field < elem->getFieldCount(); field++) {
-            if (elem->getFieldName(field)[0] != '#') {
-                mUniformCount ++;
-            }
-        }
-    }
-    mUniformCount += mTextureCount;
-
-    if (mAttribCount) {
-        mAttribNames = new String8[mAttribCount];
-    }
-    if (mUniformCount) {
-        mUniformNames = new String8[mUniformCount];
-        mUniformArraySizes = new uint32_t[mUniformCount];
-    }
-}
-
-void Program::initAddUserElement(const Element *e, String8 *names, uint32_t *arrayLengths, uint32_t *count, const char *prefix) {
-    rsAssert(e->getFieldCount());
-    for (uint32_t ct=0; ct < e->getFieldCount(); ct++) {
-        const Element *ce = e->getField(ct);
-        if (ce->getFieldCount()) {
-            initAddUserElement(ce, names, arrayLengths, count, prefix);
-        } else if (e->getFieldName(ct)[0] != '#') {
-            String8 tmp(prefix);
-            tmp.append(e->getFieldName(ct));
-            names[*count].setTo(tmp.string());
-            if (arrayLengths) {
-                arrayLengths[*count] = e->getFieldArraySize(ct);
-            }
-            (*count)++;
-        }
-    }
-}
-
 namespace android {
 namespace renderscript {
 
diff --git a/libs/rs/rsProgram.h b/libs/rs/rsProgram.h
index c48464d..bcf5519 100644
--- a/libs/rs/rsProgram.h
+++ b/libs/rs/rsProgram.h
@@ -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.
@@ -23,7 +23,6 @@
 // ---------------------------------------------------------------------------
 namespace android {
 namespace renderscript {
-class ShaderCache;
 
 #define RS_SHADER_INTERNAL "//rs_shader_internal\n"
 #define RS_SHADER_ATTR "ATTRIB_"
@@ -38,75 +37,53 @@
     virtual ~Program();
 
     void bindAllocation(Context *, Allocation *, uint32_t slot);
-    virtual void createShader();
 
     bool isUserProgram() const {return !mIsInternal;}
 
     void bindTexture(Context *, uint32_t slot, Allocation *);
     void bindSampler(Context *, uint32_t slot, Sampler *);
 
-    uint32_t getShaderID() const {return mShaderID;}
     void setShader(const char *, uint32_t len);
 
-    uint32_t getAttribCount() const {return mAttribCount;}
-    uint32_t getUniformCount() const {return mUniformCount;}
-    const String8 & getAttribName(uint32_t i) const {return mAttribNames[i];}
-    const String8 & getUniformName(uint32_t i) const {return mUniformNames[i];}
-    uint32_t getUniformArraySize(uint32_t i) const {return mUniformArraySizes[i];}
-
-    String8 getGLSLInputString() const;
-    String8 getGLSLOutputString() const;
-    String8 getGLSLConstantString() const;
-
-    bool isValid() const {return mIsValid;}
     void forceDirty() const {mDirty = true;}
 
+    struct Hal {
+        mutable void *drv;
+
+        struct State {
+            // The difference between Textures and Constants is how they are accessed
+            // Texture lookups go though a sampler which in effect converts normalized
+            // coordinates into type specific.  Multiple samples may also be taken
+            // and filtered.
+            //
+            // Constants are strictly accessed by the shader code
+            ObjectBaseRef<Allocation> *textures;
+            RsTextureTarget *textureTargets;
+            uint32_t texturesCount;
+
+            ObjectBaseRef<Sampler> *samplers;
+            uint32_t samplersCount;
+
+            ObjectBaseRef<Allocation> *constants;
+            ObjectBaseRef<Type> *constantTypes;
+            uint32_t constantsCount;
+
+            ObjectBaseRef<Element> *inputElements;
+            uint32_t inputElementsCount;
+        };
+        State state;
+    };
+    Hal mHal;
+
 protected:
-    // Components not listed in "in" will be passed though
-    // unless overwritten by components in out.
-    ObjectBaseRef<Element> *mInputElements;
-    ObjectBaseRef<Element> *mOutputElements;
-    ObjectBaseRef<Type> *mConstantTypes;
-    ObjectBaseRef<Allocation> *mConstants;
-    uint32_t mInputCount;
-    uint32_t mOutputCount;
-    uint32_t mConstantCount;
-    bool mIsValid;
     bool mIsInternal;
 
-    // Applies to vertex and fragment shaders only
-    void appendUserConstants();
-    void setupUserConstants(Context *rsc, ShaderCache *sc, bool isFragment);
-    void initAddUserElement(const Element *e, String8 *names, uint32_t *arrayLengths, uint32_t *count, const char *prefix);
-
-    void initAttribAndUniformArray();
-
     mutable bool mDirty;
-    String8 mShader;
     String8 mUserShader;
-    uint32_t mShaderID;
-
-    uint32_t mTextureCount;
-    uint32_t mAttribCount;
-    uint32_t mUniformCount;
-    String8 *mAttribNames;
-    String8 *mUniformNames;
-    uint32_t *mUniformArraySizes;
 
     void logUniform(const Element *field, const float *fd, uint32_t arraySize );
     void setUniform(Context *rsc, const Element *field, const float *fd, int32_t slot, uint32_t arraySize );
     void initMemberVars();
-
-    // The difference between Textures and Constants is how they are accessed
-    // Texture lookups go though a sampler which in effect converts normalized
-    // coordinates into type specific.  Multiple samples may also be taken
-    // and filtered.
-    //
-    // Constants are strictly accessed by programetic loads.
-    ObjectBaseRef<Allocation> *mTextures;
-    ObjectBaseRef<Sampler> *mSamplers;
-    RsTextureTarget *mTextureTargets;
-    bool loadShader(Context *, uint32_t type);
 };
 
 }
diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp
index ff314b7..39887ca 100644
--- a/libs/rs/rsProgramFragment.cpp
+++ b/libs/rs/rsProgramFragment.cpp
@@ -15,13 +15,6 @@
  */
 
 #include "rsContext.h"
-#ifndef ANDROID_RS_SERIALIZE
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#endif //ANDROID_RS_SERIALIZE
-
 #include "rsProgramFragment.h"
 
 using namespace android;
@@ -31,19 +24,16 @@
                                  uint32_t shaderLength, const uint32_t * params,
                                  uint32_t paramLength)
     : Program(rsc, shaderText, shaderLength, params, paramLength) {
-
     mConstantColor[0] = 1.f;
     mConstantColor[1] = 1.f;
     mConstantColor[2] = 1.f;
     mConstantColor[3] = 1.f;
 
-    init(rsc);
+    mRSC->mHal.funcs.fragment.init(mRSC, this, mUserShader.string(), mUserShader.length());
 }
 
 ProgramFragment::~ProgramFragment() {
-    if (mShaderID) {
-        mRSC->mShaderCache.cleanupFragment(mShaderID);
-    }
+    mRSC->mHal.funcs.fragment.destroy(mRSC, this);
 }
 
 void ProgramFragment::setConstantColor(Context *rsc, float r, float g, float b, float a) {
@@ -52,7 +42,7 @@
         rsc->setError(RS_ERROR_BAD_SHADER, "Cannot  set fixed function emulation color on user program");
         return;
     }
-    if (mConstants[0].get() == NULL) {
+    if (mHal.state.constants[0].get() == NULL) {
         LOGE("Unable to set fixed function emulation color because allocation is missing");
         rsc->setError(RS_ERROR_BAD_SHADER, "Unable to set fixed function emulation color because allocation is missing");
         return;
@@ -61,11 +51,11 @@
     mConstantColor[1] = g;
     mConstantColor[2] = b;
     mConstantColor[3] = a;
-    memcpy(mConstants[0]->getPtr(), mConstantColor, 4*sizeof(float));
+    memcpy(mHal.state.constants[0]->getPtr(), mConstantColor, 4*sizeof(float));
     mDirty = true;
 }
 
-void ProgramFragment::setupGL2(Context *rsc, ProgramFragmentState *state, ShaderCache *sc) {
+void ProgramFragment::setupGL2(Context *rsc, ProgramFragmentState *state) {
     //LOGE("sgl2 frag1 %x", glGetError());
     if ((state->mLast.get() == this) && !mDirty) {
         return;
@@ -74,94 +64,16 @@
 
     rsc->checkError("ProgramFragment::setupGL2 start");
 
-    rsc->checkError("ProgramFragment::setupGL2 begin uniforms");
-    setupUserConstants(rsc, sc, true);
-
-    uint32_t numTexturesToBind = mTextureCount;
-    uint32_t numTexturesAvailable = rsc->getMaxFragmentTextures();
-    if (numTexturesToBind >= numTexturesAvailable) {
-        LOGE("Attempting to bind %u textures on shader id %u, but only %u are available",
-             mTextureCount, (uint32_t)this, numTexturesAvailable);
-        rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind more textuers than available");
-        numTexturesToBind = numTexturesAvailable;
-    }
-
-    for (uint32_t ct=0; ct < numTexturesToBind; ct++) {
-        glActiveTexture(GL_TEXTURE0 + ct);
-        if (!mTextures[ct].get()) {
+    for (uint32_t ct=0; ct < mHal.state.texturesCount; ct++) {
+        if (!mHal.state.textures[ct].get()) {
             LOGE("No texture bound for shader id %u, texture unit %u", (uint)this, ct);
             rsc->setError(RS_ERROR_BAD_SHADER, "No texture bound");
             continue;
         }
-
-        mTextures[ct]->uploadCheck(rsc);
-        GLenum target = (GLenum)mTextures[ct]->getGLTarget();
-        if (target != GL_TEXTURE_2D && target != GL_TEXTURE_CUBE_MAP) {
-            LOGE("Attempting to bind unknown texture to shader id %u, texture unit %u", (uint)this, ct);
-            rsc->setError(RS_ERROR_BAD_SHADER, "Non-texture allocation bound to a shader");
-        }
-        glBindTexture(target, mTextures[ct]->getTextureID());
-        rsc->checkError("ProgramFragment::setupGL2 tex bind");
-        if (mSamplers[ct].get()) {
-            mSamplers[ct]->setupGL(rsc, mTextures[ct].get());
-        } else {
-            glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-            glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-            glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-            glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-            rsc->checkError("ProgramFragment::setupGL2 tex env");
-        }
-
-        glUniform1i(sc->fragUniformSlot(mTextureUniformIndexStart + ct), ct);
-        rsc->checkError("ProgramFragment::setupGL2 uniforms");
+        mHal.state.textures[ct]->uploadCheck(rsc);
     }
 
-    glActiveTexture(GL_TEXTURE0);
-    mDirty = false;
-    rsc->checkError("ProgramFragment::setupGL2");
-}
-
-void ProgramFragment::loadShader(Context *rsc) {
-    Program::loadShader(rsc, GL_FRAGMENT_SHADER);
-}
-
-void ProgramFragment::createShader() {
-    if (mUserShader.length() > 1) {
-        mShader.append("precision mediump float;\n");
-        appendUserConstants();
-        char buf[256];
-        for (uint32_t ct=0; ct < mTextureCount; ct++) {
-            if (mTextureTargets[ct] == RS_TEXTURE_2D) {
-                snprintf(buf, sizeof(buf), "uniform sampler2D UNI_Tex%i;\n", ct);
-            } else {
-                snprintf(buf, sizeof(buf), "uniform samplerCube UNI_Tex%i;\n", ct);
-            }
-            mShader.append(buf);
-        }
-        mShader.append(mUserShader);
-    } else {
-        LOGE("ProgramFragment::createShader cannot create program, shader code not defined");
-        rsAssert(0);
-    }
-}
-
-void ProgramFragment::init(Context *rsc) {
-    uint32_t uniformIndex = 0;
-    if (mUserShader.size() > 0) {
-        for (uint32_t ct=0; ct < mConstantCount; ct++) {
-            initAddUserElement(mConstantTypes[ct]->getElement(), mUniformNames, mUniformArraySizes, &uniformIndex, RS_SHADER_UNI);
-        }
-    }
-    mTextureUniformIndexStart = uniformIndex;
-    char buf[256];
-    for (uint32_t ct=0; ct < mTextureCount; ct++) {
-        snprintf(buf, sizeof(buf), "UNI_Tex%i", ct);
-        mUniformNames[uniformIndex].setTo(buf);
-        mUniformArraySizes[uniformIndex] = 1;
-        uniformIndex++;
-    }
-
-    createShader();
+    rsc->mHal.funcs.fragment.setActive(rsc, this);
 }
 
 void ProgramFragment::serialize(OStream *stream) const {
diff --git a/libs/rs/rsProgramFragment.h b/libs/rs/rsProgramFragment.h
index 3d28946..7520af0 100644
--- a/libs/rs/rsProgramFragment.h
+++ b/libs/rs/rsProgramFragment.h
@@ -32,11 +32,8 @@
                              uint32_t paramLength);
     virtual ~ProgramFragment();
 
-    virtual void setupGL2(Context *, ProgramFragmentState *, ShaderCache *sc);
+    virtual void setupGL2(Context *, ProgramFragmentState *);
 
-    virtual void createShader();
-    virtual void loadShader(Context *rsc);
-    virtual void init(Context *rsc);
     virtual void serialize(OStream *stream) const;
     virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_PROGRAM_FRAGMENT; }
     static ProgramFragment *createFromStream(Context *rsc, IStream *stream);
diff --git a/libs/rs/rsProgramStore.h b/libs/rs/rsProgramStore.h
index bfe276d..88a06a8 100644
--- a/libs/rs/rsProgramStore.h
+++ b/libs/rs/rsProgramStore.h
@@ -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.
@@ -62,8 +62,6 @@
             RsDepthFunc depthFunc;
         };
         State state;
-
-
     };
     Hal mHal;
 
diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp
index e407d3a..dfd732f 100644
--- a/libs/rs/rsProgramVertex.cpp
+++ b/libs/rs/rsProgramVertex.cpp
@@ -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.
@@ -15,13 +15,6 @@
  */
 
 #include "rsContext.h"
-#ifndef ANDROID_RS_SERIALIZE
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#endif //ANDROID_RS_SERIALIZE
-
 #include "rsProgramVertex.h"
 
 using namespace android;
@@ -32,57 +25,14 @@
                              uint32_t shaderLength, const uint32_t * params,
                              uint32_t paramLength)
     : Program(rsc, shaderText, shaderLength, params, paramLength) {
-    init(rsc);
+    mRSC->mHal.funcs.vertex.init(mRSC, this, mUserShader.string(), mUserShader.length());
 }
 
 ProgramVertex::~ProgramVertex() {
-    if (mShaderID) {
-        mRSC->mShaderCache.cleanupVertex(mShaderID);
-    }
+    mRSC->mHal.funcs.vertex.destroy(mRSC, this);
 }
 
-void ProgramVertex::loadShader(Context *rsc) {
-    Program::loadShader(rsc, GL_VERTEX_SHADER);
-}
-
-void ProgramVertex::createShader(Context *rsc) {
-    if (mUserShader.length() > 1) {
-
-        appendUserConstants();
-
-        for (uint32_t ct=0; ct < mInputCount; ct++) {
-            const Element *e = mInputElements[ct].get();
-            for (uint32_t field=0; field < e->getFieldCount(); field++) {
-                const Element *f = e->getField(field);
-                const char *fn = e->getFieldName(field);
-
-                if (fn[0] == '#') {
-                    continue;
-                }
-
-                // Cannot be complex
-                rsAssert(!f->getFieldCount());
-                switch (f->getComponent().getVectorSize()) {
-                case 1: mShader.append("attribute float ATTRIB_"); break;
-                case 2: mShader.append("attribute vec2 ATTRIB_"); break;
-                case 3: mShader.append("attribute vec3 ATTRIB_"); break;
-                case 4: mShader.append("attribute vec4 ATTRIB_"); break;
-                default:
-                    rsAssert(0);
-                }
-
-                mShader.append(fn);
-                mShader.append(";\n");
-            }
-        }
-        mShader.append(mUserShader);
-    } else {
-        rsc->setError(RS_ERROR_FATAL_UNKNOWN,
-                      "ProgramFragment::createShader cannot create program, shader code not defined");
-    }
-}
-
-void ProgramVertex::setupGL2(Context *rsc, ProgramVertexState *state, ShaderCache *sc) {
+void ProgramVertex::setupGL2(Context *rsc, ProgramVertexState *state) {
     if ((state->mLast.get() == this) && !mDirty) {
         return;
     }
@@ -90,12 +40,12 @@
     rsc->checkError("ProgramVertex::setupGL2 start");
 
     if (!isUserProgram()) {
-        if (mConstants[0].get() == NULL) {
+        if (mHal.state.constants[0].get() == NULL) {
             rsc->setError(RS_ERROR_FATAL_UNKNOWN,
                           "Unable to set fixed function emulation matrices because allocation is missing");
             return;
         }
-        float *f = static_cast<float *>(mConstants[0]->getPtr());
+        float *f = static_cast<float *>(mHal.state.constants[0]->getPtr());
         Matrix4x4 mvp;
         mvp.load(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]);
         Matrix4x4 t;
@@ -106,10 +56,10 @@
         }
     }
 
-    rsc->checkError("ProgramVertex::setupGL2 begin uniforms");
-    setupUserConstants(rsc, sc, false);
-
     state->mLast.set(this);
+
+    rsc->mHal.funcs.vertex.setActive(rsc, this);
+
     rsc->checkError("ProgramVertex::setupGL2");
 }
 
@@ -119,12 +69,12 @@
                       "Attempting to set fixed function emulation matrix projection on user program");
         return;
     }
-    if (mConstants[0].get() == NULL) {
+    if (mHal.state.constants[0].get() == NULL) {
         rsc->setError(RS_ERROR_FATAL_UNKNOWN,
                       "Unable to set fixed function emulation matrix projection because allocation is missing");
         return;
     }
-    float *f = static_cast<float *>(mConstants[0]->getPtr());
+    float *f = static_cast<float *>(mHal.state.constants[0]->getPtr());
     memcpy(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET], m, sizeof(rsc_Matrix));
     mDirty = true;
 }
@@ -135,12 +85,12 @@
                       "Attempting to set fixed function emulation matrix modelview on user program");
         return;
     }
-    if (mConstants[0].get() == NULL) {
+    if (mHal.state.constants[0].get() == NULL) {
         rsc->setError(RS_ERROR_FATAL_UNKNOWN,
                       "Unable to set fixed function emulation matrix modelview because allocation is missing");
         return;
     }
-    float *f = static_cast<float *>(mConstants[0]->getPtr());
+    float *f = static_cast<float *>(mHal.state.constants[0]->getPtr());
     memcpy(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET], m, sizeof(rsc_Matrix));
     mDirty = true;
 }
@@ -151,12 +101,12 @@
                       "Attempting to set fixed function emulation matrix texture on user program");
         return;
     }
-    if (mConstants[0].get() == NULL) {
+    if (mHal.state.constants[0].get() == NULL) {
         rsc->setError(RS_ERROR_FATAL_UNKNOWN,
                       "Unable to set fixed function emulation matrix texture because allocation is missing");
         return;
     }
-    float *f = static_cast<float *>(mConstants[0]->getPtr());
+    float *f = static_cast<float *>(mHal.state.constants[0]->getPtr());
     memcpy(&f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET], m, sizeof(rsc_Matrix));
     mDirty = true;
 }
@@ -167,12 +117,12 @@
                       "Attempting to get fixed function emulation matrix projection on user program");
         return;
     }
-    if (mConstants[0].get() == NULL) {
+    if (mHal.state.constants[0].get() == NULL) {
         rsc->setError(RS_ERROR_FATAL_UNKNOWN,
                       "Unable to get fixed function emulation matrix projection because allocation is missing");
         return;
     }
-    float *f = static_cast<float *>(mConstants[0]->getPtr());
+    float *f = static_cast<float *>(mHal.state.constants[0]->getPtr());
     memcpy(m, &f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET], sizeof(rsc_Matrix));
 }
 
@@ -180,27 +130,13 @@
     if (isUserProgram()) {
         return;
     }
-    float *f = static_cast<float *>(mConstants[0]->getPtr());
+    float *f = static_cast<float *>(mHal.state.constants[0]->getPtr());
     Matrix4x4 mvp;
     mvp.loadMultiply((Matrix4x4 *)&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET],
                      (Matrix4x4 *)&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]);
     mvp.vectorMultiply(v4out, v3in);
 }
 
-void ProgramVertex::init(Context *rsc) {
-    uint32_t attribCount = 0;
-    uint32_t uniformCount = 0;
-    if (mUserShader.size() > 0) {
-        for (uint32_t ct=0; ct < mInputCount; ct++) {
-            initAddUserElement(mInputElements[ct].get(), mAttribNames, NULL, &attribCount, RS_SHADER_ATTR);
-        }
-        for (uint32_t ct=0; ct < mConstantCount; ct++) {
-            initAddUserElement(mConstantTypes[ct]->getElement(), mUniformNames, mUniformArraySizes, &uniformCount, RS_SHADER_UNI);
-        }
-    }
-    createShader(rsc);
-}
-
 void ProgramVertex::serialize(OStream *stream) const {
 }
 
diff --git a/libs/rs/rsProgramVertex.h b/libs/rs/rsProgramVertex.h
index 2a5c863..04224a7 100644
--- a/libs/rs/rsProgramVertex.h
+++ b/libs/rs/rsProgramVertex.h
@@ -31,7 +31,7 @@
                   const uint32_t * params, uint32_t paramLength);
     virtual ~ProgramVertex();
 
-    virtual void setupGL2(Context *rsc, ProgramVertexState *state, ShaderCache *sc);
+    virtual void setupGL2(Context *rsc, ProgramVertexState *state);
 
     void setProjectionMatrix(Context *, const rsc_Matrix *) const;
     void getProjectionMatrix(Context *, rsc_Matrix *) const;
@@ -40,10 +40,6 @@
 
     void transformToScreen(Context *, float *v4out, const float *v3in) const;
 
-    virtual void createShader(Context *);
-    virtual void loadShader(Context *);
-    virtual void init(Context *);
-
     virtual void serialize(OStream *stream) const;
     virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_PROGRAM_VERTEX; }
     static ProgramVertex *createFromStream(Context *rsc, IStream *stream);
diff --git a/libs/rs/rsScriptC_LibGL.cpp b/libs/rs/rsScriptC_LibGL.cpp
index 71f1312..ecda485 100644
--- a/libs/rs/rsScriptC_LibGL.cpp
+++ b/libs/rs/rsScriptC_LibGL.cpp
@@ -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,6 +21,9 @@
 #include "rsMatrix2x2.h"
 
 #include "utils/Timers.h"
+#include "driver/rsdVertexArray.h"
+#include "driver/rsdShaderCache.h"
+#include "driver/rsdCore.h"
 
 #define GL_GLEXT_PROTOTYPES
 
@@ -134,6 +137,11 @@
         return;
     }
 
+    RsdHal *dc = (RsdHal *)rsc->mHal.drv;
+    if (!dc->gl.shaderCache->setup(rsc)) {
+        return;
+    }
+
     //LOGE("Quad");
     //LOGE("%4.2f, %4.2f, %4.2f", x1, y1, z1);
     //LOGE("%4.2f, %4.2f, %4.2f", x2, y2, z2);
@@ -143,12 +151,12 @@
     float vtx[] = {x1,y1,z1, x2,y2,z2, x3,y3,z3, x4,y4,z4};
     const float tex[] = {u1,v1, u2,v2, u3,v3, u4,v4};
 
-    VertexArray::Attrib attribs[2];
+    RsdVertexArray::Attrib attribs[2];
     attribs[0].set(GL_FLOAT, 3, 12, false, (uint32_t)vtx, "ATTRIB_position");
     attribs[1].set(GL_FLOAT, 2, 8, false, (uint32_t)tex, "ATTRIB_texture0");
 
-    VertexArray va(attribs, 2);
-    va.setupGL2(rsc, &rsc->mStateVertexArray, &rsc->mShaderCache);
+    RsdVertexArray va(attribs, 2);
+    va.setupGL2(rsc);
 
     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
 }
diff --git a/libs/rs/rsType.cpp b/libs/rs/rsType.cpp
index cd2be94..10e3182 100644
--- a/libs/rs/rsType.cpp
+++ b/libs/rs/rsType.cpp
@@ -274,17 +274,16 @@
 namespace android {
 namespace renderscript {
 
-}
-}
-
-RsType rsaTypeCreate(RsContext con, RsElement _e, uint32_t dimX,
+RsType rsi_TypeCreate(Context *rsc, RsElement _e, uint32_t dimX,
                      uint32_t dimY, uint32_t dimZ, bool mips, bool faces) {
-    Context *rsc = static_cast<Context *>(con);
     Element *e = static_cast<Element *>(_e);
 
     return Type::getType(rsc, e, dimX, dimY, dimZ, mips, faces);
 }
 
+}
+}
+
 void rsaTypeGetNativeData(RsContext con, RsType type, uint32_t *typeData, uint32_t typeDataSize) {
     rsAssert(typeDataSize == 6);
     // Pack the data in the follofing way mDimX; mDimY; mDimZ;
diff --git a/libs/rs/rsType.h b/libs/rs/rsType.h
index 90ae039..086db33 100644
--- a/libs/rs/rsType.h
+++ b/libs/rs/rsType.h
@@ -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.
@@ -18,7 +18,6 @@
 #define ANDROID_STRUCTURED_TYPE_H
 
 #include "rsElement.h"
-#include "rsVertexArray.h"
 
 // ---------------------------------------------------------------------------
 namespace android {
diff --git a/libs/rs/rs_hal.h b/libs/rs/rs_hal.h
index 4283d4a..d2f273b 100644
--- a/libs/rs/rs_hal.h
+++ b/libs/rs/rs_hal.h
@@ -32,6 +32,9 @@
 class ScriptC;
 class ProgramStore;
 class ProgramRaster;
+class ProgramVertex;
+class ProgramFragment;
+class Mesh;
 
 typedef void *(*RsHalSymbolLookupFunc)(void *usrptr, char const *symbolName);
 
@@ -98,6 +101,25 @@
         void (*destroy)(const Context *rsc, const ProgramRaster *ps);
     } raster;
 
+    struct {
+        bool (*init)(const Context *rsc, const ProgramVertex *pv,
+                     const char* shader, uint32_t shaderLen);
+        void (*setActive)(const Context *rsc, const ProgramVertex *pv);
+        void (*destroy)(const Context *rsc, const ProgramVertex *pv);
+    } vertex;
+
+    struct {
+        bool (*init)(const Context *rsc, const ProgramFragment *pf,
+                     const char* shader, uint32_t shaderLen);
+        void (*setActive)(const Context *rsc, const ProgramFragment *pf);
+        void (*destroy)(const Context *rsc, const ProgramFragment *pf);
+    } fragment;
+
+    struct {
+        bool (*init)(const Context *rsc, const Mesh *m);
+        void (*draw)(const Context *rsc, const Mesh *m, uint32_t primIndex, uint32_t start, uint32_t len);
+        void (*destroy)(const Context *rsc, const Mesh *m);
+    } mesh;
 
 } RsdHalFunctions;
 
diff --git a/libs/rs/rsg_generator.c b/libs/rs/rsg_generator.c
index 0059f19..1d8b9b5 100644
--- a/libs/rs/rsg_generator.c
+++ b/libs/rs/rsg_generator.c
@@ -97,9 +97,20 @@
     }
 }
 
-void printFuncDecl(FILE *f, const ApiEntry *api, const char *prefix, int addContext) {
+void printFuncDecl(FILE *f, const ApiEntry *api, const char *prefix, int addContext, int isFnPtr) {
     printVarTypeAndName(f, &api->ret);
-    fprintf(f, " %s%s (", prefix, api->name);
+    if (isFnPtr) {
+        char t[1024];
+        strcpy(t, api->name);
+        if (strlen(prefix) == 0) {
+            if (t[0] > 'A' && t[0] < 'Z') {
+                t[0] -= 'A' - 'a';
+            }
+        }
+        fprintf(f, " (* %s%s) (", prefix, api->name);
+    } else {
+        fprintf(f, " %s%s (", prefix, api->name);
+    }
     if (!api->nocontext) {
         if (addContext) {
             fprintf(f, "Context *");
@@ -114,12 +125,24 @@
 void printFuncDecls(FILE *f, const char *prefix, int addContext) {
     int ct;
     for (ct=0; ct < apiCount; ct++) {
-        printFuncDecl(f, &apis[ct], prefix, addContext);
+        printFuncDecl(f, &apis[ct], prefix, addContext, 0);
         fprintf(f, ";\n");
     }
     fprintf(f, "\n\n");
 }
 
+void printFuncPointers(FILE *f, int addContext) {
+    fprintf(f, "\n");
+    fprintf(f, "typedef struct RsApiEntrypoints {\n");
+    int ct;
+    for (ct=0; ct < apiCount; ct++) {
+        fprintf(f, "    ");
+        printFuncDecl(f, &apis[ct], "", addContext, 1);
+        fprintf(f, ";\n");
+    }
+    fprintf(f, "} RsApiEntrypoints_t;\n\n");
+}
+
 void printPlaybackFuncs(FILE *f, const char *prefix) {
     int ct;
     for (ct=0; ct < apiCount; ct++) {
@@ -172,26 +195,40 @@
     fprintf(f, "#include \"rsHandcode.h\"\n");
     fprintf(f, "\n");
 
+    printFuncPointers(f, 0);
+
+    // Generate RS funcs for local fifo
     for (ct=0; ct < apiCount; ct++) {
         int needFlush = 0;
         const ApiEntry * api = &apis[ct];
 
-        if (api->direct) {
-            continue;
-        }
-
-        printFuncDecl(f, api, "rs", 0);
+        fprintf(f, "static ");
+        printFuncDecl(f, api, "LF_", 0, 0);
         fprintf(f, "\n{\n");
-        if (api->handcodeApi) {
-            fprintf(f, "    rsHCAPI_%s(rsc", api->name);
+        if (api->handcodeApi || api->direct) {
+            if (api->handcodeApi) {
+                fprintf(f, "    rsHCAPI_%s(rsc", api->name);
+            } else {
+                fprintf(f, "    ");
+                if (api->ret.typeName[0]) {
+                    fprintf(f, "return ");
+                }
+                fprintf(f, "rsi_%s(", api->name);
+                if (!api->nocontext) {
+                    fprintf(f, "(Context *)rsc");
+                }
+            }
             for (ct2=0; ct2 < api->paramCount; ct2++) {
                 const VarType *vt = &api->params[ct2];
-                fprintf(f, ", %s", vt->name);
+                if (ct2 > 0 || !api->nocontext) {
+                    fprintf(f, ", ");
+                }
+                fprintf(f, "%s", vt->name);
             }
             fprintf(f, ");\n");
         } else {
             fprintf(f, "    ThreadIO *io = &((Context *)rsc)->mIO;\n");
-            fprintf(f, "    uint32_t size = sizeof(RS_CMD_%s);\n", api->name);
+            fprintf(f, "    const uint32_t size = sizeof(RS_CMD_%s);\n", api->name);
             if (hasInlineDataPointers(api)) {
                 fprintf(f, "    uint32_t dataSize = 0;\n");
                 for (ct2=0; ct2 < api->paramCount; ct2++) {
@@ -203,9 +240,16 @@
             }
 
             //fprintf(f, "    LOGE(\"add command %s\\n\");\n", api->name);
-            fprintf(f, "    RS_CMD_%s *cmd = static_cast<RS_CMD_%s *>(io->mToCore.reserve(sizeof(RS_CMD_%s)));\n", api->name, api->name, api->name);
             if (hasInlineDataPointers(api)) {
+                fprintf(f, "    RS_CMD_%s *cmd = NULL;\n", api->name);
+                fprintf(f, "    if (dataSize < 1024) {;\n");
+                fprintf(f, "        cmd = static_cast<RS_CMD_%s *>(io->mToCore.reserve(dataSize + size));\n", api->name);
+                fprintf(f, "    } else {\n");
+                fprintf(f, "        cmd = static_cast<RS_CMD_%s *>(io->mToCore.reserve(size));\n", api->name);
+                fprintf(f, "    }\n");
                 fprintf(f, "    uint8_t *payload = (uint8_t *)&cmd[1];\n");
+            } else {
+                fprintf(f, "    RS_CMD_%s *cmd = static_cast<RS_CMD_%s *>(io->mToCore.reserve(size));\n", api->name, api->name);
             }
 
             for (ct2=0; ct2 < api->paramCount; ct2++) {
@@ -252,6 +296,43 @@
         }
         fprintf(f, "};\n\n");
     }
+
+    fprintf(f, "\n");
+    fprintf(f, "static RsApiEntrypoints_t s_LocalTable = {\n");
+    for (ct=0; ct < apiCount; ct++) {
+        fprintf(f, "    LF_%s,\n", apis[ct].name);
+    }
+    fprintf(f, "};\n");
+
+    fprintf(f, "static RsApiEntrypoints_t *s_CurrentTable = &s_LocalTable;\n\n");
+
+    for (ct=0; ct < apiCount; ct++) {
+        int needFlush = 0;
+        const ApiEntry * api = &apis[ct];
+
+        printFuncDecl(f, api, "rs", 0, 0);
+        fprintf(f, "\n{\n");
+        fprintf(f, "    ");
+        if (api->ret.typeName[0]) {
+            fprintf(f, "return ");
+        }
+        fprintf(f, "s_CurrentTable->%s(", api->name);
+
+        if (!api->nocontext) {
+            fprintf(f, "(Context *)rsc");
+        }
+
+        for (ct2=0; ct2 < api->paramCount; ct2++) {
+            const VarType *vt = &api->params[ct2];
+            if (ct2 > 0 || !api->nocontext) {
+                fprintf(f, ", ");
+            }
+            fprintf(f, "%s", vt->name);
+        }
+        fprintf(f, ");\n");
+        fprintf(f, "}\n\n");
+    }
+
 }
 
 void printPlaybackCpp(FILE *f) {
@@ -373,6 +454,19 @@
             printPlaybackCpp(f);
         }
         break;
+
+        case '4': // rsgApiStream.cpp
+        {
+            printFileHeader(f);
+            printPlaybackCpp(f);
+        }
+
+        case '5': // rsgApiStreamReplay.cpp
+        {
+            printFileHeader(f);
+            printPlaybackCpp(f);
+        }
+        break;
     }
     fclose(f);
     return 0;
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 5865b92..b914169 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1228,6 +1228,84 @@
     public native void attachAuxEffect(int effectId);
 
     /**
+     * Sets the parameter indicated by key.
+     * @param key key indicates the parameter to be set.
+     * @param value value of the parameter to be set.
+     * @return true if the parameter is set successfully, false otherwise
+     * {@hide}
+     */
+    public native boolean setParameter(int key, Parcel value);
+
+    /**
+     * Sets the parameter indicated by key.
+     * @param key key indicates the parameter to be set.
+     * @param value value of the parameter to be set.
+     * @return true if the parameter is set successfully, false otherwise
+     * {@hide}
+     */
+    public boolean setParameter(int key, String value) {
+        Parcel p = Parcel.obtain();
+        p.writeString(value);
+        return setParameter(key, p);
+    }
+
+    /**
+     * Sets the parameter indicated by key.
+     * @param key key indicates the parameter to be set.
+     * @param value value of the parameter to be set.
+     * @return true if the parameter is set successfully, false otherwise
+     * {@hide}
+     */
+    public boolean setParameter(int key, int value) {
+        Parcel p = Parcel.obtain();
+        p.writeInt(value);
+        return setParameter(key, p);
+    }
+
+    /**
+     * Gets the value of the parameter indicated by key.
+     * @param key key indicates the parameter to get.
+     * @param reply value of the parameter to get.
+     */
+    private native void getParameter(int key, Parcel reply);
+
+    /**
+     * Gets the value of the parameter indicated by key.
+     * @param key key indicates the parameter to get.
+     * @return value of the parameter.
+     * {@hide}
+     */
+    public Parcel getParcelParameter(int key) {
+        Parcel p = Parcel.obtain();
+        getParameter(key, p);
+        return p;
+    }
+
+    /**
+     * Gets the value of the parameter indicated by key.
+     * @param key key indicates the parameter to get.
+     * @return value of the parameter.
+     * {@hide}
+     */
+    public String getStringParameter(int key) {
+        Parcel p = Parcel.obtain();
+        getParameter(key, p);
+        return p.readString();
+    }
+
+    /**
+     * Gets the value of the parameter indicated by key.
+     * @param key key indicates the parameter to get.
+     * @return value of the parameter.
+     * {@hide}
+     */
+    public int getIntParameter(int key) {
+        Parcel p = Parcel.obtain();
+        getParameter(key, p);
+        return p.readInt();
+    }
+
+    /**
      * Sets the send level of the player to the attached auxiliary effect
      * {@see #attachAuxEffect(int)}. The level value range is 0 to 1.0.
      * <p>By default the send level is 0, so even if an effect is attached to the player
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 80cc94e..790eaa3 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -431,34 +431,8 @@
             mFileSize = fileSize;
 
             if (!isDirectory) {
-                // special case certain file names
-                // I use regionMatches() instead of substring() below
-                // to avoid memory allocation
-                int lastSlash = path.lastIndexOf('/');
-                if (lastSlash >= 0 && lastSlash + 2 < path.length()) {
-                    if (!noMedia) {
-                        // ignore those ._* files created by MacOS
-                        if (path.regionMatches(lastSlash + 1, "._", 0, 2)) {
-                            noMedia = true;
-                        }
-
-                        // ignore album art files created by Windows Media Player:
-                        // Folder.jpg, AlbumArtSmall.jpg, AlbumArt_{...}_Large.jpg
-                        // and AlbumArt_{...}_Small.jpg
-                        if (path.regionMatches(true, path.length() - 4, ".jpg", 0, 4)) {
-                            if (path.regionMatches(true, lastSlash + 1, "AlbumArt_{", 0, 10) ||
-                                    path.regionMatches(true, lastSlash + 1, "AlbumArt.", 0, 9)) {
-                                noMedia = true;
-                            }
-                            int length = path.length() - lastSlash - 1;
-                            if ((length == 17 && path.regionMatches(
-                                    true, lastSlash + 1, "AlbumArtSmall", 0, 13)) ||
-                                    (length == 10
-                                     && path.regionMatches(true, lastSlash + 1, "Folder", 0, 6))) {
-                                noMedia = true;
-                            }
-                        }
-                    }
+                if (!noMedia && isNoMediaFile(path)) {
+                    noMedia = true;
                 }
                 mNoMedia = noMedia;
 
@@ -1231,6 +1205,40 @@
         }
     }
 
+    private static boolean isNoMediaFile(String path) {
+        File file = new File(path);
+        if (file.isDirectory()) return false;
+
+        // special case certain file names
+        // I use regionMatches() instead of substring() below
+        // to avoid memory allocation
+        int lastSlash = path.lastIndexOf('/');
+        if (lastSlash >= 0 && lastSlash + 2 < path.length()) {
+            // ignore those ._* files created by MacOS
+            if (path.regionMatches(lastSlash + 1, "._", 0, 2)) {
+                return true;
+            }
+
+            // ignore album art files created by Windows Media Player:
+            // Folder.jpg, AlbumArtSmall.jpg, AlbumArt_{...}_Large.jpg
+            // and AlbumArt_{...}_Small.jpg
+            if (path.regionMatches(true, path.length() - 4, ".jpg", 0, 4)) {
+                if (path.regionMatches(true, lastSlash + 1, "AlbumArt_{", 0, 10) ||
+                        path.regionMatches(true, lastSlash + 1, "AlbumArt.", 0, 9)) {
+                    return true;
+                }
+                int length = path.length() - lastSlash - 1;
+                if ((length == 17 && path.regionMatches(
+                        true, lastSlash + 1, "AlbumArtSmall", 0, 13)) ||
+                        (length == 10
+                         && path.regionMatches(true, lastSlash + 1, "Folder", 0, 6))) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     public static boolean isNoMediaPath(String path) {
         if (path == null) return false;
 
@@ -1252,7 +1260,7 @@
             }
             offset = slashIndex;
         }
-        return false;
+        return isNoMediaFile(path);
     }
 
     public void scanMtpFile(String path, String volumeName, int objectHandle, int format) {
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 23a77d4..8763ebd 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -814,6 +814,39 @@
     return service->pullBatteryData(reply);
 }
 
+static jboolean
+android_media_MediaPlayer_setParameter(JNIEnv *env, jobject thiz, jint key, jobject java_request)
+{
+    LOGV("setParameter: key %d", key);
+    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+    if (mp == NULL ) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return false;
+    }
+
+    Parcel *request = parcelForJavaObject(env, java_request);
+    status_t err = mp->setParameter(key, *request);
+    if (err == OK) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+static void
+android_media_MediaPlayer_getParameter(JNIEnv *env, jobject thiz, jint key, jobject java_reply)
+{
+    LOGV("getParameter: key %d", key);
+    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+    if (mp == NULL ) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return;
+    }
+
+    Parcel *reply = parcelForJavaObject(env, java_reply);
+    process_media_player_call(env, thiz, mp->getParameter(key, reply), NULL, NULL );
+}
+
 // ----------------------------------------------------------------------------
 
 static JNINativeMethod gMethods[] = {
@@ -850,6 +883,8 @@
     {"setAuxEffectSendLevel", "(F)V",                           (void *)android_media_MediaPlayer_setAuxEffectSendLevel},
     {"attachAuxEffect",     "(I)V",                             (void *)android_media_MediaPlayer_attachAuxEffect},
     {"native_pullBatteryData", "(Landroid/os/Parcel;)I",        (void *)android_media_MediaPlayer_pullBatteryData},
+    {"setParameter",        "(ILandroid/os/Parcel;)Z",          (void *)android_media_MediaPlayer_setParameter},
+    {"getParameter",        "(ILandroid/os/Parcel;)V",          (void *)android_media_MediaPlayer_getParameter},
 };
 
 static const char* const kClassPathName = "android/media/MediaPlayer";
diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp
index 8885bd5..76a8a91 100644
--- a/media/libmedia/IMediaPlayer.cpp
+++ b/media/libmedia/IMediaPlayer.cpp
@@ -48,6 +48,8 @@
     SET_AUX_EFFECT_SEND_LEVEL,
     ATTACH_AUX_EFFECT,
     SET_VIDEO_SURFACETEXTURE,
+    SET_PARAMETER,
+    GET_PARAMETER,
 };
 
 class BpMediaPlayer: public BpInterface<IMediaPlayer>
@@ -236,6 +238,26 @@
         return reply.readInt32();
     }
 
+    status_t setParameter(int key, const Parcel& request)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
+        data.writeInt32(key);
+        if (request.dataSize() > 0) {
+            data.appendFrom(const_cast<Parcel *>(&request), 0, request.dataSize());
+        }
+        remote()->transact(SET_PARAMETER, data, &reply);
+        return reply.readInt32();
+    }
+
+    status_t getParameter(int key, Parcel *reply)
+    {
+        Parcel data;
+        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
+        data.writeInt32(key);
+        return remote()->transact(GET_PARAMETER, data, reply);
+    }
+
 };
 
 IMPLEMENT_META_INTERFACE(MediaPlayer, "android.media.IMediaPlayer");
@@ -361,6 +383,23 @@
             reply->writeInt32(attachAuxEffect(data.readInt32()));
             return NO_ERROR;
         } break;
+        case SET_PARAMETER: {
+            CHECK_INTERFACE(IMediaPlayer, data, reply);
+            int key = data.readInt32();
+
+            Parcel request;
+            if (data.dataAvail() > 0) {
+                request.appendFrom(
+                        const_cast<Parcel *>(&data), data.dataPosition(), data.dataAvail());
+            }
+            request.setDataPosition(0);
+            reply->writeInt32(setParameter(key, request));
+            return NO_ERROR;
+        } break;
+        case GET_PARAMETER: {
+            CHECK_INTERFACE(IMediaPlayer, data, reply);
+            return getParameter(data.readInt32(), reply);
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/media/libmedia/MediaScanner.cpp b/media/libmedia/MediaScanner.cpp
index 4e22175..28c86426 100644
--- a/media/libmedia/MediaScanner.cpp
+++ b/media/libmedia/MediaScanner.cpp
@@ -135,20 +135,21 @@
         }
         if (type == DT_REG || type == DT_DIR) {
             if (type == DT_DIR) {
+                bool childNoMedia = noMedia;
                 // set noMedia flag on directories with a name that starts with '.'
                 // for example, the Mac ".Trashes" directory
                 if (name[0] == '.')
-                    noMedia = true;
+                    childNoMedia = true;
 
                 // report the directory to the client
                 if (stat(path, &statbuf) == 0) {
-                    client.scanFile(path, statbuf.st_mtime, 0, true, noMedia);
+                    client.scanFile(path, statbuf.st_mtime, 0, true, childNoMedia);
                 }
 
                 // and now process its contents
                 strcat(fileSpot, "/");
                 int err = doProcessDirectory(path, pathRemaining - nameLength - 1, client,
-                        noMedia, exceptionCheck, exceptionEnv);
+                        childNoMedia, exceptionCheck, exceptionEnv);
                 if (err) {
                     // pass exceptions up - ignore other errors
                     if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure;
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index 9daa80f..28e07ff 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -553,6 +553,28 @@
     return mPlayer->attachAuxEffect(effectId);
 }
 
+status_t MediaPlayer::setParameter(int key, const Parcel& request)
+{
+    LOGV("MediaPlayer::setParameter(%d)", key);
+    Mutex::Autolock _l(mLock);
+    if (mPlayer != NULL) {
+        return  mPlayer->setParameter(key, request);
+    }
+    LOGV("setParameter: no active player");
+    return INVALID_OPERATION;
+}
+
+status_t MediaPlayer::getParameter(int key, Parcel *reply)
+{
+    LOGV("MediaPlayer::getParameter(%d)", key);
+    Mutex::Autolock _l(mLock);
+    if (mPlayer != NULL) {
+         return  mPlayer->getParameter(key, reply);
+    }
+    LOGV("getParameter: no active player");
+    return INVALID_OPERATION;
+}
+
 void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj)
 {
     LOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 9dd353b..3b2cf10 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1024,6 +1024,20 @@
     return NO_ERROR;
 }
 
+status_t MediaPlayerService::Client::setParameter(int key, const Parcel &request) {
+    LOGV("[%d] setParameter(%d)", mConnId, key);
+    sp<MediaPlayerBase> p = getPlayer();
+    if (p == 0) return UNKNOWN_ERROR;
+    return p->setParameter(key, request);
+}
+
+status_t MediaPlayerService::Client::getParameter(int key, Parcel *reply) {
+    LOGV("[%d] getParameter(%d)", mConnId, key);
+    sp<MediaPlayerBase> p = getPlayer();
+    if (p == 0) return UNKNOWN_ERROR;
+    return p->getParameter(key, reply);
+}
+
 void MediaPlayerService::Client::notify(
         void* cookie, int msg, int ext1, int ext2, const Parcel *obj)
 {
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 31b518e..6c4071f 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -279,6 +279,8 @@
                                             Parcel *reply);
         virtual status_t        setAuxEffectSendLevel(float level);
         virtual status_t        attachAuxEffect(int effectId);
+        virtual status_t        setParameter(int key, const Parcel &request);
+        virtual status_t        getParameter(int key, Parcel *reply);
 
         sp<MediaPlayerBase>     createPlayer(player_type playerType);
 
diff --git a/media/libmediaplayerservice/MidiFile.h b/media/libmediaplayerservice/MidiFile.h
index a98231c..b35696f1 100644
--- a/media/libmediaplayerservice/MidiFile.h
+++ b/media/libmediaplayerservice/MidiFile.h
@@ -55,6 +55,13 @@
     virtual status_t    invoke(const Parcel& request, Parcel *reply) {
         return INVALID_OPERATION;
     }
+    virtual status_t    setParameter(int key, const Parcel &request) {
+        return INVALID_OPERATION;
+    }
+    virtual status_t    getParameter(int key, Parcel *reply) {
+        return INVALID_OPERATION;
+    }
+
 
 private:
             status_t    createOutputTrack();
diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp
index 6429395..02ec911 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.cpp
+++ b/media/libmediaplayerservice/StagefrightPlayer.cpp
@@ -177,6 +177,16 @@
     mPlayer->setAudioSink(audioSink);
 }
 
+status_t StagefrightPlayer::setParameter(int key, const Parcel &request) {
+    LOGV("setParameter");
+    return mPlayer->setParameter(key, request);
+}
+
+status_t StagefrightPlayer::getParameter(int key, Parcel *reply) {
+    LOGV("getParameter");
+    return mPlayer->getParameter(key, reply);
+}
+
 status_t StagefrightPlayer::getMetadata(
         const media::Metadata::Filter& ids, Parcel *records) {
     using media::Metadata;
diff --git a/media/libmediaplayerservice/StagefrightPlayer.h b/media/libmediaplayerservice/StagefrightPlayer.h
index e2796d2..ddd37e4 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.h
+++ b/media/libmediaplayerservice/StagefrightPlayer.h
@@ -55,6 +55,8 @@
     virtual player_type playerType();
     virtual status_t invoke(const Parcel &request, Parcel *reply);
     virtual void setAudioSink(const sp<AudioSink> &audioSink);
+    virtual status_t setParameter(int key, const Parcel &request);
+    virtual status_t getParameter(int key, Parcel *reply);
 
     virtual status_t getMetadata(
             const media::Metadata::Filter& ids, Parcel *records);
diff --git a/media/libmediaplayerservice/TestPlayerStub.h b/media/libmediaplayerservice/TestPlayerStub.h
index d9c3db3..802a11b 100644
--- a/media/libmediaplayerservice/TestPlayerStub.h
+++ b/media/libmediaplayerservice/TestPlayerStub.h
@@ -99,6 +99,12 @@
     virtual status_t invoke(const android::Parcel& in, android::Parcel *out) {
         return mPlayer->invoke(in, out);
     }
+    virtual status_t setParameter(int key, const Parcel &request) {
+        return mPlayer->setParameter(key, request);
+    }
+    virtual status_t getParameter(int key, Parcel *reply) {
+        return mPlayer->getParameter(key, reply);
+    }
 
 
     // @return true if the current build is 'eng' or 'test' and the
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 0eca958..e1213f4 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -246,6 +246,14 @@
     mPlayer->setAudioSink(audioSink);
 }
 
+status_t NuPlayerDriver::setParameter(int key, const Parcel &request) {
+    return INVALID_OPERATION;
+}
+
+status_t NuPlayerDriver::getParameter(int key, Parcel *reply) {
+    return INVALID_OPERATION;
+}
+
 status_t NuPlayerDriver::getMetadata(
         const media::Metadata::Filter& ids, Parcel *records) {
     return INVALID_OPERATION;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
index 67d0f3e..145fd80 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -52,6 +52,8 @@
     virtual player_type playerType();
     virtual status_t invoke(const Parcel &request, Parcel *reply);
     virtual void setAudioSink(const sp<AudioSink> &audioSink);
+    virtual status_t setParameter(int key, const Parcel &request);
+    virtual status_t getParameter(int key, Parcel *reply);
 
     virtual status_t getMetadata(
             const media::Metadata::Filter& ids, Parcel *records);
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 1bfdd8e..974efa7 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -312,7 +312,7 @@
     if (mDecryptHandle != NULL) {
         CHECK(mDrmManagerClient);
         if (RightsStatus::RIGHTS_VALID != mDecryptHandle->status) {
-            notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_NO_LICENSE);
+            notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_DRM_NO_LICENSE);
         }
     }
 
@@ -1801,7 +1801,7 @@
     if (mDecryptHandle != NULL) {
         CHECK(mDrmManagerClient);
         if (RightsStatus::RIGHTS_VALID != mDecryptHandle->status) {
-            notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_NO_LICENSE);
+            notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_DRM_NO_LICENSE);
         }
     }
 
@@ -1905,4 +1905,11 @@
     postCheckAudioStatusEvent_l(0 /* delayUs */);
 }
 
+status_t AwesomePlayer::setParameter(int key, const Parcel &request) {
+    return OK;
+}
+
+status_t AwesomePlayer::getParameter(int key, Parcel *reply) {
+    return OK;
+}
 }  // namespace android
diff --git a/media/libstagefright/DRMExtractor.cpp b/media/libstagefright/DRMExtractor.cpp
index c4ed516..1f3d581 100644
--- a/media/libstagefright/DRMExtractor.cpp
+++ b/media/libstagefright/DRMExtractor.cpp
@@ -146,18 +146,14 @@
     DrmBuffer *pDecryptedDrmBuffer = &decryptedDrmBuffer;
 
     if ((err = mDrmManagerClient->decrypt(mDecryptHandle, mTrackId,
-            &encryptedDrmBuffer, &pDecryptedDrmBuffer)) != DRM_NO_ERROR) {
+            &encryptedDrmBuffer, &pDecryptedDrmBuffer)) != NO_ERROR) {
 
         if (decryptedDrmBuffer.data) {
             delete [] decryptedDrmBuffer.data;
             decryptedDrmBuffer.data = NULL;
         }
 
-        if (err == DRM_ERROR_LICENSE_EXPIRED) {
-            return ERROR_NO_LICENSE;
-        } else {
-            return ERROR_IO;
-        }
+        return err;
     }
     CHECK(pDecryptedDrmBuffer == &decryptedDrmBuffer);
 
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 8c61064..2c17d92 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -88,6 +88,9 @@
     status_t getDuration(int64_t *durationUs);
     status_t getPosition(int64_t *positionUs);
 
+    status_t setParameter(int key, const Parcel &request);
+    status_t getParameter(int key, Parcel *reply);
+
     status_t seekTo(int64_t timeUs);
 
     // This is a mask of MediaExtractor::Flags.
diff --git a/media/tests/players/invoke_mock_media_player.cpp b/media/tests/players/invoke_mock_media_player.cpp
index d571106..ed3051b 100644
--- a/media/tests/players/invoke_mock_media_player.cpp
+++ b/media/tests/players/invoke_mock_media_player.cpp
@@ -84,6 +84,9 @@
     virtual status_t    setLooping(int loop) {return OK;}
     virtual player_type playerType() {return TEST_PLAYER;}
     virtual status_t    invoke(const Parcel& request, Parcel *reply);
+    virtual status_t    setParameter(int key, const Parcel &request) {return OK;}
+    virtual status_t    getParameter(int key, Parcel *reply) {return OK;}
+
 
   private:
     // Take a request, copy it to the reply.
diff --git a/opengl/java/com/google/android/gles_jni/EGLImpl.java b/opengl/java/com/google/android/gles_jni/EGLImpl.java
index 8a7124d..f162d40 100644
--- a/opengl/java/com/google/android/gles_jni/EGLImpl.java
+++ b/opengl/java/com/google/android/gles_jni/EGLImpl.java
@@ -18,10 +18,10 @@
 
 import javax.microedition.khronos.egl.*;
 
+import android.graphics.SurfaceTexture;
 import android.view.Surface;
 import android.view.SurfaceView;
 import android.view.SurfaceHolder;
-import android.view.View;
 
 public class EGLImpl implements EGL10 {
     private EGLContextImpl mContext = new EGLContextImpl(-1);
@@ -71,19 +71,28 @@
     }
 
     public EGLSurface eglCreateWindowSurface(EGLDisplay display, EGLConfig config, Object native_window, int[] attrib_list) {
-        Surface sur;
+        Surface sur = null;
         if (native_window instanceof SurfaceView) {
             SurfaceView surfaceView = (SurfaceView)native_window;
             sur = surfaceView.getHolder().getSurface();
         } else if (native_window instanceof SurfaceHolder) {
             SurfaceHolder holder = (SurfaceHolder)native_window;
             sur = holder.getSurface();
+        }
+
+        int eglSurfaceId;
+        if (sur != null) {
+            eglSurfaceId = _eglCreateWindowSurface(display, config, sur, attrib_list);
+        } else if (native_window instanceof SurfaceTexture) {
+            eglSurfaceId = _eglCreateWindowSurfaceTexture(display, config,
+                    ((SurfaceTexture) native_window).mSurfaceTexture, attrib_list);
         } else {
             throw new java.lang.UnsupportedOperationException(
                 "eglCreateWindowSurface() can only be called with an instance of " +
-                "SurfaceView or SurfaceHolder at the moment, this will be fixed later.");
+                "SurfaceView, SurfaceHolder or SurfaceTexture at the moment, " + 
+                "this will be fixed later.");
         }
-        int eglSurfaceId = _eglCreateWindowSurface(display, config, sur, attrib_list);
+
         if (eglSurfaceId == 0) {
             return EGL10.EGL_NO_SURFACE;
         }
@@ -134,6 +143,7 @@
     private native int _eglCreatePbufferSurface(EGLDisplay display, EGLConfig config, int[] attrib_list);
     private native void _eglCreatePixmapSurface(EGLSurface sur, EGLDisplay display, EGLConfig config, Object native_pixmap, int[] attrib_list);
     private native int _eglCreateWindowSurface(EGLDisplay display, EGLConfig config, Object native_window, int[] attrib_list);
+    private native int _eglCreateWindowSurfaceTexture(EGLDisplay display, EGLConfig config, int native_window, int[] attrib_list);
     private native int _eglGetDisplay(Object native_display);
     private native int _eglGetCurrentContext();
     private native int _eglGetCurrentDisplay();
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 40fa148..97e913ba 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -440,7 +440,6 @@
         android_native_buffer_t* src, void const* src_vaddr,
         const Region& clip)
 {
-    // FIXME: use copybit if possible
     // NOTE: dst and src must be the same format
     
     Region::const_iterator cur = clip.begin();
diff --git a/opengl/libagl2/src/egl.cpp b/opengl/libagl2/src/egl.cpp
index ba771c3..ec5889d 100644
--- a/opengl/libagl2/src/egl.cpp
+++ b/opengl/libagl2/src/egl.cpp
@@ -432,7 +432,6 @@
         android_native_buffer_t* src, void const* src_vaddr,
         const Region& clip)
 {
-    // FIXME: use copybit if possible
     // NOTE: dst and src must be the same format
 
     Region::const_iterator cur = clip.begin();
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index 1f06dcc..c47383a 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -316,9 +316,10 @@
                 filteredPos++;
             }
 
-            throw new IllegalArgumentException("position " + position + " out of "
-                    + "range of showable actions, filtered count = "
-                    + "= " + getCount() + ", keyguardshowing=" + mKeyguardShowing
+            throw new IllegalArgumentException("position " + position
+                    + " out of range of showable actions"
+                    + ", filtered count=" + getCount()
+                    + ", keyguardshowing=" + mKeyguardShowing
                     + ", provisioned=" + mDeviceProvisioned);
         }
 
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 27b7e1c..6a800f0 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -28,17 +28,15 @@
 
 import com.android.internal.view.RootViewSurfaceTaker;
 import com.android.internal.view.StandaloneActionMode;
-import com.android.internal.view.menu.ActionMenuView;
 import com.android.internal.view.menu.ContextMenuBuilder;
-import com.android.internal.view.menu.ListMenuPresenter;
 import com.android.internal.view.menu.IconMenuPresenter;
+import com.android.internal.view.menu.ListMenuPresenter;
 import com.android.internal.view.menu.MenuBuilder;
 import com.android.internal.view.menu.MenuDialogHelper;
-import com.android.internal.view.menu.MenuItemImpl;
-import com.android.internal.view.menu.MenuPopupHelper;
-import com.android.internal.view.menu.MenuView;
 import com.android.internal.view.menu.MenuPresenter;
+import com.android.internal.view.menu.MenuView;
 import com.android.internal.view.menu.SubMenuBuilder;
+import com.android.internal.widget.ActionBarContainer;
 import com.android.internal.widget.ActionBarContextView;
 import com.android.internal.widget.ActionBarView;
 
@@ -625,7 +623,7 @@
         }
     }
 
-    private void checkCloseActionMenu(Menu menu) {
+    void checkCloseActionMenu(Menu menu) {
         if (mClosingActionMenu) {
             return;
         }
@@ -882,7 +880,7 @@
                     final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
                     if (cb.onPreparePanel(FEATURE_OPTIONS_PANEL, st.createdPanelView, st.menu)) {
                         cb.onMenuOpened(FEATURE_ACTION_BAR, st.menu);
-                        mActionBar.openOverflowMenu();
+                        mActionBar.showOverflowMenu();
                     }
                 }
             } else {
@@ -2591,11 +2589,16 @@
                     final boolean splitActionBar = getWindowStyle().getBoolean(
                             com.android.internal.R.styleable.Window_windowSplitActionBar, false);
                     if (splitActionBar) {
-                        final ViewGroup splitView = (ViewGroup) findViewById(
-                                com.android.internal.R.id.lower_action_context_bar);
+                        final ActionBarContainer splitView = (ActionBarContainer) findViewById(
+                                com.android.internal.R.id.split_action_bar);
                         if (splitView != null) {
+                            splitView.setVisibility(View.VISIBLE);
                             mActionBar.setSplitActionBar(splitActionBar);
                             mActionBar.setSplitView(splitView);
+
+                            final ActionBarContextView cab = (ActionBarContextView) findViewById(
+                                    com.android.internal.R.id.action_context_bar);
+                            cab.setSplitView(splitView);
                         } else {
                             Log.e(TAG, "Window style requested split action bar with " +
                                     "incompatible window decor! Ignoring request.");
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 6c3b3d3..c225e89 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -34,6 +34,7 @@
 import android.net.NetworkUtils;
 import android.net.Proxy;
 import android.net.ProxyProperties;
+import android.net.RouteInfo;
 import android.net.vpn.VpnManager;
 import android.net.wifi.WifiStateTracker;
 import android.os.Binder;
@@ -1417,14 +1418,19 @@
         if (p == null) return;
         String interfaceName = p.getInterfaceName();
         if (TextUtils.isEmpty(interfaceName)) return;
-        for (InetAddress gateway : p.getGateways()) {
+        for (RouteInfo route : p.getRoutes()) {
 
-            if (NetworkUtils.addHostRoute(interfaceName, gateway, null) &&
-                    NetworkUtils.addDefaultRoute(interfaceName, gateway)) {
-                if (DBG) {
-                    NetworkInfo networkInfo = nt.getNetworkInfo();
-                    log("addDefaultRoute for " + networkInfo.getTypeName() +
-                            " (" + interfaceName + "), GatewayAddr=" + gateway.getHostAddress());
+            //TODO - handle non-default routes
+            if (route.isDefaultRoute()) {
+                InetAddress gateway = route.getGateway();
+                if (NetworkUtils.addHostRoute(interfaceName, gateway, null) &&
+                        NetworkUtils.addDefaultRoute(interfaceName, gateway)) {
+                    if (DBG) {
+                        NetworkInfo networkInfo = nt.getNetworkInfo();
+                        log("addDefaultRoute for " + networkInfo.getTypeName() +
+                                " (" + interfaceName + "), GatewayAddr=" +
+                                gateway.getHostAddress());
+                    }
                 }
             }
         }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 31b7f86..da34644 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -7091,18 +7091,25 @@
      * to append various headers to the dropbox log text.
      */
     private void appendDropBoxProcessHeaders(ProcessRecord process, StringBuilder sb) {
+        // Watchdog thread ends up invoking this function (with
+        // a null ProcessRecord) to add the stack file to dropbox.
+        // Do not acquire a lock on this (am) in such cases, as it
+        // could cause a potential deadlock, if and when watchdog
+        // is invoked due to unavailability of lock on am and it
+        // would prevent watchdog from killing system_server.
+        if (process == null) {
+            sb.append("Process: system_server\n");
+            return;
+        }
         // Note: ProcessRecord 'process' is guarded by the service
         // instance.  (notably process.pkgList, which could otherwise change
         // concurrently during execution of this method)
         synchronized (this) {
-            if (process == null || process.pid == MY_PID) {
+            if (process.pid == MY_PID) {
                 sb.append("Process: system_server\n");
             } else {
                 sb.append("Process: ").append(process.processName).append("\n");
             }
-            if (process == null) {
-                return;
-            }
             int flags = process.info.flags;
             IPackageManager pm = AppGlobals.getPackageManager();
             sb.append("Flags: 0x").append(Integer.toString(flags, 16)).append("\n");
diff --git a/services/surfaceflinger/TextureManager.cpp b/services/surfaceflinger/TextureManager.cpp
index c9a15f5..9e24f90 100644
--- a/services/surfaceflinger/TextureManager.cpp
+++ b/services/surfaceflinger/TextureManager.cpp
@@ -186,7 +186,7 @@
     if (texture->name == -1UL) {
         status_t err = initTexture(texture);
         LOGE_IF(err, "loadTexture failed in initTexture (%s)", strerror(err));
-        return err;
+        if (err != NO_ERROR) return err;
     }
 
     if (texture->target != Texture::TEXTURE_2D)
diff --git a/telephony/java/android/telephony/JapanesePhoneNumberFormatter.java b/telephony/java/android/telephony/JapanesePhoneNumberFormatter.java
index 6390d8e..f5e53ef 100644
--- a/telephony/java/android/telephony/JapanesePhoneNumberFormatter.java
+++ b/telephony/java/android/telephony/JapanesePhoneNumberFormatter.java
@@ -24,6 +24,7 @@
  *
  * 022-229-1234 0223-23-1234 022-301-9876 015-482-7849 0154-91-3478
  * 01547-5-4534 090-1234-1234 080-0123-6789
+ * 050-0000-0000 060-0000-0000
  * 0800-000-9999 0570-000-000 0276-00-0000
  *
  * As you can see, there is no straight-forward rule here.
@@ -31,7 +32,7 @@
  */
 /* package */ class JapanesePhoneNumberFormatter {
     private static short FORMAT_MAP[] = {
-    -100, 10, 220, -15, 410, 530, -15, 670, 780, 1060,
+    -100, 10, 220, -15, 410, 530, 1200, 670, 780, 1060,
     -100, -25, 20, 40, 70, 100, 150, 190, 200, 210,
     -36, -100, -100, -35, -35, -35, 30, -100, -100, -100,
     -35, -35, -35, -35, -35, -35, -35, -45, -35, -35,
@@ -84,7 +85,7 @@
     -35, -25, -25, -25, -25, -25, -25, -25, -25, -25,
     -25, -25, -25, -35, -35, -35, -25, -25, -25, 520,
     -100, -100, -45, -100, -45, -100, -45, -100, -45, -100,
-    -25, -100, -25, 540, 580, 590, 600, 610, 630, 640,
+    -26, -100, -25, 540, 580, 590, 600, 610, 630, 640,
     -25, -35, -35, -35, -25, -25, -35, -35, -35, 550,
     -35, -35, -25, -25, -25, -25, 560, 570, -25, -35,
     -35, -35, -35, -35, -25, -25, -25, -25, -25, -25,
@@ -150,7 +151,8 @@
     -35, 1170, -25, -35, 1180, -35, 1190, -35, -25, -25,
     -100, -100, -45, -45, -100, -100, -100, -100, -100, -100,
     -25, -35, -35, -35, -35, -35, -35, -25, -25, -35,
-    -35, -35, -35, -35, -35, -35, -35, -35, -35, -45};
+    -35, -35, -35, -35, -35, -35, -35, -35, -35, -45,
+    -26, -15, -15, -15, -15, -15, -15, -15, -15, -15};
 
     public static void format(Editable text) {
         // Here, "root" means the position of "'":
diff --git a/telephony/java/com/android/internal/telephony/ApnContext.java b/telephony/java/com/android/internal/telephony/ApnContext.java
index 3f1ca9e..010d61d 100644
--- a/telephony/java/com/android/internal/telephony/ApnContext.java
+++ b/telephony/java/com/android/internal/telephony/ApnContext.java
@@ -51,6 +51,8 @@
 
     DataConnection mDataConnection;
 
+    DataConnectionAc mDataConnectionAc;
+
     String mReason;
 
     PendingIntent mReconnectIntent;
@@ -96,6 +98,17 @@
         mDataConnection = dataConnection;
     }
 
+
+    public synchronized DataConnectionAc getDataConnectionAc() {
+        log("getDataConnectionAc dcac=" + mDataConnectionAc);
+        return mDataConnectionAc;
+    }
+
+    public synchronized void setDataConnectionAc(DataConnectionAc dcac) {
+        log("setDataConnectionAc dcac=" + dcac);
+        mDataConnectionAc = dcac;
+    }
+
     public synchronized ApnSetting getApnSetting() {
         return mApnSetting;
     }
@@ -206,6 +219,11 @@
        return mDependencyMet.get();
     }
 
+    @Override
+    public String toString() {
+        return "state=" + getState() + " apnType=" + mApnType;
+    }
+
     protected void log(String s) {
         Log.d(LOG_TAG, "[ApnContext] " + s);
     }
diff --git a/telephony/java/com/android/internal/telephony/DataCallState.java b/telephony/java/com/android/internal/telephony/DataCallState.java
index a883e8e..1d67d45 100644
--- a/telephony/java/com/android/internal/telephony/DataCallState.java
+++ b/telephony/java/com/android/internal/telephony/DataCallState.java
@@ -20,6 +20,7 @@
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkUtils;
+import android.net.RouteInfo;
 import android.os.SystemProperties;
 import android.util.Log;
 
@@ -196,7 +197,7 @@
                     } catch (IllegalArgumentException e) {
                         throw new UnknownHostException("Non-numeric gateway addr=" + addr);
                     }
-                    linkProperties.addGateway(ia);
+                    linkProperties.addRoute(new RouteInfo(ia));
                 }
 
                 result = SetupResult.SUCCESS;
@@ -223,5 +224,3 @@
         return result;
     }
 }
-
-
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index 791fbfd..6a5b82c 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -17,22 +17,24 @@
 package com.android.internal.telephony;
 
 
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 
-import android.net.LinkAddress;
 import android.net.LinkCapabilities;
 import android.net.LinkProperties;
-import android.net.NetworkUtils;
+import android.net.ProxyProperties;
 import android.os.AsyncResult;
+import android.os.Bundle;
 import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.SystemProperties;
 import android.text.TextUtils;
 
-import java.net.InetAddress;
-import java.net.Inet4Address;
-import java.net.UnknownHostException;
 import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * {@hide}
@@ -60,6 +62,8 @@
 
     protected static Object mCountLock = new Object();
     protected static int mCount;
+    protected AsyncChannel mAc;
+
 
     /**
      * Used internally for saving connecting parameters.
@@ -76,13 +80,6 @@
     }
 
     /**
-     * An instance used for notification of blockingReset.
-     * TODO: Remove when blockingReset is removed.
-     */
-    class ResetSynchronouslyLock {
-    }
-
-    /**
      * Used internally for saving disconnecting parameters.
      */
     protected static class DisconnectParams {
@@ -90,15 +87,9 @@
             this.reason = reason;
             this.onCompletedMsg = onCompletedMsg;
         }
-        public DisconnectParams(ResetSynchronouslyLock lockObj) {
-            this.reason = null;
-            this.lockObj = lockObj;
-        }
-
         public int tag;
         public String reason;
         public Message onCompletedMsg;
-        public ResetSynchronouslyLock lockObj;
     }
 
     /**
@@ -188,13 +179,13 @@
     }
 
     // ***** Event codes for driving the state machine
-    protected static final int EVENT_RESET = 1;
-    protected static final int EVENT_CONNECT = 2;
-    protected static final int EVENT_SETUP_DATA_CONNECTION_DONE = 3;
-    protected static final int EVENT_GET_LAST_FAIL_DONE = 4;
-    protected static final int EVENT_DEACTIVATE_DONE = 5;
-    protected static final int EVENT_DISCONNECT = 6;
-    protected static final int EVENT_RIL_CONNECTED = 7;
+    protected static final int BASE = Protocol.BASE_DATA_CONNECTION;
+    protected static final int EVENT_CONNECT = BASE + 0;
+    protected static final int EVENT_SETUP_DATA_CONNECTION_DONE = BASE + 1;
+    protected static final int EVENT_GET_LAST_FAIL_DONE = BASE + 2;
+    protected static final int EVENT_DEACTIVATE_DONE = BASE + 3;
+    protected static final int EVENT_DISCONNECT = BASE + 4;
+    protected static final int EVENT_RIL_CONNECTED = BASE + 5;
 
     //***** Tag IDs for EventLog
     protected static final int EVENT_LOG_BAD_DNS_ADDRESS = 50100;
@@ -313,13 +304,8 @@
             AsyncResult.forMessage(msg);
             msg.sendToTarget();
         }
-        if (dp.lockObj != null) {
-            synchronized(dp.lockObj) {
-                dp.lockObj.notify();
-            }
-        }
-
         clearSettings();
+        if (DBG) log("NotifyDisconnectCompleted DisconnectParams=" + dp);
     }
 
     protected int getRadioTechnology(int defaultRadioTechnology) {
@@ -408,6 +394,49 @@
         return mRetryMgr.isRetryForever();
     }
 
+    private AtomicInteger mRefCount = new AtomicInteger(0);
+
+    /**
+     * Set refCount.
+     *
+     * @param val is new refCount
+     */
+    public void setRefCount(int val) {
+        mRefCount.set(val);
+    }
+
+    /**
+     * Get refCount
+     *
+     * @return refCount
+     */
+    public int getRefCount() {
+        return mRefCount.get();
+    }
+
+    /**
+     * @return decrement and return refCount
+     *
+     * TODO: Consider using the refCount for defining the
+     * life time of a connection. When this goes zero the
+     * DataConnection could tear itself down.
+     */
+    public int decAndGetRefCount() {
+        int v = mRefCount.decrementAndGet();
+        if (v < 0) {
+            log("BUG: decAndGetRefCount caused refCount to be < 0");
+            mRefCount.set(0);
+        }
+        return v;
+     }
+
+    /**
+     * @return increment and return refCount
+     */
+    public int incAndGetRefCount() {
+        return mRefCount.incrementAndGet();
+    }
+
     /*
      * **************************************************************************
      * End members owned by DataConnectionTracker
@@ -498,12 +527,74 @@
             AsyncResult ar;
 
             switch (msg.what) {
-                case EVENT_RESET:
-                    if (DBG) log("DcDefaultState: msg.what=EVENT_RESET");
-                    clearSettings();
-                    if (msg.obj != null) {
-                        notifyDisconnectCompleted((DisconnectParams) msg.obj);
+                case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
+                    if (mAc != null) {
+                        log("Disconnecting to previous connection mAc=" + mAc);
+                        mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
+                                AsyncChannel.STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED);
+                    } else {
+                        mAc = new AsyncChannel();
+                        mAc.connected(null, getHandler(), msg.replyTo);
+                        log("DcDefaultState: FULL_CONNECTION reply connected");
+                        mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
+                                AsyncChannel.STATUS_SUCCESSFUL, mId, "hi");
                     }
+                    break;
+                }
+                case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
+                    log("CMD_CHANNEL_DISCONNECT");
+                    mAc.disconnect();
+                    break;
+                }
+                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
+                    log("CMD_CHANNEL_DISCONNECTED");
+                    mAc = null;
+                    break;
+                }
+                case DataConnectionAc.REQ_IS_INACTIVE: {
+                    boolean val = getCurrentState() == mInactiveState;
+                    log("REQ_IS_INACTIVE  isInactive=" + val);
+                    mAc.replyToMessage(msg, DataConnectionAc.RSP_IS_INACTIVE, val ? 1 : 0);
+                    break;
+                }
+                case DataConnectionAc.REQ_GET_CID: {
+                    log("REQ_GET_CID  cid=" + cid);
+                    mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_CID, cid);
+                    break;
+                }
+                case DataConnectionAc.REQ_GET_APNSETTING: {
+                    log("REQ_GET_APNSETTING  apnSetting=" + mApn);
+                    mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_APNSETTING, mApn);
+                    break;
+                }
+                case DataConnectionAc.REQ_GET_LINK_PROPERTIES: {
+                    LinkProperties lp = new LinkProperties(mLinkProperties);
+                    log("REQ_GET_LINK_PROPERTIES linkProperties" + lp);
+                    mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_LINK_PROPERTIES, lp);
+                    break;
+                }
+                case DataConnectionAc.REQ_SET_LINK_PROPERTIES_HTTP_PROXY: {
+                    ProxyProperties proxy = (ProxyProperties) msg.obj;
+                    log("REQ_SET_LINK_PROPERTIES_HTTP_PROXY proxy=" + proxy);
+                    mLinkProperties.setHttpProxy(proxy);
+                    mAc.replyToMessage(msg, DataConnectionAc.RSP_SET_LINK_PROPERTIES_HTTP_PROXY);
+                    break;
+                }
+                case DataConnectionAc.REQ_GET_LINK_CAPABILITIES: {
+                    LinkCapabilities lc = new LinkCapabilities(mCapabilities);
+                    log("REQ_GET_LINK_CAPABILITIES linkCapabilities" + lc);
+                    mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_LINK_CAPABILITIES, lc);
+                    break;
+                }
+                case DataConnectionAc.REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE: {
+                    Bundle data = msg.getData();
+                    mLinkProperties = (LinkProperties) data.get("linkProperties");
+                    break;
+                }
+                case DataConnectionAc.REQ_RESET:
+                    if (DBG) log("DcDefaultState: msg.what=REQ_RESET");
+                    clearSettings();
+                    mAc.replyToMessage(msg, DataConnectionAc.RSP_RESET);
                     transitionTo(mInactiveState);
                     break;
 
@@ -539,7 +630,7 @@
                     break;
             }
 
-            return true;
+            return HANDLED;
         }
     }
     private DcDefaultState mDefaultState = new DcDefaultState();
@@ -597,14 +688,12 @@
             boolean retVal;
 
             switch (msg.what) {
-                case EVENT_RESET:
+                case DataConnectionAc.REQ_RESET:
                     if (DBG) {
-                        log("DcInactiveState: msg.what=EVENT_RESET, ignore we're already reset");
+                        log("DcInactiveState: msg.what=RSP_RESET, ignore we're already reset");
                     }
-                    if (msg.obj != null) {
-                        notifyDisconnectCompleted((DisconnectParams) msg.obj);
-                    }
-                    retVal = true;
+                    mAc.replyToMessage(msg, DataConnectionAc.RSP_RESET);
+                    retVal = HANDLED;
                     break;
 
                 case EVENT_CONNECT:
@@ -613,12 +702,12 @@
                     cp.tag = mTag;
                     onConnect(cp);
                     transitionTo(mActivatingState);
-                    retVal = true;
+                    retVal = HANDLED;
                     break;
 
                 default:
                     if (DBG) log("DcInactiveState nothandled msg.what=" + msg.what);
-                    retVal = false;
+                    retVal = NOT_HANDLED;
                     break;
             }
             return retVal;
@@ -640,7 +729,7 @@
                 case EVENT_DISCONNECT:
                     if (DBG) log("DcActivatingState deferring msg.what=EVENT_DISCONNECT");
                     deferMessage(msg);
-                    retVal = true;
+                    retVal = HANDLED;
                     break;
 
                 case EVENT_SETUP_DATA_CONNECTION_DONE:
@@ -685,7 +774,7 @@
                         default:
                             throw new RuntimeException("Unknown SetupResult, should not happen");
                     }
-                    retVal = true;
+                    retVal = HANDLED;
                     break;
 
                 case EVENT_GET_LAST_FAIL_DONE:
@@ -710,12 +799,12 @@
                         }
                     }
 
-                    retVal = true;
+                    retVal = HANDLED;
                     break;
 
                 default:
                     if (DBG) log("DcActivatingState not handled msg.what=" + msg.what);
-                    retVal = false;
+                    retVal = NOT_HANDLED;
                     break;
             }
             return retVal;
@@ -768,12 +857,12 @@
                     dp.tag = mTag;
                     tearDownData(dp);
                     transitionTo(mDisconnectingState);
-                    retVal = true;
+                    retVal = HANDLED;
                     break;
 
                 default:
                     if (DBG) log("DcActiveState nothandled msg.what=" + msg.what);
-                    retVal = false;
+                    retVal = NOT_HANDLED;
                     break;
             }
             return retVal;
@@ -803,12 +892,12 @@
                         if (DBG) log("DcDisconnectState EVENT_DEACTIVATE_DONE stale dp.tag="
                                 + dp.tag + " mTag=" + mTag);
                     }
-                    retVal = true;
+                    retVal = HANDLED;
                     break;
 
                 default:
                     if (DBG) log("DcDisconnectingState not handled msg.what=" + msg.what);
-                    retVal = false;
+                    retVal = NOT_HANDLED;
                     break;
             }
             return retVal;
@@ -845,7 +934,7 @@
                                     " stale dp.tag=" + cp.tag + ", mTag=" + mTag);
                         }
                     }
-                    retVal = true;
+                    retVal = HANDLED;
                     break;
 
                 default:
@@ -853,7 +942,7 @@
                         log("DcDisconnectionErrorCreatingConnection not handled msg.what="
                                 + msg.what);
                     }
-                    retVal = false;
+                    retVal = NOT_HANDLED;
                     break;
             }
             return retVal;
@@ -865,147 +954,26 @@
     // ******* public interface
 
     /**
-     * Disconnect from the network.
-     *
-     * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
-     *        With AsyncResult.userObj set to the original msg.obj.
-     */
-    public void reset(Message onCompletedMsg) {
-        sendMessage(obtainMessage(EVENT_RESET, new DisconnectParams(null, onCompletedMsg)));
-    }
-
-    /**
-     * Reset the connection and wait for it to complete.
-     * TODO: Remove when all callers only need the asynchronous
-     * reset defined above.
-     */
-    public void resetSynchronously() {
-        ResetSynchronouslyLock lockObj = new ResetSynchronouslyLock();
-        synchronized(lockObj) {
-            sendMessage(obtainMessage(EVENT_RESET, new DisconnectParams(lockObj)));
-            try {
-                lockObj.wait();
-            } catch (InterruptedException e) {
-                log("blockingReset: unexpected interrupted of wait()");
-            }
-        }
-    }
-
-    /**
-     * Connect to the apn and return an AsyncResult in onCompletedMsg.
+     * Bring up a connection to the apn and return an AsyncResult in onCompletedMsg.
      * Used for cellular networks that use Acesss Point Names (APN) such
      * as GSM networks.
      *
      * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
      *        With AsyncResult.userObj set to the original msg.obj,
      *        AsyncResult.result = FailCause and AsyncResult.exception = Exception().
-     * @param apn is the Access Point Name to connect to
+     * @param apn is the Access Point Name to bring up a connection to
      */
-    public void connect(Message onCompletedMsg, ApnSetting apn) {
+    public void bringUp(Message onCompletedMsg, ApnSetting apn) {
         sendMessage(obtainMessage(EVENT_CONNECT, new ConnectionParams(apn, onCompletedMsg)));
     }
 
     /**
-     * Connect to the apn and return an AsyncResult in onCompletedMsg.
-     *
-     * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
-     *        With AsyncResult.userObj set to the original msg.obj,
-     *        AsyncResult.result = FailCause and AsyncResult.exception = Exception().
-     */
-    public void connect(Message onCompletedMsg) {
-        sendMessage(obtainMessage(EVENT_CONNECT, new ConnectionParams(null, onCompletedMsg)));
-    }
-
-    /**
-     * Disconnect from the network.
+     * Tear down the connection through the apn on the network.
      *
      * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
      *        With AsyncResult.userObj set to the original msg.obj.
      */
-    public void disconnect(String reason, Message onCompletedMsg) {
+    public void tearDown(String reason, Message onCompletedMsg) {
         sendMessage(obtainMessage(EVENT_DISCONNECT, new DisconnectParams(reason, onCompletedMsg)));
     }
-
-    // ****** The following are used for debugging.
-
-    /**
-     * TODO: This should be an asynchronous call and we wouldn't
-     * have to use handle the notification in the DcInactiveState.enter.
-     *
-     * @return true if the state machine is in the inactive state.
-     */
-    public boolean isInactive() {
-        boolean retVal = getCurrentState() == mInactiveState;
-        return retVal;
-    }
-
-    /**
-     * TODO: This should be an asynchronous call and we wouldn't
-     * have to use handle the notification in the DcActiveState.enter.
-     *
-     * @return true if the state machine is in the active state.
-     */
-    public boolean isActive() {
-        boolean retVal = getCurrentState() == mActiveState;
-        return retVal;
-    }
-
-    /**
-     * Return the LinkProperties for the connection.
-     *
-     * @return a copy of the LinkProperties, is never null.
-     */
-    public LinkProperties getLinkProperties() {
-        return new LinkProperties(mLinkProperties);
-    }
-
-    /**
-     * A capability is an Integer/String pair, the capabilities
-     * are defined in the class LinkSocket#Key.
-     *
-     * @return a copy of this connections capabilities, may be empty but never null.
-     */
-    public LinkCapabilities getLinkCapabilities() {
-        return new LinkCapabilities(mCapabilities);
-    }
-
-    /**
-     * @return the current state as a string.
-     */
-    public String getStateAsString() {
-        String retVal = getCurrentState().getName();
-        return retVal;
-    }
-
-    /**
-     * @return the time of when this connection was created.
-     */
-    public long getConnectionTime() {
-        return createTime;
-    }
-
-    /**
-     * @return the time of the last failure.
-     */
-    public long getLastFailTime() {
-        return lastFailTime;
-    }
-
-    /**
-     * @return the last cause of failure.
-     */
-    public FailCause getLastFailCause() {
-        return lastFailCause;
-    }
-
-    /**
-     * @return the current ApnSetting
-     */
-    public ApnSetting getApn() {
-        return mApn;
-    }
-
-    public int getCid() {
-        return cid;
-    }
 }
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionAc.java b/telephony/java/com/android/internal/telephony/DataConnectionAc.java
new file mode 100644
index 0000000..a9796dd
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/DataConnectionAc.java
@@ -0,0 +1,288 @@
+/*
+ * 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.internal.telephony;
+
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
+
+import android.net.LinkCapabilities;
+import android.net.LinkProperties;
+import android.net.ProxyProperties;
+import android.os.Message;
+
+/**
+ * AsyncChannel to a DataConnection
+ */
+public class DataConnectionAc extends AsyncChannel {
+    private static final boolean DBG = true;
+    private String mLogTag;
+
+    public DataConnection dataConnection;
+
+    public static final int BASE = Protocol.BASE_DATA_CONNECTION_AC;
+
+    public static final int REQ_IS_INACTIVE = BASE + 0;
+    public static final int RSP_IS_INACTIVE = BASE + 1;
+
+    public static final int REQ_GET_CID = BASE + 2;
+    public static final int RSP_GET_CID = BASE + 3;
+
+    public static final int REQ_GET_APNSETTING = BASE + 4;
+    public static final int RSP_GET_APNSETTING = BASE + 5;
+
+    public static final int REQ_GET_LINK_PROPERTIES = BASE + 6;
+    public static final int RSP_GET_LINK_PROPERTIES = BASE + 7;
+
+    public static final int REQ_SET_LINK_PROPERTIES_HTTP_PROXY = BASE + 8;
+    public static final int RSP_SET_LINK_PROPERTIES_HTTP_PROXY = BASE + 9;
+
+    public static final int REQ_GET_LINK_CAPABILITIES = BASE + 10;
+    public static final int RSP_GET_LINK_CAPABILITIES = BASE + 11;
+
+    public static final int REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE = BASE + 12;
+    public static final int RSP_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE = BASE + 13;
+
+    public static final int REQ_RESET = BASE + 14;
+    public static final int RSP_RESET = BASE + 15;
+
+    public DataConnectionAc(DataConnection dc, String logTag) {
+        dataConnection = dc;
+        mLogTag = logTag;
+    }
+
+    /**
+     * Request if the state machine is in the inactive state.
+     * Response {@link #rspIsInactive}
+     */
+    public void reqIsInactive() {
+        sendMessage(REQ_IS_INACTIVE);
+        if (DBG) log("reqIsInactive");
+    }
+
+    /**
+     * Evaluate RSP_IS_INACTIVE.
+     *
+     * @return true if the state machine is in the inactive state.
+     */
+    public boolean rspIsInactive(Message response) {
+        boolean retVal = response.arg1 == 1;
+        if (DBG) log("rspIsInactive=" + retVal);
+        return retVal;
+    }
+
+    /**
+     * @return true if the state machine is in the inactive state.
+     */
+    public boolean isInactiveSync() {
+        Message response = sendMessageSynchronously(REQ_IS_INACTIVE);
+        if ((response != null) && (response.what == RSP_IS_INACTIVE)) {
+            return rspIsInactive(response);
+        } else {
+            log("rspIsInactive error response=" + response);
+            return false;
+        }
+    }
+
+    /**
+     * Request the Connection ID.
+     * Response {@link #rspCid}
+     */
+    public void reqCid() {
+        sendMessage(REQ_GET_CID);
+        if (DBG) log("reqCid");
+    }
+
+    /**
+     * Evaluate a RSP_GET_CID message and return the cid.
+     *
+     * @param response Message
+     * @return connection id or -1 if an error
+     */
+    public int rspCid(Message response) {
+        int retVal = response.arg1;
+        if (DBG) log("rspCid=" + retVal);
+        return retVal;
+    }
+
+    /**
+     * @return connection id or -1 if an error
+     */
+    public int getCidSync() {
+        Message response = sendMessageSynchronously(REQ_GET_CID);
+        if ((response != null) && (response.what == RSP_GET_CID)) {
+            return rspCid(response);
+        } else {
+            log("rspCid error response=" + response);
+            return -1;
+        }
+    }
+
+    /**
+     * Request the connections ApnSetting.
+     * Response {@link #rspApnSetting}
+     */
+    public void reqApnSetting() {
+        sendMessage(REQ_GET_APNSETTING);
+        if (DBG) log("reqApnSetting");
+    }
+
+    /**
+     * Evaluate a RSP_APN_SETTING message and return the ApnSetting.
+     *
+     * @param response Message
+     * @return ApnSetting, maybe null
+     */
+    public ApnSetting rspApnSetting(Message response) {
+        ApnSetting retVal = (ApnSetting) response.obj;
+        if (DBG) log("rspApnSetting=" + retVal);
+        return retVal;
+    }
+
+    /**
+     * Get the connections ApnSetting.
+     *
+     * @return ApnSetting or null if an error
+     */
+    public ApnSetting getApnSettingSync() {
+        Message response = sendMessageSynchronously(REQ_GET_APNSETTING);
+        if ((response != null) && (response.what == RSP_GET_APNSETTING)) {
+            return rspApnSetting(response);
+        } else {
+            log("getApnSetting error response=" + response);
+            return null;
+        }
+    }
+
+    /**
+     * Request the connections LinkProperties.
+     * Response {@link #rspLinkProperties}
+     */
+    public void reqLinkProperties() {
+        sendMessage(REQ_GET_LINK_PROPERTIES);
+        if (DBG) log("reqLinkProperties");
+    }
+
+    /**
+     * Evaluate RSP_GET_LINK_PROPERTIES
+     *
+     * @param response
+     * @return LinkProperties, maybe null.
+     */
+    public LinkProperties rspLinkProperties(Message response) {
+        LinkProperties retVal = (LinkProperties) response.obj;
+        if (DBG) log("rspLinkProperties=" + retVal);
+        return retVal;
+    }
+
+    /**
+     * Get the connections LinkProperties.
+     *
+     * @return LinkProperties or null if an error
+     */
+    public LinkProperties getLinkPropertiesSync() {
+        Message response = sendMessageSynchronously(REQ_GET_LINK_PROPERTIES);
+        if ((response != null) && (response.what == RSP_GET_LINK_PROPERTIES)) {
+            return rspLinkProperties(response);
+        } else {
+            log("getLinkProperties error response=" + response);
+            return null;
+        }
+    }
+
+    /**
+     * Request setting the connections LinkProperties.HttpProxy.
+     * Response RSP_SET_LINK_PROPERTIES when complete.
+     */
+    public void reqSetLinkPropertiesHttpProxy(ProxyProperties proxy) {
+        sendMessage(REQ_SET_LINK_PROPERTIES_HTTP_PROXY, proxy);
+        if (DBG) log("reqSetLinkPropertiesHttpProxy proxy=" + proxy);
+    }
+
+    /**
+     * Set the connections LinkProperties.HttpProxy
+     */
+    public void setLinkPropertiesHttpProxySync(ProxyProperties proxy) {
+        Message response =
+            sendMessageSynchronously(REQ_SET_LINK_PROPERTIES_HTTP_PROXY, proxy);
+        if ((response != null) && (response.what == RSP_SET_LINK_PROPERTIES_HTTP_PROXY)) {
+            if (DBG) log("setLinkPropertiesHttpPoxy ok");
+        } else {
+            log("setLinkPropertiesHttpPoxy error response=" + response);
+        }
+    }
+
+    /**
+     * Request the connections LinkCapabilities.
+     * Response {@link #rspLinkCapabilities}
+     */
+    public void reqLinkCapabilities() {
+        sendMessage(REQ_GET_LINK_CAPABILITIES);
+        if (DBG) log("reqLinkCapabilities");
+    }
+
+    /**
+     * Evaluate RSP_GET_LINK_CAPABILITIES
+     *
+     * @param response
+     * @return LinkCapabilites, maybe null.
+     */
+    public LinkCapabilities rspLinkCapabilities(Message response) {
+        LinkCapabilities retVal = (LinkCapabilities) response.obj;
+        if (DBG) log("rspLinkCapabilities=" + retVal);
+        return retVal;
+    }
+
+    /**
+     * Get the connections LinkCapabilities.
+     *
+     * @return LinkCapabilities or null if an error
+     */
+    public LinkCapabilities getLinkCapabilitiesSync() {
+        Message response = sendMessageSynchronously(REQ_GET_LINK_CAPABILITIES);
+        if ((response != null) && (response.what == RSP_GET_LINK_CAPABILITIES)) {
+            return rspLinkCapabilities(response);
+        } else {
+            log("getLinkCapabilities error response=" + response);
+            return null;
+        }
+    }
+
+    /**
+     * Request the connections LinkCapabilities.
+     * Response RSP_RESET when complete
+     */
+    public void reqReset() {
+        sendMessage(REQ_RESET);
+        if (DBG) log("reqReset");
+    }
+
+    /**
+     * Reset the connection and wait for it to complete.
+     */
+    public void resetSync() {
+        Message response = sendMessageSynchronously(REQ_RESET);
+        if ((response != null) && (response.what == RSP_RESET)) {
+            if (DBG) log("restSync ok");
+        } else {
+            if (DBG) log("restSync error response=" + response);
+        }
+    }
+
+    private void log(String s) {
+        android.util.Log.d(mLogTag, "DataConnectionAc " + s);
+    }
+}
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index 01ac95f..ad4e796 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -22,7 +22,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
-import android.net.IConnectivityManager;
 import android.net.LinkCapabilities;
 import android.net.LinkProperties;
 import android.net.NetworkInfo;
@@ -32,7 +31,6 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.Messenger;
-import android.os.ServiceManager;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
@@ -40,6 +38,8 @@
 import android.util.Log;
 
 import com.android.internal.R;
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -91,38 +91,39 @@
     public static String EXTRA_MESSENGER = "EXTRA_MESSENGER";
 
     /***** Event Codes *****/
-    protected static final int EVENT_DATA_SETUP_COMPLETE = 1;
-    protected static final int EVENT_RADIO_AVAILABLE = 3;
-    protected static final int EVENT_RECORDS_LOADED = 4;
-    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_RADIO_OFF_OR_NOT_AVAILABLE = 12;
-    protected static final int EVENT_VOICE_CALL_STARTED = 14;
-    protected static final int EVENT_VOICE_CALL_ENDED = 15;
-    protected static final int EVENT_DATA_CONNECTION_DETACHED = 19;
-    protected static final int EVENT_LINK_STATE_CHANGED = 20;
-    protected static final int EVENT_ROAMING_ON = 21;
-    protected static final int EVENT_ROAMING_OFF = 22;
-    protected static final int EVENT_ENABLE_NEW_APN = 23;
-    protected static final int EVENT_RESTORE_DEFAULT_APN = 24;
-    protected static final int EVENT_DISCONNECT_DONE = 25;
-    protected static final int EVENT_DATA_CONNECTION_ATTACHED = 26;
-    protected static final int EVENT_START_NETSTAT_POLL = 27;
-    protected static final int EVENT_START_RECOVERY = 28;
-    protected static final int EVENT_APN_CHANGED = 29;
-    protected static final int EVENT_CDMA_DATA_DETACHED = 30;
-    protected static final int EVENT_NV_READY = 31;
-    protected static final int EVENT_PS_RESTRICT_ENABLED = 32;
-    protected static final int EVENT_PS_RESTRICT_DISABLED = 33;
-    public static final int EVENT_CLEAN_UP_CONNECTION = 34;
-    protected static final int EVENT_CDMA_OTA_PROVISION = 35;
-    protected static final int EVENT_RESTART_RADIO = 36;
-    protected static final int EVENT_SET_INTERNAL_DATA_ENABLE = 37;
-    protected static final int EVENT_RESET_DONE = 38;
-    public static final int CMD_SET_DATA_ENABLE = 39;
-    public static final int EVENT_CLEAN_UP_ALL_CONNECTIONS = 40;
-    public static final int CMD_SET_DEPENDENCY_MET = 41;
+    protected static final int BASE = Protocol.BASE_DATA_CONNECTION_TRACKER;
+    protected static final int EVENT_DATA_SETUP_COMPLETE = BASE + 0;
+    protected static final int EVENT_RADIO_AVAILABLE = BASE + 1;
+    protected static final int EVENT_RECORDS_LOADED = BASE + 2;
+    protected static final int EVENT_TRY_SETUP_DATA = BASE + 3;
+    protected static final int EVENT_DATA_STATE_CHANGED = BASE + 4;
+    protected static final int EVENT_POLL_PDP = BASE + 5;
+    protected static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = BASE + 6;
+    protected static final int EVENT_VOICE_CALL_STARTED = BASE + 7;
+    protected static final int EVENT_VOICE_CALL_ENDED = BASE + 8;
+    protected static final int EVENT_DATA_CONNECTION_DETACHED = BASE + 9;
+    protected static final int EVENT_LINK_STATE_CHANGED = BASE + 10;
+    protected static final int EVENT_ROAMING_ON = BASE + 11;
+    protected static final int EVENT_ROAMING_OFF = BASE + 12;
+    protected static final int EVENT_ENABLE_NEW_APN = BASE + 13;
+    protected static final int EVENT_RESTORE_DEFAULT_APN = BASE + 14;
+    protected static final int EVENT_DISCONNECT_DONE = BASE + 15;
+    protected static final int EVENT_DATA_CONNECTION_ATTACHED = BASE + 16;
+    protected static final int EVENT_START_NETSTAT_POLL = BASE + 17;
+    protected static final int EVENT_START_RECOVERY = BASE + 18;
+    protected static final int EVENT_APN_CHANGED = BASE + 19;
+    protected static final int EVENT_CDMA_DATA_DETACHED = BASE + 20;
+    protected static final int EVENT_NV_READY = BASE + 21;
+    protected static final int EVENT_PS_RESTRICT_ENABLED = BASE + 22;
+    protected static final int EVENT_PS_RESTRICT_DISABLED = BASE + 23;
+    public static final int EVENT_CLEAN_UP_CONNECTION = BASE + 24;
+    protected static final int EVENT_CDMA_OTA_PROVISION = BASE + 25;
+    protected static final int EVENT_RESTART_RADIO = BASE + 26;
+    protected static final int EVENT_SET_INTERNAL_DATA_ENABLE = BASE + 27;
+    protected static final int EVENT_RESET_DONE = BASE + 28;
+    public static final int CMD_SET_DATA_ENABLE = BASE + 29;
+    public static final int EVENT_CLEAN_UP_ALL_CONNECTIONS = BASE + 30;
+    public static final int CMD_SET_DEPENDENCY_MET = BASE + 31;
 
     /***** Constants *****/
 
@@ -227,7 +228,7 @@
     /** indication of our availability (preconditions to trysetupData are met) **/
     protected boolean mAvailability = false;
 
-    // When false we will not auto attach and manully attaching is required.
+    // When false we will not auto attach and manually attaching is required.
     protected boolean mAutoAttachOnCreation = false;
 
     // State of screen
@@ -235,12 +236,6 @@
     //        really a lower power mode")
     protected boolean mIsScreenOn = true;
 
-    /** The link properties (dns, gateway, ip, etc) */
-    protected LinkProperties mLinkProperties = new LinkProperties();
-
-    /** The link capabilities */
-    protected LinkCapabilities mLinkCapabilities = new LinkCapabilities();
-
     /** Allows the generation of unique Id's for DataConnection objects */
     protected AtomicInteger mUniqueIdGenerator = new AtomicInteger(0);
 
@@ -248,6 +243,10 @@
     protected HashMap<Integer, DataConnection> mDataConnections =
         new HashMap<Integer, DataConnection>();
 
+    /** The data connection async channels */
+    protected HashMap<Integer, DataConnectionAc> mDataConnectionAsyncChannels =
+        new HashMap<Integer, DataConnectionAc>();
+
     /** Convert an ApnType string to Id (TODO: Use "enumeration" instead of String for ApnType) */
     protected HashMap<String, Integer> mApnToDataConnectionId =
                                     new HashMap<String, Integer>();
@@ -267,7 +266,6 @@
     /** Is packet service restricted by network */
     protected boolean mIsPsRestricted = false;
 
-
     /* Once disposed dont handle any messages */
     protected boolean mIsDisposed = false;
 
@@ -351,6 +349,10 @@
     }
 
     public void dispose() {
+        for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) {
+            dcac.disconnect();
+        }
+        mDataConnectionAsyncChannels.clear();
         mIsDisposed = true;
         mPhone.getContext().unregisterReceiver(this.mIntentReceiver);
     }
@@ -463,7 +465,13 @@
     @Override
     public void handleMessage(Message msg) {
         switch (msg.what) {
-
+            case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
+                log("DISCONNECTED_CONNECTED: msg=" + msg);
+                DataConnectionAc dcac = (DataConnectionAc) msg.obj;
+                mDataConnectionAsyncChannels.remove(dcac.dataConnection.getDataConnectionId());
+                dcac.disconnected();
+                break;
+            }
             case EVENT_ENABLE_NEW_APN:
                 onEnableApn(msg.arg1, msg.arg2);
                 break;
@@ -528,19 +536,20 @@
                 break;
             }
             case EVENT_RESET_DONE: {
+                if (DBG) log("EVENT_RESET_DONE");
                 onResetDone((AsyncResult) msg.obj);
                 break;
             }
             case CMD_SET_DATA_ENABLE: {
-                log("CMD_SET_DATA_ENABLE msg=" + msg);
                 boolean enabled = (msg.arg1 == ENABLED) ? true : false;
+                if (DBG) log("CMD_SET_DATA_ENABLE enabled=" + enabled);
                 onSetDataEnabled(enabled);
                 break;
             }
 
             case CMD_SET_DEPENDENCY_MET: {
-                log("CMD_SET_DEPENDENCY_MET msg=" + msg);
                 boolean met = (msg.arg1 == ENABLED) ? true : false;
+                if (DBG) log("CMD_SET_DEPENDENCY_MET met=" + met);
                 Bundle bundle = msg.getData();
                 if (bundle != null) {
                     String apnType = (String)bundle.get(APN_TYPE_KEY);
@@ -552,7 +561,7 @@
             }
 
             default:
-                Log.e("DATA", "Unidentified event = " + msg.what);
+                Log.e("DATA", "Unidentified event msg=" + msg);
                 break;
         }
     }
@@ -618,7 +627,8 @@
     protected LinkProperties getLinkProperties(String apnType) {
         int id = apnTypeToId(apnType);
         if (isApnIdEnabled(id)) {
-            return new LinkProperties(mLinkProperties);
+            DataConnectionAc dcac = mDataConnectionAsyncChannels.get(id);
+            return dcac.getLinkPropertiesSync();
         } else {
             return new LinkProperties();
         }
@@ -627,33 +637,13 @@
     protected LinkCapabilities getLinkCapabilities(String apnType) {
         int id = apnTypeToId(apnType);
         if (isApnIdEnabled(id)) {
-            return new LinkCapabilities(mLinkCapabilities);
+            DataConnectionAc dcac = mDataConnectionAsyncChannels.get(id);
+            return dcac.getLinkCapabilitiesSync();
         } else {
             return new LinkCapabilities();
         }
     }
 
-    /**
-     * Return the LinkProperties for the connection.
-     *
-     * @param connection
-     * @return a copy of the LinkProperties, is never null.
-     */
-    protected LinkProperties getLinkProperties(DataConnection connection) {
-        return connection.getLinkProperties();
-    }
-
-    /**
-     * A capability is an Integer/String pair, the capabilities
-     * are defined in the class LinkSocket#Key.
-     *
-     * @param connection
-     * @return a copy of this connections capabilities, may be empty but never null.
-     */
-    protected LinkCapabilities getLinkCapabilities(DataConnection connection) {
-        return connection.getLinkCapabilities();
-    }
-
     // tell all active apns of the current condition
     protected void notifyDataConnection(String reason) {
         for (int id = 0; id < APN_NUM_TYPES; id++) {
diff --git a/telephony/java/com/android/internal/telephony/IccUtils.java b/telephony/java/com/android/internal/telephony/IccUtils.java
index df579b0..c3b0ffc 100644
--- a/telephony/java/com/android/internal/telephony/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/IccUtils.java
@@ -416,7 +416,6 @@
         int colorNumber = data[valueIndex++] & 0xFF;
         int clutOffset = ((data[valueIndex++] & 0xFF) << 8)
                 | (data[valueIndex++] & 0xFF);
-        length = length - 6;
 
         int[] colorIndexArray = getCLUT(data, clutOffset, colorNumber);
         if (true == transparency) {
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index c052e5142..490051d 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -3024,7 +3024,7 @@
             dataCall.active = p.readInt();
             dataCall.type = p.readString();
             String addresses = p.readString();
-            if (TextUtils.isEmpty(addresses)) {
+            if (!TextUtils.isEmpty(addresses)) {
                 dataCall.addresses = addresses.split(" ");
             }
         } else {
@@ -3033,7 +3033,8 @@
             dataCall.active = p.readInt();
             dataCall.type = p.readString();
             dataCall.ifname = p.readString();
-            if (TextUtils.isEmpty(dataCall.ifname)) {
+            if ((dataCall.status == DataConnection.FailCause.NONE.getErrorCode()) &&
+                    TextUtils.isEmpty(dataCall.ifname)) {
               throw new RuntimeException("getDataCallState, no ifname");
             }
             String addresses = p.readString();
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
index e299d4a..d55f346 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
@@ -20,7 +20,6 @@
 import android.util.Log;
 
 import com.android.internal.telephony.DataConnection;
-import com.android.internal.telephony.DataConnection.FailCause;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.RetryManager;
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index dc85017..4b185a0 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -37,10 +37,12 @@
 import com.android.internal.telephony.DataCallState;
 import com.android.internal.telephony.DataConnection.FailCause;
 import com.android.internal.telephony.DataConnection;
+import com.android.internal.telephony.DataConnectionAc;
 import com.android.internal.telephony.DataConnectionTracker;
 import com.android.internal.telephony.EventLogTags;
 import com.android.internal.telephony.RetryManager;
 import com.android.internal.telephony.Phone;
+import com.android.internal.util.AsyncChannel;
 
 import java.util.ArrayList;
 
@@ -297,14 +299,18 @@
         boolean notificationDeferred = false;
         for (DataConnection conn : mDataConnections.values()) {
             if(conn != null) {
+                DataConnectionAc dcac =
+                    mDataConnectionAsyncChannels.get(conn.getDataConnectionId());
                 if (tearDown) {
                     if (DBG) log("cleanUpConnection: teardown, call conn.disconnect");
-                    conn.disconnect(reason, obtainMessage(EVENT_DISCONNECT_DONE,
+                    conn.tearDown(reason, obtainMessage(EVENT_DISCONNECT_DONE,
                             conn.getDataConnectionId(), 0, reason));
                     notificationDeferred = true;
                 } else {
                     if (DBG) log("cleanUpConnection: !tearDown, call conn.resetSynchronously");
-                    conn.resetSynchronously();
+                    if (dcac != null) {
+                        dcac.resetSync();
+                    }
                     notificationDeferred = false;
                 }
             }
@@ -319,11 +325,13 @@
     }
 
     private CdmaDataConnection findFreeDataConnection() {
-        for (DataConnection dc : mDataConnections.values()) {
-            if (dc.isInactive()) {
-                return (CdmaDataConnection) dc;
+        for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) {
+            if (dcac.isInactiveSync()) {
+                log("found free GsmDataConnection");
+                return (CdmaDataConnection) dcac.dataConnection;
             }
         }
+        log("NO free CdmaDataConnection");
         return null;
     }
 
@@ -349,12 +357,12 @@
         }
         mActiveApn = new ApnSetting(apnId, "", "", "", "", "", "", "", "", "",
                                     "", 0, types, "IP", "IP");
-        if (DBG) log("setupData: mActiveApn=" + mActiveApn);
+        if (DBG) log("call conn.bringUp mActiveApn=" + mActiveApn);
 
         Message msg = obtainMessage();
         msg.what = EVENT_DATA_SETUP_COMPLETE;
         msg.obj = reason;
-        conn.connect(msg, mActiveApn);
+        conn.bringUp(msg, mActiveApn);
 
         setState(State.INITING);
         notifyDataConnection(reason);
@@ -653,11 +661,7 @@
         }
 
         if (ar.exception == null) {
-            // TODO: We should clear LinkProperties/Capabilities when torn down or disconnected
-            mLinkProperties = getLinkProperties(mPendingDataConnection);
-            mLinkCapabilities = getLinkCapabilities(mPendingDataConnection);
-
-            // everything is setup
+            // Everything is setup
             notifyDefaultData(reason);
         } else {
             FailCause cause = (FailCause) (ar.result);
@@ -767,6 +771,16 @@
             int id = mUniqueIdGenerator.getAndIncrement();
             dataConn = CdmaDataConnection.makeDataConnection(mCdmaPhone, id, rm);
             mDataConnections.put(id, dataConn);
+            DataConnectionAc dcac = new DataConnectionAc(dataConn, LOG_TAG);
+            int status = dcac.fullyConnectSync(mPhone.getContext(), this, dataConn.getHandler());
+            if (status == AsyncChannel.STATUS_SUCCESSFUL) {
+                log("Fully connected");
+                mDataConnectionAsyncChannels.put(dcac.dataConnection.getDataConnectionId(), dcac);
+            } else {
+                log("Could not connect to dcac.dataConnection=" + dcac.dataConnection +
+                        " status=" + status);
+            }
+
         }
     }
 
@@ -897,6 +911,7 @@
 
     @Override
     public void handleMessage (Message msg) {
+        if (DBG) log("CdmaDCT handleMessage msg=" + msg);
 
         if (!mPhone.mIsTheCurrentActivePhone || mIsDisposed) {
             log("Ignore CDMA msgs since CDMA phone is inactive");
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
index f019487..32c5d75 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
@@ -44,22 +44,14 @@
 
     CDMALTEPhone mCdmaLtePhone;
 
-    private int gprsState = ServiceState.STATE_OUT_OF_SERVICE;
-
-    private int newGPRSState = ServiceState.STATE_OUT_OF_SERVICE;
+    private ServiceState  mLteSS;  // The last LTE state from Voice Registration
 
     public CdmaLteServiceStateTracker(CDMALTEPhone phone) {
         super(phone);
         mCdmaLtePhone = phone;
-        if (DBG) log("CdmaLteServiceStateTracker Constructors");
-    }
 
-    /**
-     * @return The current GPRS state. IN_SERVICE is the same as "attached" and
-     *         OUT_OF_SERVICE is the same as detached.
-     */
-    public int getCurrentDataConnectionState() {
-        return gprsState;
+        mLteSS = new ServiceState();
+        if (DBG) log("CdmaLteServiceStateTracker Constructors");
     }
 
     @Override
@@ -77,11 +69,13 @@
     }
 
     /**
-     * The LTE data connection state, only return true here
+     * Set the cdmaSS for EVENT_POLL_STATE_REGISTRATION_CDMA
      */
     @Override
-    protected boolean checkAdditionalDataAvaiable() {
-        return newGPRSState != ServiceState.STATE_IN_SERVICE;
+    protected void setCdmaTechnology(int radioTechnology) {
+        // Called on voice registration state response.
+        // Just record new CDMA radio technology
+        newSS.setRadioTechnology(radioTechnology);
     }
 
     /**
@@ -109,14 +103,10 @@
                 }
             }
 
-            newGPRSState = regCodeToServiceState(regState);
             // Not sure if this is needed in CDMALTE phone.
             // mDataRoaming = regCodeIsRoaming(regState);
-            if (newGPRSState == ServiceState.STATE_IN_SERVICE) {
-                this.newCdmaDataConnectionState = newGPRSState;
-                newNetworkType = type;
-                newSS.setRadioTechnology(type);
-            }
+            mLteSS.setRadioTechnology(type);
+            mLteSS.setState(regCodeToServiceState(regState));
         } else {
             super.handlePollStateResultMessage(what, ar);
         }
@@ -216,6 +206,21 @@
 
     @Override
     protected void pollStateDone() {
+        // determine data NetworkType from both LET and CDMA SS
+        if (mLteSS.getState() == ServiceState.STATE_IN_SERVICE) {
+            //in LTE service
+            newNetworkType = mLteSS.getRadioTechnology();
+            mNewDataConnectionState = mLteSS.getState();
+            newSS.setRadioTechnology(newNetworkType);
+            log("pollStateDone LTE/eHRPD STATE_IN_SERVICE newNetworkType = " + newNetworkType);
+        } else {
+            // LTE out of service, get CDMA Service State
+            newNetworkType = newSS.getRadioTechnology();
+            mNewDataConnectionState = radioTechnologyToDataServiceState(newNetworkType);
+            log("pollStateDone CDMA STATE_IN_SERVICE newNetworkType = " + newNetworkType +
+                " mNewDataConnectionState = " + mNewDataConnectionState);
+        }
+
         if (DBG) log("pollStateDone: oldSS=[" + ss + "] newSS=[" + newSS + "]");
 
         boolean hasRegistered = ss.getState() != ServiceState.STATE_IN_SERVICE
@@ -225,15 +230,15 @@
                 && newSS.getState() != ServiceState.STATE_IN_SERVICE;
 
         boolean hasCdmaDataConnectionAttached =
-            this.cdmaDataConnectionState != ServiceState.STATE_IN_SERVICE
-                && this.newCdmaDataConnectionState == ServiceState.STATE_IN_SERVICE;
+            mDataConnectionState != ServiceState.STATE_IN_SERVICE
+                && mNewDataConnectionState == ServiceState.STATE_IN_SERVICE;
 
         boolean hasCdmaDataConnectionDetached =
-            this.cdmaDataConnectionState == ServiceState.STATE_IN_SERVICE
-                && this.newCdmaDataConnectionState != ServiceState.STATE_IN_SERVICE;
+            mDataConnectionState == ServiceState.STATE_IN_SERVICE
+                && mNewDataConnectionState != ServiceState.STATE_IN_SERVICE;
 
         boolean hasCdmaDataConnectionChanged =
-            cdmaDataConnectionState != newCdmaDataConnectionState;
+            mDataConnectionState != mNewDataConnectionState;
 
         boolean hasNetworkTypeChanged = networkType != newNetworkType;
 
@@ -272,9 +277,9 @@
         }
         // Add an event log when connection state changes
         if (ss.getState() != newSS.getState()
-                || cdmaDataConnectionState != newCdmaDataConnectionState) {
+                || mDataConnectionState != mNewDataConnectionState) {
             EventLog.writeEvent(EventLogTags.CDMA_SERVICE_STATE_CHANGE, ss.getState(),
-                    cdmaDataConnectionState, newSS.getState(), newCdmaDataConnectionState);
+                    mDataConnectionState, newSS.getState(), mNewDataConnectionState);
         }
 
         ServiceState tss;
@@ -283,6 +288,7 @@
         newSS = tss;
         // clean slate for next time
         newSS.setStateOutOfService();
+        mLteSS.setStateOutOfService();
 
         // TODO: 4G Tech Handoff
         // if (has4gHandoff) {
@@ -309,11 +315,9 @@
         cellLoc = newCellLoc;
         newCellLoc = tcl;
 
-        cdmaDataConnectionState = newCdmaDataConnectionState;
+        mDataConnectionState = mNewDataConnectionState;
         networkType = newNetworkType;
 
-        gprsState = newCdmaDataConnectionState;
-
         newSS.setStateOutOfService(); // clean slate for next time
 
         if (hasNetworkTypeChanged) {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index ac8352d..afebebe 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -97,8 +97,8 @@
     /**
      * Initially assume no data connection.
      */
-    protected int cdmaDataConnectionState = ServiceState.STATE_OUT_OF_SERVICE;
-    protected int newCdmaDataConnectionState = ServiceState.STATE_OUT_OF_SERVICE;
+    protected int mDataConnectionState = ServiceState.STATE_OUT_OF_SERVICE;
+    protected int mNewDataConnectionState = ServiceState.STATE_OUT_OF_SERVICE;
     protected int mRegistrationState = -1;
     protected RegistrantList cdmaForSubscriptionInfoReadyRegistrants = new RegistrantList();
 
@@ -217,8 +217,8 @@
         phone.mRuimRecords.unregisterForRecordsLoaded(this);
         cm.unSetOnSignalStrengthUpdate(this);
         cm.unSetOnNITZTime(this);
-        cr.unregisterContentObserver(this.mAutoTimeObserver);
-        cr.unregisterContentObserver(this.mAutoTimeZoneObserver);
+        cr.unregisterContentObserver(mAutoTimeObserver);
+        cr.unregisterContentObserver(mAutoTimeZoneObserver);
     }
 
     @Override
@@ -548,10 +548,12 @@
     }
 
     /**
-    * The LTE data connection state, only return true here
+    * Determine data network type based on radio technology.
     */
-    protected boolean checkAdditionalDataAvaiable(){
-        return true;
+    protected void setCdmaTechnology(int radioTechnology){
+        mNewDataConnectionState = radioTechnologyToDataServiceState(radioTechnology);
+        newSS.setRadioTechnology(radioTechnology);
+        newNetworkType = radioTechnology;
     }
 
     /**
@@ -639,12 +641,7 @@
                     regCodeIsRoaming(registrationState) && !isRoamIndForHomeSystem(states[10]);
             newSS.setState (regCodeToServiceState(registrationState));
 
-            if(checkAdditionalDataAvaiable()) {
-                this.newCdmaDataConnectionState =
-                        radioTechnologyToDataServiceState(radioTechnology);
-                newSS.setRadioTechnology(radioTechnology);
-                newNetworkType = radioTechnology;
-            }
+            setCdmaTechnology(radioTechnology);
 
             newSS.setCssIndicator(cssIndicator);
             newSS.setSystemAndNetworkId(systemId, networkId);
@@ -953,15 +950,15 @@
             && newSS.getState() != ServiceState.STATE_IN_SERVICE;
 
         boolean hasCdmaDataConnectionAttached =
-            this.cdmaDataConnectionState != ServiceState.STATE_IN_SERVICE
-            && this.newCdmaDataConnectionState == ServiceState.STATE_IN_SERVICE;
+            mDataConnectionState != ServiceState.STATE_IN_SERVICE
+            && mNewDataConnectionState == ServiceState.STATE_IN_SERVICE;
 
         boolean hasCdmaDataConnectionDetached =
-            this.cdmaDataConnectionState == ServiceState.STATE_IN_SERVICE
-            && this.newCdmaDataConnectionState != ServiceState.STATE_IN_SERVICE;
+            mDataConnectionState == ServiceState.STATE_IN_SERVICE
+            && mNewDataConnectionState != ServiceState.STATE_IN_SERVICE;
 
         boolean hasCdmaDataConnectionChanged =
-                       cdmaDataConnectionState != newCdmaDataConnectionState;
+                       mDataConnectionState != mNewDataConnectionState;
 
         boolean hasNetworkTypeChanged = networkType != newNetworkType;
 
@@ -975,10 +972,10 @@
 
         // Add an event log when connection state changes
         if (ss.getState() != newSS.getState() ||
-                cdmaDataConnectionState != newCdmaDataConnectionState) {
+                mDataConnectionState != mNewDataConnectionState) {
             EventLog.writeEvent(EventLogTags.CDMA_SERVICE_STATE_CHANGE,
-                    ss.getState(), cdmaDataConnectionState,
-                    newSS.getState(), newCdmaDataConnectionState);
+                    ss.getState(), mDataConnectionState,
+                    newSS.getState(), mNewDataConnectionState);
         }
 
         ServiceState tss;
@@ -992,7 +989,7 @@
         cellLoc = newCellLoc;
         newCellLoc = tcl;
 
-        cdmaDataConnectionState = newCdmaDataConnectionState;
+        mDataConnectionState = mNewDataConnectionState;
         networkType = newNetworkType;
         // this new state has been applied - forget it until we get a new new state
         newNetworkType = 0;
@@ -1175,7 +1172,7 @@
     }
 
 
-    private int radioTechnologyToDataServiceState(int code) {
+    protected int radioTechnologyToDataServiceState(int code) {
         int retVal = ServiceState.STATE_OUT_OF_SERVICE;
         switch(code) {
         case 0:
@@ -1226,14 +1223,14 @@
      * ServiceState.RADIO_TECHNOLOGY_UNKNOWN is the same as detached.
      */
     /*package*/ int getCurrentCdmaDataConnectionState() {
-        return cdmaDataConnectionState;
+        return mDataConnectionState;
     }
 
     /**
     * TODO: In the future, we need remove getCurrentCdmaDataConnectionState
     */
     public int getCurrentDataConnectionState() {
-        return cdmaDataConnectionState;
+        return mDataConnectionState;
     }
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java
index 545ad8a..9695344 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java
@@ -21,7 +21,6 @@
 import android.util.Patterns;
 import android.text.TextUtils;
 
-import com.android.internal.telephony.ApnSetting;
 import com.android.internal.telephony.DataConnection;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneBase;
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 8e675fc..a1b4376 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -53,6 +53,7 @@
 import com.android.internal.telephony.ApnSetting;
 import com.android.internal.telephony.DataCallState;
 import com.android.internal.telephony.DataConnection;
+import com.android.internal.telephony.DataConnectionAc;
 import com.android.internal.telephony.DataConnectionTracker;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneBase;
@@ -60,6 +61,7 @@
 import com.android.internal.telephony.EventLogTags;
 import com.android.internal.telephony.DataConnection.FailCause;
 import com.android.internal.telephony.RILConstants;
+import com.android.internal.util.AsyncChannel;
 
 import java.io.IOException;
 import java.net.InetAddress;
@@ -119,7 +121,7 @@
 
     @Override
     protected void onActionIntentReconnectAlarm(Intent intent) {
-        log("GPRS reconnect alarm. Previous state was " + mState);
+        if (DBG) log("GPRS reconnect alarm. Previous state was " + mState);
 
         String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON);
         String type = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE);
@@ -224,7 +226,7 @@
         boolean possible = (isDataAllowed()
                 && !(getAnyDataEnabled() && (getOverallState() == State.FAILED)));
         if (!possible && DBG && isDataAllowed()) {
-            log("Data not possible.  No coverage: dataState = " + getOverallState());
+            if (DBG) log("Data not possible.  No coverage: dataState = " + getOverallState());
         }
         return possible;
     }
@@ -319,10 +321,10 @@
     protected LinkProperties getLinkProperties(String apnType) {
         ApnContext apnContext = mApnContexts.get(apnType);
         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();
+            DataConnectionAc dcac = apnContext.getDataConnectionAc();
+            if (dcac != null) {
+                if (DBG) log("return link properites for " + apnType);
+                return dcac.getLinkPropertiesSync();
             }
         }
         if (DBG) log("return new LinkProperties");
@@ -333,10 +335,10 @@
     protected LinkCapabilities getLinkCapabilities(String apnType) {
         ApnContext apnContext = mApnContexts.get(apnType);
         if (apnContext!=null) {
-            DataConnection dataConnection = apnContext.getDataConnection();
-            if (dataConnection != null) {
+            DataConnectionAc dataConnectionAc = apnContext.getDataConnectionAc();
+            if (dataConnectionAc != null) {
                 if (DBG) log("get active pdp is not null, return link Capabilities for " + apnType);
-                return dataConnection.getLinkCapabilities();
+                return dataConnectionAc.getLinkCapabilitiesSync();
             }
         }
         if (DBG) log("return new LinkCapabilities");
@@ -424,6 +426,7 @@
         }
 
         if (!isAnyEnabled) { // Nothing enabled. return IDLE.
+            if (DBG) log( "overall state is IDLE");
             return State.IDLE;
         }
 
@@ -450,34 +453,34 @@
      */
     @Override
     public synchronized int enableApnType(String apnType) {
-        if (DBG) log("calling enableApnType with type:" + apnType);
-
         ApnContext apnContext = mApnContexts.get(apnType);
         if (apnContext == null || !isApnTypeAvailable(apnType)) {
-            if (DBG) log("type not available");
+            if (DBG) log("enableApnType: " + apnType + " is type not available");
             return Phone.APN_TYPE_NOT_AVAILABLE;
         }
 
         // If already active, return
-        log("enableApnType(" + apnType + ")" + ", mState(" + apnContext.getState() + ")");
+        if (DBG) log("enableApnType: " + apnType + " mState(" + apnContext.getState() + ")");
 
         if (apnContext.getState() == State.INITING) {
-            if (DBG) log("return APN_REQUEST_STARTED");
+            if (DBG) log("enableApnType: return APN_REQUEST_STARTED");
             return Phone.APN_REQUEST_STARTED;
         }
         else if (apnContext.getState() == State.CONNECTED) {
-            if (DBG) log("return APN_ALREADY_ACTIVE");
+            if (DBG) log("enableApnType: return APN_ALREADY_ACTIVE");
             return Phone.APN_ALREADY_ACTIVE;
         }
         else if (apnContext.getState() == State.DISCONNECTING) {
-            if (DBG) log("requested APN while disconnecting");
+            if (DBG) log("enableApnType: while disconnecting, return APN_REQUEST_STARTED");
             apnContext.setPendingAction(ApnContext.PENDING_ACTION_RECONNECT);
             return Phone.APN_REQUEST_STARTED;
         }
 
-        if (DBG) log("new apn request for type " + apnType + " is to be handled");
         setEnabled(apnTypeToId(apnType), true);
-        if (DBG) log("return APN_REQUEST_STARTED");
+        if (DBG) {
+            log("enableApnType: new apn request for type " + apnType +
+                    " return APN_REQUEST_STARTED");
+        }
         return Phone.APN_REQUEST_STARTED;
     }
 
@@ -502,7 +505,7 @@
 
     @Override
     public synchronized int disableApnType(String type) {
-        if (DBG) log("calling disableApnType with type:" + type);
+        if (DBG) log("disableApnType:" + type);
         ApnContext apnContext = mApnContexts.get(type);
 
         if (apnContext != null) {
@@ -515,18 +518,19 @@
                 apnContext.setReason(Phone.REASON_DATA_DISABLED);
                 msg.obj = apnContext;
                 sendMessage(msg);
-                if (DBG) log("return APN_REQUEST_STARTED");
+                if (DBG) log("diableApnType: return APN_REQUEST_STARTED");
                 return Phone.APN_REQUEST_STARTED;
             } else {
-                if (DBG) log("return APN_ALREADY_INACTIVE");
+                if (DBG) log("disableApnType: return APN_ALREADY_INACTIVE");
                 apnContext.setEnabled(false);
                 apnContext.setDataConnection(null);
                 return Phone.APN_ALREADY_INACTIVE;
             }
 
         } else {
-            if (DBG)
-                log("no apn context was found, return APN_REQUEST_FAILED");
+            if (DBG) {
+                log("disableApnType: no apn context was found, return APN_REQUEST_FAILED");
+            }
             return Phone.APN_REQUEST_FAILED;
         }
     }
@@ -583,12 +587,15 @@
          * We presently believe it is unnecessary to tear down the PDP context
          * when GPRS detaches, but we should stop the network polling.
          */
+        if (DBG) log ("onDataConnectionDetached: stop polling and notify detached");
         stopNetStatPoll();
         notifyDataConnection(Phone.REASON_DATA_DETACHED);
     }
 
     private void onDataConnectionAttached() {
+        if (DBG) log("onDataConnectionAttached");
         if (getOverallState() == State.CONNECTED) {
+            if (DBG) log("onDataConnectionAttached: start polling notify attached");
             startNetStatPoll();
             notifyDataConnection(Phone.REASON_DATA_ATTACHED);
         }
@@ -624,11 +631,40 @@
             }
             if (mIsPsRestricted) reason += " - mIsPsRestricted= true";
             if (!desiredPowerState) reason += " - desiredPowerState= false";
-            log("Data not allowed due to" + reason);
+            if (DBG) log("isDataAllowed: not allowed due to" + reason);
         }
         return allowed;
     }
 
+    /**
+     * Release the apnContext
+     *
+     * @param apnContext
+     * @param tearDown
+     * @return refCount
+     */
+    private int releaseApnContext(ApnContext apnContext, boolean tearDown) {
+        if (apnContext == null) {
+            if (DBG) loge("releaseApnContext: apnContext null should not happen, ignore");
+            return -1;
+        }
+        DataConnection dc = apnContext.getDataConnection();
+        if (dc == null) {
+            if (DBG) loge("releaseApnContext: apnContext dc == null should not happen, ignore");
+            return -1;
+        }
+        int refCount = dc.decAndGetRefCount();
+        if (DBG) log("releaseApnContext: dec refCount=" + refCount + " tearDown=" + tearDown);
+        if (tearDown && (refCount == 0)) {
+            if (DBG) log("releaseApnContext: tearing down");
+            Message msg = obtainMessage(EVENT_DISCONNECT_DONE, apnContext);
+            apnContext.getDataConnection().tearDown(apnContext.getReason(), msg);
+        }
+        apnContext.setDataConnection(null);
+        apnContext.setDataConnectionAc(null);
+        return refCount;
+    }
+
     private void setupDataOnReadyApns(String reason) {
         // Only check for default APN state
         for (ApnContext apnContext : mApnContexts.values()) {
@@ -651,9 +687,8 @@
 
     private boolean trySetupData(String reason, String type) {
         if (DBG) {
-            log("***trySetupData for type:" + type +
-                    " due to " + (reason == null ? "(unspecified)" : reason) +
-                    " isPsRestricted=" + mIsPsRestricted);
+            log("trySetupData: " + type + " due to " + (reason == null ? "(unspecified)" : reason)
+                    + " isPsRestricted=" + mIsPsRestricted);
         }
 
         if (type == null) {
@@ -663,18 +698,16 @@
         ApnContext apnContext = mApnContexts.get(type);
 
         if (apnContext == null ){
-            if (DBG) log("new apn context for type:" + type);
+            if (DBG) log("trySetupData new apn context for type:" + type);
             apnContext = new ApnContext(type, LOG_TAG);
             mApnContexts.put(type, apnContext);
         }
         apnContext.setReason(reason);
 
         return trySetupData(apnContext);
-
     }
 
     private boolean trySetupData(ApnContext apnContext) {
-
         if (DBG) {
             log("trySetupData for type:" + apnContext.getApnType() +
                     " due to " + apnContext.getReason());
@@ -687,7 +720,7 @@
             apnContext.setState(State.CONNECTED);
             mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
 
-            log("(fix?) We're on the simulator; assuming data is connected");
+            log("trySetupData: (fix?) We're on the simulator; assuming data is connected");
             return true;
         }
 
@@ -699,13 +732,15 @@
             if (apnContext.getState() == State.IDLE) {
                 ArrayList<ApnSetting> waitingApns = buildWaitingApns(apnContext.getApnType());
                 if (waitingApns.isEmpty()) {
-                    if (DBG) log("No APN found");
+                    if (DBG) log("trySetupData: No APN found");
                     notifyNoData(GsmDataConnection.FailCause.MISSING_UNKNOWN_APN, apnContext);
                     notifyOffApnsOfAvailability(apnContext.getReason(), false);
                     return false;
                 } else {
                     apnContext.setWaitingApns(waitingApns);
-                    log ("Create from mAllApns : " + apnListToString(mAllApns));
+                    if (DBG) {
+                        log ("trySetupData: Create from mAllApns : " + apnListToString(mAllApns));
+                    }
                 }
             }
 
@@ -735,7 +770,7 @@
 
         for (ApnContext apnContext : mApnContexts.values()) {
             if (!apnContext.isReady()) {
-                if (DBG) log("notify disconnected for type:" + apnContext.getApnType());
+                if (DBG) log("notifyOffApnOfAvailability type:" + apnContext.getApnType());
                 mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(),
                                             apnContext.getApnType(),
                                             Phone.DataState.DISCONNECTED);
@@ -753,7 +788,7 @@
      * @param reason reason for the clean up.
      */
     protected void cleanUpAllConnections(boolean tearDown, String reason) {
-        if (DBG) log("Clean up all connections due to " + reason);
+        if (DBG) log("cleanUpAllConnections: tearDown=" + tearDown + " reason=" + reason);
 
         for (ApnContext apnContext : mApnContexts.values()) {
             apnContext.setReason(reason);
@@ -784,11 +819,13 @@
     private void cleanUpConnection(boolean tearDown, ApnContext apnContext) {
 
         if (apnContext == null) {
-            if (DBG) log("apn context is null");
+            if (DBG) log("cleanUpConnection: apn context is null");
             return;
         }
 
-        if (DBG) log("Clean up connection due to " + apnContext.getReason());
+        if (DBG) {
+            log("cleanUpConnection: tearDown=" + tearDown + " reason=" + apnContext.getReason());
+        }
 
         // Clear the reconnect alarm, if set.
         if (apnContext.getReconnectIntent() != null) {
@@ -799,24 +836,26 @@
         }
 
         if (apnContext.getState() == State.IDLE || apnContext.getState() == State.DISCONNECTING) {
-            if (DBG) log("state is in " + apnContext.getState());
+            if (DBG) log("cleanUpConnection: state= " + apnContext.getState());
             return;
         }
 
         if (apnContext.getState() == State.FAILED) {
-            if (DBG) log("state is in FAILED");
+            if (DBG) log("cleanUpConnection: state is in FAILED");
             apnContext.setState(State.IDLE);
             return;
         }
 
         DataConnection conn = apnContext.getDataConnection();
         if (conn != null) {
+            DataConnectionAc dcac = mDataConnectionAsyncChannels.get(conn.getDataConnectionId());
             apnContext.setState(State.DISCONNECTING);
             if (tearDown) {
-                Message msg = obtainMessage(EVENT_DISCONNECT_DONE, apnContext);
-                conn.disconnect(apnContext.getReason(), msg);
+                releaseApnContext(apnContext, tearDown);
             } else {
-                conn.resetSynchronously();
+                if (dcac != null) {
+                    dcac.resetSync();
+                }
                 apnContext.setState(State.IDLE);
                 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
             }
@@ -871,27 +910,31 @@
     }
 
     private GsmDataConnection findFreeDataConnection() {
-        for (DataConnection dc : mDataConnections.values()) {
-            if (dc.isInactive()) {
-                log("found free GsmDataConnection");
-                return (GsmDataConnection) dc;
+        for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) {
+            if (dcac.isInactiveSync()) {
+                log("findFreeDataConnection: found free GsmDataConnection");
+                return (GsmDataConnection) dcac.dataConnection;
             }
         }
-        log("NO free GsmDataConnection");
+        log("findFreeDataConnection: NO free GsmDataConnection");
         return null;
     }
 
     protected GsmDataConnection findReadyDataConnection(ApnSetting apn) {
         if (DBG)
-            log("findReadyDataConnection for apn string <" +
+            log("findReadyDataConnection: apn string <" +
                 (apn!=null?(apn.toString()):"null") +">");
-        for (DataConnection conn : mDataConnections.values()) {
-            GsmDataConnection dc = (GsmDataConnection) conn;
-            if (DBG) log("dc apn string <" +
-                         (dc.getApn() != null ? (dc.getApn().toString()) : "null") + ">");
-            if (dc.getApn() != null && apn != null
-                && dc.getApn().toString().equals(apn.toString())) {
-                return dc;
+        if (apn == null) {
+            return null;
+        }
+        for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) {
+            ApnSetting apnSetting = dcac.getApnSettingSync();
+            if (DBG) {
+                log("findReadyDataConnection: dc apn string <" +
+                         (apnSetting != null ? (apnSetting.toString()) : "null") + ">");
+            }
+            if ((apnSetting != null) && TextUtils.equals(apnSetting.toString(), apn.toString())) {
+                return (GsmDataConnection) dcac.dataConnection;
             }
         }
         return null;
@@ -899,7 +942,7 @@
 
 
     private boolean setupData(ApnContext apnContext) {
-        if (DBG) log("enter setupData!");
+        if (DBG) log("setupData: apnContext=" + apnContext);
         ApnSetting apn;
         GsmDataConnection dc;
 
@@ -920,7 +963,7 @@
         }
 
         if (dc == null) {
-            dc = createDataConnection(apnContext);
+            dc = createDataConnection(apnContext.getApnType());
         }
 
         if (dc == null) {
@@ -928,17 +971,19 @@
             return false;
         }
 
-        apnContext.setApnSetting(apn);
-        apnContext.setDataConnection(dc);
         dc.setProfileId( profileId );
         dc.setActiveApnType(apnContext.getApnType());
+        int refCount = dc.incAndGetRefCount();
+        if (DBG) log("setupData: init dc and apnContext refCount=" + refCount);
+        DataConnectionAc dcac = mDataConnectionAsyncChannels.get(dc.getDataConnectionId());
+        apnContext.setDataConnectionAc(mDataConnectionAsyncChannels.get(dc.getDataConnectionId()));
+        apnContext.setApnSetting(apn);
+        apnContext.setDataConnection(dc);
 
         Message msg = obtainMessage();
         msg.what = EVENT_DATA_SETUP_COMPLETE;
         msg.obj = apnContext;
-
-        if (DBG) log("dc connect!");
-        dc.connect(msg, apn);
+        dc.bringUp(msg, apn);
 
         apnContext.setState(State.INITING);
         mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
@@ -981,7 +1026,7 @@
 
         // 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");
+        if (DBG) log("onApnChanged: createAllApnList and cleanUpAllConnections");
         createAllApnList();
         cleanUpAllConnections(isConnected, Phone.REASON_APN_CHANGED);
         if (!isConnected) {
@@ -998,25 +1043,30 @@
     private void onDataStateChanged (AsyncResult ar) {
         ArrayList<DataCallState> dataCallStates;
 
+        if (DBG) log("onDataStateChanged(ar) E");
         dataCallStates = (ArrayList<DataCallState>)(ar.result);
 
         if (ar.exception != null) {
             // This is probably "radio not available" or something
             // of that sort. If so, the whole connection is going
             // to come down soon anyway
+            if (DBG) log("onDataStateChanged(ar): exception; likely radio not available, ignore");
             return;
         }
 
         for (ApnContext apnContext : mApnContexts.values()) {
             onDataStateChanged(dataCallStates, apnContext);
         }
+        if (DBG) log("onDataStateChanged(ar) X");
     }
 
     private void onDataStateChanged (ArrayList<DataCallState> dataCallStates,
                                      ApnContext apnContext) {
+        if (DBG) log("onDataStateChanged(dataCallState, apnContext):  apnContext=" + apnContext);
 
         if (apnContext == null) {
             // Should not happen
+            if (DBG) log("onDataStateChanged(dataCallState, apnContext):  ignore apnContext=null");
             return;
         }
 
@@ -1027,28 +1077,37 @@
             // context is still listed with active = false, which
             // makes it hard to distinguish an activating context from
             // an activated-and-then deactivated one.
-            if (!dataCallStatesHasCID(dataCallStates, apnContext.getDataConnection().getCid())) {
+            DataConnectionAc dcac = apnContext.getDataConnectionAc();
+            if (dcac == null) {
+                if (DBG) log("onDataStateChanged(dataCallState, apnContext):  dcac==null BAD NEWS");
+                return;
+            }
+            int cid = dcac.getCidSync();
+            if (!dataCallStatesHasCID(dataCallStates, cid)) {
                 // It looks like the PDP context has deactivated.
                 // Tear everything down and try to reconnect.
 
-                Log.i(LOG_TAG, "PDP connection has dropped. Reconnecting");
-
+                if (DBG) {
+                    log("onDataStateChanged(dataCallStates,apnContext) " +
+                        "PDP connection has dropped. Reconnecting");
+                }
                 // Add an event log when the network drops PDP
-                int cid = getCellLocationId();
-                EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP, cid,
+                int cellLocationId = getCellLocationId();
+                EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP, cellLocationId,
                         TelephonyManager.getDefault().getNetworkType());
 
                 cleanUpConnection(true, apnContext);
-                return;
             } else if (!dataCallStatesHasActiveCID(dataCallStates,
-                    apnContext.getDataConnection().getCid())) {
+                    apnContext.getDataConnectionAc().getCidSync())) {
 
-                Log.i(LOG_TAG, "PDP connection has dropped (active=false case). "
-                                    + " Reconnecting");
+                if (DBG) {
+                    log("onDataStateChanged(dataCallStates,apnContext) " +
+                        "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,
+                int cellLocationId = getCellLocationId();
+                EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP, cellLocationId,
                         TelephonyManager.getDefault().getNetworkType());
 
                 cleanUpConnection(true, apnContext);
@@ -1057,9 +1116,10 @@
     }
 
     private void notifyDefaultData(ApnContext apnContext) {
-        if (DBG)
-            log("notifyDefaultData for type: " + apnContext.getApnType()
+        if (DBG) {
+            log("notifyDefaultData: type=" + apnContext.getApnType()
                 + ", reason:" + apnContext.getReason());
+        }
         apnContext.setState(State.CONNECTED);
         // setState(State.CONNECTED);
         mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
@@ -1091,21 +1151,25 @@
             if (mPdpResetCount < maxPdpReset) {
                 mPdpResetCount++;
                 EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, mSentSinceLastRecv);
+                if (DBG) log("doRecovery() cleanup all connections mPdpResetCount < max");
                 cleanUpAllConnections(true, Phone.REASON_PDP_RESET);
             } else {
                 mPdpResetCount = 0;
                 EventLog.writeEvent(EventLogTags.PDP_REREGISTER_NETWORK, mSentSinceLastRecv);
+                if (DBG) log("doRecovery() re-register getting preferred network type");
                 mPhone.getServiceStateTracker().reRegisterNetwork(null);
             }
             // TODO: Add increasingly drastic recovery steps, eg,
             // reset the radio, reset the device.
+        } else {
+            if (DBG) log("doRecovery(): ignore, we're not connected");
         }
     }
 
     @Override
     protected void startNetStatPoll() {
         if (getOverallState() == State.CONNECTED && mNetStatPollEnabled == false) {
-            log("[DataConnection] Start poll NetStat");
+            if (DBG) log("startNetStatPoll");
             resetPollStats();
             mNetStatPollEnabled = true;
             mPollNetStat.run();
@@ -1116,12 +1180,12 @@
     protected void stopNetStatPoll() {
         mNetStatPollEnabled = false;
         removeCallbacks(mPollNetStat);
-        log("[DataConnection] Stop poll NetStat");
+        if (DBG) log("stopNetStatPoll");
     }
 
     @Override
     protected void restartRadio() {
-        log("************TURN OFF RADIO**************");
+        if (DBG) log("restartRadio: ************TURN OFF RADIO**************");
         cleanUpAllConnections(true, Phone.REASON_RADIO_TURNED_OFF);
         mPhone.getServiceStateTracker().powerOffRadioSafely(this);
         /* Note: no need to call setRadioPower(true).  Assuming the desired
@@ -1202,7 +1266,7 @@
                 if (mNoRecvPollCount < noRecvPollLimit) {
                     // 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");
+                    if (DBG) log("Polling: no DATAIN in a while; polling PDP");
                     mPhone.mCM.getDataCallList(obtainMessage(EVENT_DATA_STATE_CHANGED));
 
                     mNoRecvPollCount++;
@@ -1212,7 +1276,7 @@
                             Settings.Secure.PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS,
                             POLL_NETSTAT_SLOW_MILLIS);
                 } else {
-                    if (DBG) log("Sent " + String.valueOf(mSentSinceLastRecv) +
+                    if (DBG) log("Polling: Sent " + String.valueOf(mSentSinceLastRecv) +
                                         " pkts since last received start recovery process");
                     stopNetStatPoll();
                     sendMessage(obtainMessage(EVENT_START_RECOVERY));
@@ -1262,14 +1326,12 @@
 
     private void reconnectAfterFail(FailCause lastFailCauseCode, ApnContext apnContext) {
         if (apnContext == null) {
-            Log.d(LOG_TAG, "It is impossible");
+            loge("reconnectAfterFail: apnContext == null, impossible");
             return;
         }
         if (apnContext.getState() == State.FAILED) {
             if (!apnContext.getDataConnection().isRetryNeeded()) {
-                if (!apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT)){
-                    // if no more retries on a secondary APN attempt, tell the world and revert.
-                    apnContext.setDataConnection(null);
+                if (!apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT)) {
                     notifyDataConnection(Phone.REASON_APN_FAILED);
                     return;
                 }
@@ -1278,7 +1340,7 @@
                     apnContext.getDataConnection().retryForeverUsingLastTimeout();
                 } else {
                     // Try to Re-register to the network.
-                    log("PDP activate failed, Reregistering to the network");
+                    if (DBG) log("reconnectAfterFail: activate failed, Reregistering to network");
                     mReregisterOnReconnectFailure = true;
                     mPhone.getServiceStateTracker().reRegisterNetwork(null);
                     apnContext.getDataConnection().resetRetryCount();
@@ -1287,8 +1349,10 @@
             }
 
             int nextReconnectDelay = apnContext.getDataConnection().getRetryTimer();
-            log("PDP activate failed. Scheduling next attempt for "
+            if (DBG) {
+                log("reconnectAfterFail: activate failed. Scheduling next attempt for "
                     + (nextReconnectDelay / 1000) + "s");
+            }
 
             AlarmManager am =
                 (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
@@ -1305,8 +1369,10 @@
             apnContext.getDataConnection().increaseRetryCount();
 
             if (!shouldPostNotification(lastFailCauseCode)) {
-                Log.d(LOG_TAG, "NOT Posting GPRS Unavailable notification "
+                if (DBG) {
+                    log("reconnectAfterFail: NOT Posting GPRS Unavailable notification "
                                 + "-- likely transient error");
+                }
             } else {
                 notifyNoData(lastFailCauseCode, apnContext);
             }
@@ -1315,7 +1381,7 @@
 
     private void notifyNoData(GsmDataConnection.FailCause lastFailCauseCode,
                               ApnContext apnContext) {
-        if (DBG) log( "notifyNoData for type:" + apnContext.getApnType());
+        if (DBG) log( "notifyNoData: type=" + apnContext.getApnType());
         apnContext.setState(State.FAILED);
         if (lastFailCauseCode.isPermanentFail()
             && (!apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT))) {
@@ -1327,7 +1393,7 @@
         if (DBG) log("onRecordsLoaded: createAllApnList");
         createAllApnList();
         if (mRadioAvailable) {
-            if (DBG) log("onRecordsLoaded, notifying data availability");
+            if (DBG) log("onRecordsLoaded: notifying data availability");
             notifyDataAvailability(null);
         }
         setupDataOnReadyApns(Phone.REASON_SIM_LOADED);
@@ -1337,7 +1403,8 @@
     protected void onSetDependencyMet(String apnType, boolean met) {
         ApnContext apnContext = mApnContexts.get(apnType);
         if (apnContext == null) {
-            log("ApnContext not found in onSetDependencyMet(" + apnType + ", " + met + ")");
+            loge("onSetDependencyMet: ApnContext not found in onSetDependencyMet(" +
+                    apnType + ", " + met + ")");
             return;
         }
         applyNewState(apnContext, apnContext.isEnabled(), met);
@@ -1373,12 +1440,15 @@
                     }
                     trySetup = true;
                 } else {
-                    // TODO send notifications
-                    if (DBG) {
-                        log("Found existing connection for " + apnContext.getApnType() +
-                                ": " + conn);
-                    }
+                    int refCount = conn.incAndGetRefCount();
                     apnContext.setDataConnection(conn);
+                    apnContext.setDataConnectionAc(
+                            mDataConnectionAsyncChannels.get(conn.getDataConnectionId()));
+                    if (DBG) {
+                        log("applyNewState: Found existing connection for " +
+                                apnContext.getApnType() + " inc refCount=" + refCount +
+                                " conn=" + conn);
+                    }
                 }
             }
         }
@@ -1395,9 +1465,16 @@
             DataConnection conn = c.getDataConnection();
             if (conn != null) {
                 ApnSetting apnSetting = c.getApnSetting();
-                if (apnSetting != null && apnSetting.canHandleType(apnType)) return conn;
+                if (apnSetting != null && apnSetting.canHandleType(apnType)) {
+                    if (DBG) {
+                        log("checkForConnectionForApnContext: apnContext=" + apnContext +
+                                " found conn=" + conn);
+                    }
+                    return conn;
+                }
             }
         }
+        if (DBG) log("checkForConnectionForApnContext: apnContext=" + apnContext + " NO conn");
         return null;
     }
 
@@ -1405,43 +1482,47 @@
     protected void onEnableApn(int apnId, int enabled) {
         ApnContext apnContext = mApnContexts.get(apnIdToType(apnId));
         if (apnContext == null) {
-            log("ApnContext not found in onEnableApn(" + apnId + ", " + enabled + ")");
+            loge("onEnableApn(" + apnId + ", " + enabled + "): NO ApnContext");
             return;
         }
         // TODO change our retry manager to use the appropriate numbers for the new APN
-        log("onEnableApn with ApnContext E");
+        if (DBG) log("onEnableApn: apnContext=" + apnContext + " call applyNewState");
         applyNewState(apnContext, enabled == ENABLED, apnContext.getDependencyMet());
     }
 
     @Override
     // TODO: We shouldnt need this.
     protected boolean onTrySetupData(String reason) {
+        if (DBG) log("onTrySetupData: reason=" + reason);
         setupDataOnReadyApns(reason);
         return true;
     }
 
     protected boolean onTrySetupData(ApnContext apnContext) {
+        if (DBG) log("onTrySetupData: apnContext=" + apnContext);
         return trySetupData(apnContext);
     }
 
     @Override
     protected void onRoamingOff() {
+        if (DBG) log("onRoamingOff");
         setupDataOnReadyApns(Phone.REASON_ROAMING_OFF);
     }
 
     @Override
     protected void onRoamingOn() {
         if (getDataOnRoamingEnabled()) {
+            if (DBG) log("onRoamingOn: setup data on roaming");
             setupDataOnReadyApns(Phone.REASON_ROAMING_ON);
         } else {
-            if (DBG) log("Tear down data connection on roaming.");
+            if (DBG) log("onRoamingOn: Tear down data connection on roaming.");
             cleanUpAllConnections(true, Phone.REASON_ROAMING_ON);
         }
     }
 
     @Override
     protected void onRadioAvailable() {
-
+        if (DBG) log("onRadioAvailable");
         mRadioAvailable = true;
         if (mPhone.getSimulatedRadioControl() != null) {
             // Assume data is connected on the simulator
@@ -1449,7 +1530,7 @@
             // setState(State.CONNECTED);
             notifyDataConnection(null);
 
-            log("We're on the simulator; assuming data is connected");
+            log("onRadioAvailable: We're on the simulator; assuming data is connected");
         }
 
         if (mPhone.mSIMRecords.getRecordsLoaded()) {
@@ -1477,7 +1558,7 @@
             // FIXME  this can be improved
             log("We're on the simulator; assuming radio off is meaningless");
         } else {
-            if (DBG) log("Radio is off and clean up all connection");
+            if (DBG) log("onRadioOffOrNotAvailable: is off and clean up all connections");
             cleanUpAllConnections(false, Phone.REASON_RADIO_TURNED_OFF);
         }
         notifyDataAvailability(null);
@@ -1490,27 +1571,29 @@
 
         if(ar.userObj instanceof ApnContext){
             apnContext = (ApnContext)ar.userObj;
+        } else {
+            throw new RuntimeException("onDataSetupComplete: No apnContext");
         }
+        DataConnectionAc dcac = apnContext.getDataConnectionAc();
+        if (dcac == null) {
+            throw new RuntimeException("onDataSetupCompete: No dcac");
+        }
+        DataConnection dc = apnContext.getDataConnection();
 
         if (ar.exception == null) {
-            // Everything is setup
-            // TODO: We should clear LinkProperties/Capabilities when torn down or disconnected
             if (DBG) {
                 log(String.format("onDataSetupComplete: success apn=%s",
-                    apnContext.getWaitingApns().get(0).apn));
+                    apnContext.getWaitingApns().get(0).apn) + " refCount=" + dc.getRefCount());
             }
-            mLinkProperties = getLinkProperties(apnContext.getApnType());
-            mLinkCapabilities = getLinkCapabilities(apnContext.getApnType());
-
             ApnSetting apn = apnContext.getApnSetting();
             if (apn.proxy != null && apn.proxy.length() != 0) {
                 try {
                     ProxyProperties proxy = new ProxyProperties(apn.proxy,
                             Integer.parseInt(apn.port), null);
-                    mLinkProperties.setHttpProxy(proxy);
+                    dcac.setLinkPropertiesHttpProxySync(proxy);
                 } catch (NumberFormatException e) {
-                    loge("NumberFormatException making ProxyProperties (" + apn.port +
-                            "): " + e);
+                    loge("onDataSetupComplete: NumberFormatException making ProxyProperties (" +
+                            apn.port + "): " + e);
                 }
             }
 
@@ -1518,7 +1601,7 @@
             if(TextUtils.equals(apnContext.getApnType(),Phone.APN_TYPE_DEFAULT)) {
                 SystemProperties.set("gsm.defaultpdpcontext.active", "true");
                 if (canSetPreferApn && mPreferredApn == null) {
-                    log("PREFERED APN is null");
+                    if (DBG) log("onDataSetupComplete: PREFERED APN is null");
                     mPreferredApn = apnContext.getApnSetting();
                     if (mPreferredApn != null) {
                         setPreferredApn(mPreferredApn.id);
@@ -1528,15 +1611,13 @@
                 SystemProperties.set("gsm.defaultpdpcontext.active", "false");
             }
             notifyDefaultData(apnContext);
-
-            // TODO: For simultaneous PDP support, we need to build another
-            // trigger another TRY_SETUP_DATA for the next APN type.  (Note
-            // that the existing connection may service that type, in which
-            // case we should try the next type, etc.
-            // I dont believe for simultaneous PDP you need to trigger. Each
-            // Connection should be independent and they can be setup simultaneously
-            // So, dont have to wait till one is finished.
         } else {
+            int refCount = releaseApnContext(apnContext, false);
+            if (DBG) {
+                log(String.format("onDataSetupComplete: error apn=%s",
+                    apnContext.getWaitingApns().get(0).apn) + " refCount=" + refCount);
+            }
+
             GsmDataConnection.FailCause cause;
             cause = (GsmDataConnection.FailCause) (ar.result);
             if (DBG) {
@@ -1573,7 +1654,6 @@
                         log("onDataSetupComplete: All APN's had permanent failures, stop retrying");
                     }
                     apnContext.setState(State.FAILED);
-                    apnContext.setDataConnection(null);
                     notifyDataConnection(Phone.REASON_APN_FAILED);
                 } else {
                     if (DBG) log("onDataSetupComplete: Not all permanent failures, retry");
@@ -1597,7 +1677,7 @@
     protected void onDisconnectDone(int connId, AsyncResult ar) {
         ApnContext apnContext = null;
 
-        if(DBG) log("EVENT_DISCONNECT_DONE connId=" + connId);
+        if(DBG) log("onDisconnectDone: EVENT_DISCONNECT_DONE connId=" + connId);
         if (ar.userObj instanceof ApnContext) {
             apnContext = (ApnContext) ar.userObj;
         } else {
@@ -1610,9 +1690,7 @@
 
         // Check if APN disabled.
         if (apnContext.getPendingAction() == ApnContext.PENDING_ACTION_APN_DISABLE) {
-           apnContext.setEnabled(false);
            apnContext.setPendingAction(ApnContext.PENDING_ACTION_NONE);
-           apnContext.setDataConnection(null);
         }
         mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
 
@@ -1648,7 +1726,9 @@
 
     @Override
     protected void onVoiceCallStarted() {
+        if (DBG) log("onVoiceCallStarted");
         if (isConnected() && ! mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
+            if (DBG) log("onVoiceCallStarted stop polling");
             stopNetStatPoll();
             notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
         }
@@ -1656,6 +1736,7 @@
 
     @Override
     protected void onVoiceCallEnded() {
+        if (DBG) log("onVoiceCallEnded");
         if (isConnected()) {
             if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
                 startNetStatPoll();
@@ -1688,10 +1769,10 @@
 
     @Override
     protected void notifyDataConnection(String reason) {
-        if (DBG) log("notify all enabled connection for:" + reason);
+        if (DBG) log("notifyDataConnection: reason=" + reason);
         for (ApnContext apnContext : mApnContexts.values()) {
             if (apnContext.isReady()) {
-                if (DBG) log("notify for type:"+apnContext.getApnType());
+                if (DBG) log("notifyDataConnection: type:"+apnContext.getApnType());
                 mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(),
                         apnContext.getApnType());
             }
@@ -1738,17 +1819,16 @@
     }
 
     /** Return the id for a new data connection */
-    private GsmDataConnection createDataConnection(ApnContext apnContext) {
-        String apnType = apnContext.getApnType();
-        log("createDataConnection(" + apnType + ") E");
+    private GsmDataConnection createDataConnection(String apnType) {
+        if (DBG) log("createDataConnection(" + apnType + ") E");
         RetryManager rm = new RetryManager();
 
         if (apnType.equals(Phone.APN_TYPE_DEFAULT)) {
             if (!rm.configure(SystemProperties.get("ro.gsm.data_retry_config"))) {
                 if (!rm.configure(DEFAULT_DATA_RETRY_CONFIG)) {
                     // Should never happen, log an error and default to a simple linear sequence.
-                    log("Could not configure using DEFAULT_DATA_RETRY_CONFIG="
-                            + DEFAULT_DATA_RETRY_CONFIG);
+                    loge("createDataConnection: Could not configure using " +
+                            "DEFAULT_DATA_RETRY_CONFIG=" + DEFAULT_DATA_RETRY_CONFIG);
                     rm.configure(20, 2000, 1000);
                 }
             }
@@ -1756,8 +1836,8 @@
             if (!rm.configure(SystemProperties.get("ro.gsm.2nd_data_retry_config"))) {
                 if (!rm.configure(SECONDARY_DATA_RETRY_CONFIG)) {
                     // Should never happen, log an error and default to a simple sequence.
-                    log("Could note configure using SECONDARY_DATA_RETRY_CONFIG="
-                            + SECONDARY_DATA_RETRY_CONFIG);
+                    loge("createDataConnection: Could note configure using " +
+                            "SECONDARY_DATA_RETRY_CONFIG=" + SECONDARY_DATA_RETRY_CONFIG);
                     rm.configure("max_retries=3, 333, 333, 333");
                 }
             }
@@ -1767,18 +1847,25 @@
         GsmDataConnection conn = GsmDataConnection.makeDataConnection(mPhone, id, rm);
         conn.resetRetryCount();
         mDataConnections.put(id, conn);
-        apnContext.setDataConnection(conn);
+        DataConnectionAc dcac = new DataConnectionAc(conn, LOG_TAG);
+        int status = dcac.fullyConnectSync(mPhone.getContext(), this, conn.getHandler());
+        if (status == AsyncChannel.STATUS_SUCCESSFUL) {
+            mDataConnectionAsyncChannels.put(dcac.dataConnection.getDataConnectionId(), dcac);
+        } else {
+            loge("createDataConnection: Could not connect to dcac.mDc=" + dcac.dataConnection +
+                    " status=" + status);
+        }
 
-        log("createDataConnection(" + apnType + ") X id=" + id);
+        if (DBG) log("createDataConnection(" + apnType + ") X id=" + id);
         return conn;
     }
 
     private void destroyDataConnections() {
         if(mDataConnections != null) {
-            log("destroyDataConnectionList clear mDataConnectionList");
+            if (DBG) log("destroyDataConnections: clear mDataConnectionList");
             mDataConnections.clear();
         } else {
-            log("destroyDataConnectionList mDataConnecitonList is empty, ignore");
+            if (DBG) log("destroyDataConnectionList mDataConnecitonList is empty, ignore");
         }
     }
 
@@ -1802,8 +1889,10 @@
         String operator = mPhone.mSIMRecords.getSIMOperatorNumeric();
         if (requestedApnType.equals(Phone.APN_TYPE_DEFAULT)) {
             if (canSetPreferApn && mPreferredApn != null) {
-                log("Preferred APN:" + operator + ":"
+                if (DBG) {
+                    log("buildWaitingApns: Preferred APN:" + operator + ":"
                         + mPreferredApn.numeric + ":" + mPreferredApn);
+                }
                 if (mPreferredApn.numeric.equals(operator)) {
                     apnList.add(mPreferredApn);
                     if (DBG) log("buildWaitingApns: X added preferred apnList=" + apnList);
@@ -1894,10 +1983,10 @@
 
     @Override
     public void handleMessage (Message msg) {
-        if (DBG) log("GSMDataConnTrack handleMessage "+msg);
+        if (DBG) log("handleMessage msg=" + msg);
 
         if (!mPhone.mIsTheCurrentActivePhone || mIsDisposed) {
-            log("Ignore GSM msgs since GSM phone is inactive");
+            loge("handleMessage: Ignore GSM msgs since GSM phone is inactive");
             return;
         }
 
@@ -1941,7 +2030,7 @@
                  * PDP context and notify us with PDP_CONTEXT_CHANGED.
                  * But we should stop the network polling and prevent reset PDP.
                  */
-                log("EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
+                if (DBG) log("EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
                 stopNetStatPoll();
                 mIsPsRestricted = true;
                 break;
@@ -1951,7 +2040,7 @@
                  * When PS restrict is removed, we need setup PDP connection if
                  * PDP connection is down.
                  */
-                log("EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted);
+                if (DBG) log("EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted);
                 mIsPsRestricted  = false;
                 if (isConnected()) {
                     startNetStatPoll();
@@ -1968,19 +2057,20 @@
             case EVENT_TRY_SETUP_DATA:
                 if (msg.obj instanceof ApnContext) {
                     onTrySetupData((ApnContext)msg.obj);
+                } else if (msg.obj instanceof String) {
+                    onTrySetupData((String)msg.obj);
                 } else {
-                    if (msg.obj instanceof String) {
-                        onTrySetupData((String)msg.obj);
-                    }
+                    loge("EVENT_TRY_SETUP request w/o apnContext or String");
                 }
                 break;
 
             case EVENT_CLEAN_UP_CONNECTION:
                 boolean tearDown = (msg.arg1 == 0) ? false : true;
+                if (DBG) log("EVENT_CLEAN_UP_CONNECTION tearDown=" + tearDown);
                 if (msg.obj instanceof ApnContext) {
                     cleanUpConnection(tearDown, (ApnContext)msg.obj);
                 } else {
-                    loge("[GsmDataConnectionTracker] connectpion cleanup request w/o apn context");
+                    loge("EVENT_CLEAN_UP_CONNECTION request w/o apn context");
                 }
                 break;
             default:
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index c5c3f70..6285880 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -37,6 +37,15 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+
+        <activity
+                android:name="GLTextureViewActivity"
+                android:label="_TextureViewGL">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
         
         <activity
                 android:name="BitmapMeshActivity"
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java
new file mode 100644
index 0000000..7f97098
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java
@@ -0,0 +1,274 @@
+/*
+ * 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.test.hwui;
+
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.graphics.SurfaceTexture;
+import android.opengl.GLES20;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.TextureView;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGL11;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+import javax.microedition.khronos.opengles.GL;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class GLTextureViewActivity extends Activity implements TextureView.SurfaceTextureListener {
+    private RenderThread mRenderThread;
+    private TextureView mTextureView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mTextureView = new TextureView(this);
+        mTextureView.setSurfaceTextureListener(this);
+
+        setContentView(mTextureView, new FrameLayout.LayoutParams(500, 400, Gravity.CENTER));
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        mRenderThread.finish();
+    }
+
+    @Override
+    public void onSurfaceTextureAvailable(SurfaceTexture surface) {
+        mRenderThread = new RenderThread(surface);
+        mRenderThread.start();
+
+        mTextureView.setCameraDistance(5000);
+
+        ObjectAnimator animator = ObjectAnimator.ofFloat(mTextureView, "rotationY", 0.0f, 360.0f);
+        animator.setRepeatMode(ObjectAnimator.REVERSE);
+        animator.setRepeatCount(ObjectAnimator.INFINITE);
+        animator.setDuration(4000);
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                ((View) mTextureView.getParent()).invalidate();
+            }
+        });
+        animator.start();
+    }
+
+    @Override
+    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+    }
+
+    private static class RenderThread extends Thread {
+        private static final String LOG_TAG = "GLTextureView";
+
+        static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+        static final int EGL_SURFACE_TYPE = 0x3033;
+        static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400;
+        static final int EGL_OPENGL_ES2_BIT = 4;
+
+        private volatile boolean mFinished;
+
+        private SurfaceTexture mSurface;
+        
+        private EGL10 mEgl;
+        private EGLDisplay mEglDisplay;
+        private EGLConfig mEglConfig;
+        private EGLContext mEglContext;
+        private EGLSurface mEglSurface;
+        private GL mGL;
+
+        RenderThread(SurfaceTexture surface) {
+            mSurface = surface;
+        }
+
+        @Override
+        public void run() {
+            initGL();
+
+            float red = 0.0f;
+            while (!mFinished) {
+                checkCurrent();
+
+                GLES20.glClearColor(red, 0.0f, 0.0f, 1.0f);
+                int error = GLES20.glGetError();
+                if (error != GLES20.GL_NO_ERROR) {
+                    Log.w(LOG_TAG, "GL error = 0x" + Integer.toHexString(error));
+                }
+
+                GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+                error = GLES20.glGetError();
+                if (error != GLES20.GL_NO_ERROR) {
+                    Log.w(LOG_TAG, "GL error = 0x" + Integer.toHexString(error));
+                }
+
+                if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
+                    throw new RuntimeException("Cannot swap buffers");
+                }
+                
+                try {
+                    Thread.sleep(20);
+                } catch (InterruptedException e) {
+                    // Ignore
+                }
+
+                red += 0.021f;
+                if (red > 1.0f) red = 0.0f;
+            }
+
+            finishGL();
+        }
+
+        private void finishGL() {
+            mEgl.eglDestroyContext(mEglDisplay, mEglContext);
+            mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
+        }
+
+        private void checkCurrent() {
+            if (!mEglContext.equals(mEgl.eglGetCurrentContext()) ||
+                    !mEglSurface.equals(mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) {
+                if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+                    throw new RuntimeException("eglMakeCurrent failed "
+                            + getEGLErrorString(mEgl.eglGetError()));
+                }
+            }
+        }
+        
+        private void initGL() {
+            mEgl = (EGL10) EGLContext.getEGL();
+
+            mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+            if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
+                throw new RuntimeException("eglGetDisplay failed "
+                        + getEGLErrorString(mEgl.eglGetError()));
+            }
+            
+            int[] version = new int[2];
+            if (!mEgl.eglInitialize(mEglDisplay, version)) {
+                throw new RuntimeException("eglInitialize failed " +
+                        getEGLErrorString(mEgl.eglGetError()));
+            }
+
+            mEglConfig = chooseEglConfig();
+            if (mEglConfig == null) {
+                throw new RuntimeException("eglConfig not initialized");
+            }
+            
+            mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
+
+            mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface, null);
+
+            if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
+                int error = mEgl.eglGetError();
+                if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
+                    Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
+                    return;
+                }
+                throw new RuntimeException("createWindowSurface failed "
+                        + getEGLErrorString(error));
+            }
+
+            if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+                throw new RuntimeException("eglMakeCurrent failed "
+                        + getEGLErrorString(mEgl.eglGetError()));
+            }
+
+            mGL = mEglContext.getGL();
+        }
+        
+
+        EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
+            int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
+            return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);            
+        }
+
+        private EGLConfig chooseEglConfig() {
+            int[] configsCount = new int[1];
+            EGLConfig[] configs = new EGLConfig[1];
+            int[] configSpec = getConfig();
+            if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
+                throw new IllegalArgumentException("eglChooseConfig failed " +
+                        getEGLErrorString(mEgl.eglGetError()));
+            } else if (configsCount[0] > 0) {
+                return configs[0];
+            }
+            return null;
+        }
+        
+        private int[] getConfig() {
+            return new int[] {
+                    EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+                    EGL10.EGL_RED_SIZE, 8,
+                    EGL10.EGL_GREEN_SIZE, 8,
+                    EGL10.EGL_BLUE_SIZE, 8,
+                    EGL10.EGL_ALPHA_SIZE, 8,
+                    EGL10.EGL_DEPTH_SIZE, 0,
+                    EGL10.EGL_STENCIL_SIZE, 0,
+                    EGL10.EGL_NONE
+            };
+        }
+
+        static String getEGLErrorString(int error) {
+            switch (error) {
+                case EGL10.EGL_SUCCESS:
+                    return "EGL_SUCCESS";
+                case EGL10.EGL_NOT_INITIALIZED:
+                    return "EGL_NOT_INITIALIZED";
+                case EGL10.EGL_BAD_ACCESS:
+                    return "EGL_BAD_ACCESS";
+                case EGL10.EGL_BAD_ALLOC:
+                    return "EGL_BAD_ALLOC";
+                case EGL10.EGL_BAD_ATTRIBUTE:
+                    return "EGL_BAD_ATTRIBUTE";
+                case EGL10.EGL_BAD_CONFIG:
+                    return "EGL_BAD_CONFIG";
+                case EGL10.EGL_BAD_CONTEXT:
+                    return "EGL_BAD_CONTEXT";
+                case EGL10.EGL_BAD_CURRENT_SURFACE:
+                    return "EGL_BAD_CURRENT_SURFACE";
+                case EGL10.EGL_BAD_DISPLAY:
+                    return "EGL_BAD_DISPLAY";
+                case EGL10.EGL_BAD_MATCH:
+                    return "EGL_BAD_MATCH";
+                case EGL10.EGL_BAD_NATIVE_PIXMAP:
+                    return "EGL_BAD_NATIVE_PIXMAP";
+                case EGL10.EGL_BAD_NATIVE_WINDOW:
+                    return "EGL_BAD_NATIVE_WINDOW";
+                case EGL10.EGL_BAD_PARAMETER:
+                    return "EGL_BAD_PARAMETER";
+                case EGL10.EGL_BAD_SURFACE:
+                    return "EGL_BAD_SURFACE";
+                case EGL11.EGL_CONTEXT_LOST:
+                    return "EGL_CONTEXT_LOST";
+                default:
+                    return "0x" + Integer.toHexString(error);
+            }
+        }
+
+        void finish() {
+            mFinished = true;
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java
index 55fab3f..7173a85 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java
@@ -86,6 +86,14 @@
             canvas.drawLines(copyPoints, 0, 12, p);
         }
 
+        private void drawVerticalLine(Canvas canvas, Paint p, float length, float x, float y) {
+            canvas.drawLine(x, y, x, y + length, p);
+        }
+
+        private void drawDiagonalLine(Canvas canvas, Paint p, float length, float x, float y) {
+            canvas.drawLine(x, y, x + length, y + length, p);
+        }
+
         @Override
         protected void onDraw(Canvas canvas) {
             super.onDraw(canvas);
@@ -145,6 +153,99 @@
             canvas.translate(60, 0);
             drawLines(canvas, p, mOffset/2, yOffset/2);
             canvas.restore();
+
+            yOffset += 100;
+            canvas.save();
+            p.setStrokeWidth(1);
+            float x = 10 + mOffset;
+            for (float length = 1; length <= 10; length +=1 ) {
+                p.setAntiAlias(false);
+                drawVerticalLine(canvas, p, length, x, yOffset);
+                x += 5;
+                p.setAntiAlias(true);
+                drawVerticalLine(canvas, p, length, x, yOffset);
+                x += 5;
+            }
+            p.setStrokeWidth(5);
+            for (float length = 1; length <= 10; length +=1 ) {
+                p.setAntiAlias(false);
+                drawVerticalLine(canvas, p, length, x, yOffset);
+                x += 10;
+                p.setAntiAlias(true);
+                drawVerticalLine(canvas, p, length, x, yOffset);
+                x += 10;
+            }
+            canvas.restore();
+
+            yOffset += 20;
+            canvas.save();
+            p.setStrokeWidth(1);
+            x = 10 + mOffset;
+            for (float length = 1; length <= 10; length +=1 ) {
+                p.setAntiAlias(false);
+                drawDiagonalLine(canvas, p, length, x, yOffset);
+                x += 5;
+                p.setAntiAlias(true);
+                drawDiagonalLine(canvas, p, length, x, yOffset);
+                x += 5;
+            }
+            p.setStrokeWidth(2);
+            for (float length = 1; length <= 10; length +=1 ) {
+                p.setAntiAlias(false);
+                drawDiagonalLine(canvas, p, length, x, yOffset);
+                x += 10;
+                p.setAntiAlias(true);
+                drawDiagonalLine(canvas, p, length, x, yOffset);
+                x += 10;
+            }
+            canvas.restore();
+
+            yOffset += 20;
+            canvas.save();
+            p.setStrokeWidth(1);
+            x = 10 + mOffset;
+            for (float length = 1; length <= 10; length +=1 ) {
+                p.setAntiAlias(false);
+                canvas.drawLine(x, yOffset, x + 1, yOffset + length, p);
+                x += 5;
+                p.setAntiAlias(true);
+                canvas.drawLine(x, yOffset, x + 1, yOffset + length, p);
+                x += 5;
+            }
+            p.setStrokeWidth(2);
+            for (float length = 1; length <= 10; length +=1 ) {
+                p.setAntiAlias(false);
+                canvas.drawLine(x, yOffset, x + 1, yOffset + length, p);
+                x += 10;
+                p.setAntiAlias(true);
+                canvas.drawLine(x, yOffset, x + 1, yOffset + length, p);
+                x += 10;
+            }
+            canvas.restore();
+
+            yOffset += 20;
+            canvas.save();
+            p.setStrokeWidth(1);
+            x = 10 + mOffset;
+            for (float length = 1; length <= 10; length +=1 ) {
+                p.setAntiAlias(false);
+                canvas.drawLine(x, yOffset, x + length, yOffset + 1, p);
+                x += 5;
+                p.setAntiAlias(true);
+                canvas.drawLine(x, yOffset, x + length, yOffset + 1, p);
+                x += 5;
+            }
+            p.setStrokeWidth(2);
+            for (float length = 1; length <= 10; length +=1 ) {
+                p.setAntiAlias(false);
+                canvas.drawLine(x, yOffset, x + length, yOffset + 1, p);
+                x += 10;
+                p.setAntiAlias(true);
+                canvas.drawLine(x, yOffset, x + length, yOffset + 1, p);
+                x += 10;
+            }
+            canvas.restore();
+
         }
     }
 }
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
index 4726672..2feda57 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
@@ -84,4 +84,9 @@
         animator.setDuration(4000);
         animator.start();
     }
+
+    @Override
+    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+        // Ignored, the Camera does all the work for us
+    }
 }
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/threshold.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/threshold.rs
index f2f9a36..16ebe08 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/threshold.rs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/threshold.rs
@@ -63,7 +63,7 @@
 
 static void copyInput() {
     rs_allocation ain;
-    rsSetObject(&ain,rsGetAllocation(InPixel));
+    ain = rsGetAllocation(InPixel);
     uint32_t dimx = rsAllocationGetDimX(ain);
     uint32_t dimy = rsAllocationGetDimY(ain);
     for (uint32_t y = 0; y < dimy; y++) {
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs
index 3fc59fc..91bac88 100644
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs
+++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs
@@ -109,11 +109,11 @@
 static void displayFontSamples(int fillNum) {
 
     rs_font fonts[5];
-    rsSetObject(&fonts[0], gFontSans);
-    rsSetObject(&fonts[1], gFontSerif);
-    rsSetObject(&fonts[2], gFontSerifBold);
-    rsSetObject(&fonts[3], gFontSerifBoldItalic);
-    rsSetObject(&fonts[4], gFontSans);
+    fonts[0] = gFontSans;
+    fonts[1] = gFontSerif;
+    fonts[2] = gFontSerifBold;
+    fonts[3] = gFontSerifBoldItalic;
+    fonts[4] = gFontSans;
 
     uint width = gRenderSurfaceW;
     uint height = gRenderSurfaceH;
@@ -140,10 +140,6 @@
             }
         }
     }
-
-    for (int i = 0; i < 5; i ++) {
-        rsClearObject(&fonts[i]);
-    }
 }
 
 static void bindProgramVertexOrtho() {
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/rslist.rs b/tests/RenderScriptTests/tests/src/com/android/rs/test/rslist.rs
index aeae13f..d8663fb 100644
--- a/tests/RenderScriptTests/tests/src/com/android/rs/test/rslist.rs
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/rslist.rs
@@ -47,7 +47,7 @@
     rsgBindFont(gFont);
 
     rs_allocation listAlloc;
-    rsSetObject(&listAlloc, rsGetAllocation(gList));
+    listAlloc = rsGetAllocation(gList);
     int allocSize = rsAllocationGetDimX(listAlloc);
 
     int width = rsgGetWidth();
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/rstypes.rs b/tests/RenderScriptTests/tests/src/com/android/rs/test/rstypes.rs
index f3bf244..22d9c13 100644
--- a/tests/RenderScriptTests/tests/src/com/android/rs/test/rstypes.rs
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/rstypes.rs
@@ -48,17 +48,17 @@
 
     struct my_struct structTest;
 
-    rsSetObject(&fontTestLocal, fontTest);
+    fontTestLocal = fontTest;
     //allocationTestLocal = allocationTest;
 
-    rsSetObject(&fontTest, fontTestLocal);
+    fontTest = fontTestLocal;
     //allocationTest = allocationTestLocal;
 
     /*for (int i = 0; i < 4; i++) {
-        rsSetObject(&fontTestLocalArray[i], fontTestLocal);
+        fontTestLocalArray[i] = fontTestLocal;
     }*/
 
-    /*rsSetObject(&fontTest, fontTestLocalArray[3]);*/
+    /*fontTest = fontTestLocalArray[3];*/
 
     return failed;
 }
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index 6455d84..7f9fc31 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -23,6 +23,7 @@
 import android.net.LinkProperties;
 import android.net.NetworkUtils;
 import android.net.ProxyProperties;
+import android.net.RouteInfo;
 import android.net.wifi.WifiConfiguration.IpAssignment;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.net.wifi.WifiConfiguration.ProxySettings;
@@ -120,7 +121,7 @@
     private static final String ipConfigFile = Environment.getDataDirectory() +
             "/misc/wifi/ipconfig.txt";
 
-    private static final int IPCONFIG_FILE_VERSION = 1;
+    private static final int IPCONFIG_FILE_VERSION = 2;
 
     /* IP and proxy configuration keys */
     private static final String ID_KEY = "id";
@@ -445,9 +446,8 @@
             if (iter.hasNext()) {
                 LinkAddress linkAddress = iter.next();
                 dhcpInfoInternal.ipAddress = linkAddress.getAddress().getHostAddress();
-                Iterator<InetAddress>gateways = linkProperties.getGateways().iterator();
-                if (gateways.hasNext()) {
-                    dhcpInfoInternal.gateway = gateways.next().getHostAddress();
+                for (RouteInfo route : linkProperties.getRoutes()) {
+                    dhcpInfoInternal.addRoute(route);
                 }
                 dhcpInfoInternal.prefixLength = linkAddress.getNetworkPrefixLength();
                 Iterator<InetAddress> dnsIterator = linkProperties.getDnses().iterator();
@@ -604,9 +604,22 @@
                                     out.writeUTF(linkAddr.getAddress().getHostAddress());
                                     out.writeInt(linkAddr.getNetworkPrefixLength());
                                 }
-                                for (InetAddress gateway : linkProperties.getGateways()) {
+                                for (RouteInfo route : linkProperties.getRoutes()) {
                                     out.writeUTF(GATEWAY_KEY);
-                                    out.writeUTF(gateway.getHostAddress());
+                                    LinkAddress dest = route.getDestination();
+                                    if (dest != null) {
+                                        out.writeInt(1);
+                                        out.writeUTF(dest.getAddress().getHostAddress());
+                                        out.writeInt(dest.getNetworkPrefixLength());
+                                    } else {
+                                        out.writeInt(0);
+                                    }
+                                    if (route.getGateway() != null) {
+                                        out.writeInt(1);
+                                        out.writeUTF(route.getGateway().getHostAddress());
+                                    } else {
+                                        out.writeInt(0);
+                                    }
                                 }
                                 for (InetAddress inetAddr : linkProperties.getDnses()) {
                                     out.writeUTF(DNS_KEY);
@@ -682,7 +695,8 @@
             in = new DataInputStream(new BufferedInputStream(new FileInputStream(
                     ipConfigFile)));
 
-            if (in.readInt() != IPCONFIG_FILE_VERSION) {
+            int version = in.readInt();
+            if (version != 2 && version != 1) {
                 Log.e(TAG, "Bad version on IP configuration file, ignore read");
                 return;
             }
@@ -709,8 +723,22 @@
                                     NetworkUtils.numericToInetAddress(in.readUTF()), in.readInt());
                             linkProperties.addLinkAddress(linkAddr);
                         } else if (key.equals(GATEWAY_KEY)) {
-                            linkProperties.addGateway(
-                                    NetworkUtils.numericToInetAddress(in.readUTF()));
+                            LinkAddress dest = null;
+                            InetAddress gateway = null;
+                            if (version == 1) {
+                                // only supported default gateways - leave the dest/prefix empty
+                                gateway = NetworkUtils.numericToInetAddress(in.readUTF());
+                            } else {
+                                if (in.readInt() == 1) {
+                                    dest = new LinkAddress(
+                                            NetworkUtils.numericToInetAddress(in.readUTF()),
+                                            in.readInt());
+                                }
+                                if (in.readInt() == 1) {
+                                    gateway = NetworkUtils.numericToInetAddress(in.readUTF());
+                                }
+                            }
+                            linkProperties.addRoute(new RouteInfo(dest, gateway));
                         } else if (key.equals(DNS_KEY)) {
                             linkProperties.addDns(
                                     NetworkUtils.numericToInetAddress(in.readUTF()));
@@ -1022,22 +1050,21 @@
                         .getLinkAddresses();
                 Collection<InetAddress> currentDnses = currentConfig.linkProperties.getDnses();
                 Collection<InetAddress> newDnses = newConfig.linkProperties.getDnses();
-                Collection<InetAddress> currentGateways =
-                        currentConfig.linkProperties.getGateways();
-                Collection<InetAddress> newGateways = newConfig.linkProperties.getGateways();
+                Collection<RouteInfo> currentRoutes = currentConfig.linkProperties.getRoutes();
+                Collection<RouteInfo> newRoutes = newConfig.linkProperties.getRoutes();
 
                 boolean linkAddressesDiffer =
                         (currentLinkAddresses.size() != newLinkAddresses.size()) ||
                         !currentLinkAddresses.containsAll(newLinkAddresses);
                 boolean dnsesDiffer = (currentDnses.size() != newDnses.size()) ||
                         !currentDnses.containsAll(newDnses);
-                boolean gatewaysDiffer = (currentGateways.size() != newGateways.size()) ||
-                        !currentGateways.containsAll(newGateways);
+                boolean routesDiffer = (currentRoutes.size() != newRoutes.size()) ||
+                        !currentRoutes.containsAll(newRoutes);
 
                 if ((currentConfig.ipAssignment != newConfig.ipAssignment) ||
                         linkAddressesDiffer ||
                         dnsesDiffer ||
-                        gatewaysDiffer) {
+                        routesDiffer) {
                     ipChanged = true;
                 }
                 break;
@@ -1112,8 +1139,8 @@
         for (LinkAddress linkAddr : config.linkProperties.getLinkAddresses()) {
             linkProperties.addLinkAddress(linkAddr);
         }
-        for (InetAddress gateway : config.linkProperties.getGateways()) {
-            linkProperties.addGateway(gateway);
+        for (RouteInfo route : config.linkProperties.getRoutes()) {
+            linkProperties.addRoute(route);
         }
         for (InetAddress dns : config.linkProperties.getDnses()) {
             linkProperties.addDns(dns);
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 33d4e1f..9125037 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -48,6 +48,7 @@
 import android.net.ConnectivityManager;
 import android.net.DhcpInfo;
 import android.net.DhcpInfoInternal;
+import android.net.DhcpStateMachine;
 import android.net.InterfaceConfiguration;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
@@ -152,6 +153,7 @@
     private NetworkInfo mNetworkInfo;
     private SupplicantStateTracker mSupplicantStateTracker;
     private WpsStateMachine mWpsStateMachine;
+    private DhcpStateMachine mDhcpStateMachine;
 
     private AlarmManager mAlarmManager;
     private PendingIntent mScanIntent;
@@ -189,10 +191,10 @@
     static final int CMD_START_DRIVER                     = BASE + 13;
     /* Start the driver */
     static final int CMD_STOP_DRIVER                      = BASE + 14;
-    /* Indicates DHCP succeded */
-    static final int CMD_IP_CONFIG_SUCCESS                = BASE + 15;
-    /* Indicates DHCP failed */
-    static final int CMD_IP_CONFIG_FAILURE                = BASE + 16;
+    /* Indicates Static IP succeded */
+    static final int CMD_STATIC_IP_SUCCESS                = BASE + 15;
+    /* Indicates Static IP failed */
+    static final int CMD_STATIC_IP_FAILURE                = BASE + 16;
 
     /* Start the soft access point */
     static final int CMD_START_AP                         = BASE + 21;
@@ -338,8 +340,11 @@
      */
     private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
 
-    private static final int POWER_MODE_ACTIVE = 1;
-    private static final int POWER_MODE_AUTO = 0;
+    static final int POWER_MODE_ACTIVE = 1;
+    static final int POWER_MODE_AUTO = 0;
+
+    /* Tracks the power mode for restoration after a DHCP request/renewal goes through */
+    private int mPowerMode = POWER_MODE_AUTO;
 
     /**
      * Default framework scan interval in milliseconds. This is used in the scenario in which
@@ -1408,8 +1413,10 @@
          */
         NetworkUtils.resetConnections(mInterfaceName);
 
-        if (!NetworkUtils.stopDhcp(mInterfaceName)) {
-            Log.e(TAG, "Could not stop DHCP");
+        if (mDhcpStateMachine != null) {
+            mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
+            mDhcpStateMachine.quit();
+            mDhcpStateMachine = null;
         }
 
         /* Disable interface */
@@ -1435,6 +1442,100 @@
 
     }
 
+    void handlePreDhcpSetup() {
+        if (!mBluetoothConnectionActive) {
+            /*
+             * There are problems setting the Wi-Fi driver's power
+             * mode to active when bluetooth coexistence mode is
+             * enabled or sense.
+             * <p>
+             * We set Wi-Fi to active mode when
+             * obtaining an IP address because we've found
+             * compatibility issues with some routers with low power
+             * mode.
+             * <p>
+             * In order for this active power mode to properly be set,
+             * we disable coexistence mode until we're done with
+             * obtaining an IP address.  One exception is if we
+             * are currently connected to a headset, since disabling
+             * coexistence would interrupt that connection.
+             */
+            // Disable the coexistence mode
+            WifiNative.setBluetoothCoexistenceModeCommand(
+                    WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
+        }
+
+        mPowerMode =  WifiNative.getPowerModeCommand();
+        if (mPowerMode < 0) {
+            // Handle the case where supplicant driver does not support
+            // getPowerModeCommand.
+            mPowerMode = WifiStateMachine.POWER_MODE_AUTO;
+        }
+        if (mPowerMode != WifiStateMachine.POWER_MODE_ACTIVE) {
+            WifiNative.setPowerModeCommand(WifiStateMachine.POWER_MODE_ACTIVE);
+        }
+    }
+
+
+    void handlePostDhcpSetup() {
+        /* restore power mode */
+        WifiNative.setPowerModeCommand(mPowerMode);
+
+        // Set the coexistence mode back to its default value
+        WifiNative.setBluetoothCoexistenceModeCommand(
+                WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
+    }
+
+    private void handleSuccessfulIpConfiguration(DhcpInfoInternal dhcpInfoInternal) {
+        synchronized (mDhcpInfoInternal) {
+            mDhcpInfoInternal = dhcpInfoInternal;
+        }
+        mLastSignalLevel = -1; // force update of signal strength
+        WifiConfigStore.setIpConfiguration(mLastNetworkId, dhcpInfoInternal);
+        InetAddress addr = NetworkUtils.numericToInetAddress(dhcpInfoInternal.ipAddress);
+        mWifiInfo.setInetAddress(addr);
+        if (getNetworkDetailedState() == DetailedState.CONNECTED) {
+            //DHCP renewal in connected state
+            LinkProperties linkProperties = dhcpInfoInternal.makeLinkProperties();
+            linkProperties.setHttpProxy(WifiConfigStore.getProxyProperties(mLastNetworkId));
+            linkProperties.setInterfaceName(mInterfaceName);
+            if (!linkProperties.equals(mLinkProperties)) {
+                Log.d(TAG, "Link configuration changed for netId: " + mLastNetworkId
+                    + " old: " + mLinkProperties + "new: " + linkProperties);
+                NetworkUtils.resetConnections(mInterfaceName);
+                mLinkProperties = linkProperties;
+                sendLinkConfigurationChangedBroadcast();
+            }
+        } else {
+            configureLinkProperties();
+            setNetworkDetailedState(DetailedState.CONNECTED);
+            sendNetworkStateChangeBroadcast(mLastBssid);
+        }
+    }
+
+    private void handleFailedIpConfiguration() {
+        Log.e(TAG, "IP configuration failed");
+
+        mWifiInfo.setInetAddress(null);
+        /**
+         * If we've exceeded the maximum number of retries for DHCP
+         * to a given network, disable the network
+         */
+        if (++mReconnectCount > getMaxDhcpRetries()) {
+            Log.e(TAG, "Failed " +
+                    mReconnectCount + " times, Disabling " + mLastNetworkId);
+            WifiConfigStore.disableNetwork(mLastNetworkId);
+            mReconnectCount = 0;
+        }
+
+        /* DHCP times out after about 30 seconds, we do a
+         * disconnect and an immediate reconnect to try again
+         */
+        WifiNative.disconnectCommand();
+        WifiNative.reconnectCommand();
+
+    }
+
 
     /*********************************************************
      * Notifications from WifiMonitor
@@ -1606,6 +1707,8 @@
                 case CMD_FORGET_NETWORK:
                 case CMD_RSSI_POLL:
                 case CMD_ENABLE_ALL_NETWORKS:
+                case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
+                case DhcpStateMachine.CMD_POST_DHCP_ACTION:
                     break;
                 case CMD_START_WPS:
                     /* Return failure when the state machine cannot handle WPS initiation*/
@@ -2483,74 +2586,18 @@
     }
 
     class ConnectingState extends State {
-        boolean mModifiedBluetoothCoexistenceMode;
-        int mPowerMode;
-        boolean mUseStaticIp;
-        Thread mDhcpThread;
 
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
             EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-            mUseStaticIp = WifiConfigStore.isUsingStaticIp(mLastNetworkId);
-            if (!mUseStaticIp) {
-                mDhcpThread = null;
-                mModifiedBluetoothCoexistenceMode = false;
-                mPowerMode = POWER_MODE_AUTO;
 
-                if (!mBluetoothConnectionActive) {
-                    /*
-                     * There are problems setting the Wi-Fi driver's power
-                     * mode to active when bluetooth coexistence mode is
-                     * enabled or sense.
-                     * <p>
-                     * We set Wi-Fi to active mode when
-                     * obtaining an IP address because we've found
-                     * compatibility issues with some routers with low power
-                     * mode.
-                     * <p>
-                     * In order for this active power mode to properly be set,
-                     * we disable coexistence mode until we're done with
-                     * obtaining an IP address.  One exception is if we
-                     * are currently connected to a headset, since disabling
-                     * coexistence would interrupt that connection.
-                     */
-                    mModifiedBluetoothCoexistenceMode = true;
-
-                    // Disable the coexistence mode
-                    WifiNative.setBluetoothCoexistenceModeCommand(
-                            WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
-                }
-
-                mPowerMode =  WifiNative.getPowerModeCommand();
-                if (mPowerMode < 0) {
-                  // Handle the case where supplicant driver does not support
-                  // getPowerModeCommand.
-                    mPowerMode = POWER_MODE_AUTO;
-                }
-                if (mPowerMode != POWER_MODE_ACTIVE) {
-                    WifiNative.setPowerModeCommand(POWER_MODE_ACTIVE);
-                }
-
-                Log.d(TAG, "DHCP request started");
-                mDhcpThread = new Thread(new Runnable() {
-                    public void run() {
-                        DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal();
-                        if (NetworkUtils.runDhcp(mInterfaceName, dhcpInfoInternal)) {
-                            Log.d(TAG, "DHCP request succeeded");
-                            synchronized (mDhcpInfoInternal) {
-                                mDhcpInfoInternal = dhcpInfoInternal;
-                            }
-                            WifiConfigStore.setIpConfiguration(mLastNetworkId, dhcpInfoInternal);
-                            sendMessage(CMD_IP_CONFIG_SUCCESS);
-                        } else {
-                            Log.d(TAG, "DHCP request failed: " +
-                                    NetworkUtils.getDhcpError());
-                            sendMessage(CMD_IP_CONFIG_FAILURE);
-                        }
-                    }
-                });
-                mDhcpThread.start();
+             if (!WifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
+                //start DHCP
+                mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
+                        mContext, WifiStateMachine.this, mInterfaceName);
+                mDhcpStateMachine.registerForPreDhcpNotification();
+                mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
             } else {
                 DhcpInfoInternal dhcpInfoInternal = WifiConfigStore.getIpConfiguration(
                         mLastNetworkId);
@@ -2562,16 +2609,13 @@
                 try {
                     netd.setInterfaceConfig(mInterfaceName, ifcg);
                     Log.v(TAG, "Static IP configuration succeeded");
-                    synchronized (mDhcpInfoInternal) {
-                        mDhcpInfoInternal = dhcpInfoInternal;
-                    }
-                    sendMessage(CMD_IP_CONFIG_SUCCESS);
+                    sendMessage(CMD_STATIC_IP_SUCCESS, dhcpInfoInternal);
                 } catch (RemoteException re) {
                     Log.v(TAG, "Static IP configuration failed: " + re);
-                    sendMessage(CMD_IP_CONFIG_FAILURE);
+                    sendMessage(CMD_STATIC_IP_FAILURE);
                 } catch (IllegalStateException e) {
                     Log.v(TAG, "Static IP configuration failed: " + e);
-                    sendMessage(CMD_IP_CONFIG_FAILURE);
+                    sendMessage(CMD_STATIC_IP_FAILURE);
                 }
             }
          }
@@ -2580,44 +2624,26 @@
           if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
 
           switch(message.what) {
-              case CMD_IP_CONFIG_SUCCESS:
-                  mLastSignalLevel = -1; // force update of signal strength
-                  InetAddress addr;
-                  synchronized (mDhcpInfoInternal) {
-                      addr = NetworkUtils.numericToInetAddress(mDhcpInfoInternal.ipAddress);
+              case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
+                  handlePreDhcpSetup();
+                  mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE);
+                  break;
+              case DhcpStateMachine.CMD_POST_DHCP_ACTION:
+                  handlePostDhcpSetup();
+                  if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
+                      handleSuccessfulIpConfiguration((DhcpInfoInternal) message.obj);
+                      transitionTo(mConnectedState);
+                  } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
+                      handleFailedIpConfiguration();
+                      transitionTo(mDisconnectingState);
                   }
-                  mWifiInfo.setInetAddress(addr);
-                  configureLinkProperties();
-                  if (getNetworkDetailedState() == DetailedState.CONNECTED) {
-                      sendLinkConfigurationChangedBroadcast();
-                  } else {
-                      setNetworkDetailedState(DetailedState.CONNECTED);
-                      sendNetworkStateChangeBroadcast(mLastBssid);
-                  }
-                  //TODO: The framework is not detecting a DHCP renewal and a possible
-                  //IP change. we should detect this and send out a config change broadcast
+                  break;
+              case CMD_STATIC_IP_SUCCESS:
+                  handleSuccessfulIpConfiguration((DhcpInfoInternal) message.obj);
                   transitionTo(mConnectedState);
                   break;
-              case CMD_IP_CONFIG_FAILURE:
-                  mWifiInfo.setInetAddress(null);
-
-                  Log.e(TAG, "IP configuration failed");
-                  /**
-                   * If we've exceeded the maximum number of retries for DHCP
-                   * to a given network, disable the network
-                   */
-                  if (++mReconnectCount > getMaxDhcpRetries()) {
-                      Log.e(TAG, "Failed " +
-                              mReconnectCount + " times, Disabling " + mLastNetworkId);
-                      WifiConfigStore.disableNetwork(mLastNetworkId);
-                      mReconnectCount = 0;
-                  }
-
-                  /* DHCP times out after about 30 seconds, we do a
-                   * disconnect and an immediate reconnect to try again
-                   */
-                  WifiNative.disconnectCommand();
-                  WifiNative.reconnectCommand();
+              case CMD_STATIC_IP_FAILURE:
+                  handleFailedIpConfiguration();
                   transitionTo(mDisconnectingState);
                   break;
               case CMD_DISCONNECT:
@@ -2661,23 +2687,6 @@
           EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
           return HANDLED;
       }
-
-      @Override
-      public void exit() {
-          /* reset power state & bluetooth coexistence if on DHCP */
-          if (!mUseStaticIp) {
-              if (mPowerMode != POWER_MODE_ACTIVE) {
-                  WifiNative.setPowerModeCommand(mPowerMode);
-              }
-
-              if (mModifiedBluetoothCoexistenceMode) {
-                  // Set the coexistence mode back to its default value
-                  WifiNative.setBluetoothCoexistenceModeCommand(
-                          WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
-              }
-          }
-
-      }
     }
 
     class ConnectedState extends State {
@@ -2695,6 +2704,19 @@
             if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
             boolean eventLoggingEnabled = true;
             switch (message.what) {
+              case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
+                  handlePreDhcpSetup();
+                  mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE);
+                  break;
+              case DhcpStateMachine.CMD_POST_DHCP_ACTION:
+                  handlePostDhcpSetup();
+                  if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
+                      handleSuccessfulIpConfiguration((DhcpInfoInternal) message.obj);
+                  } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
+                      handleFailedIpConfiguration();
+                      transitionTo(mDisconnectingState);
+                  }
+                  break;
                 case CMD_DISCONNECT:
                     WifiNative.disconnectCommand();
                     transitionTo(mDisconnectingState);