Merge changes I1302cacd,I05125c79,I8d9653ce

* changes:
  MTP: Fix some warnings
  MTP: Include current property value in GetDevicePropDesc
  MTP: Fix reading and writing device property descriptors
diff --git a/api/current.xml b/api/current.xml
index 80428bd..42787e6 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -947,6 +947,17 @@
  visibility="public"
 >
 </field>
+<field name="SET_ALARM"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;com.android.alarm.permission.SET_ALARM&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="SET_ALWAYS_FINISH"
  type="java.lang.String"
  transient="false"
@@ -105949,6 +105960,17 @@
  visibility="public"
 >
 </field>
+<field name="ACTION_VIEW_DOWNLOADS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.action.VIEW_DOWNLOADS&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="COLUMN_BYTES_DOWNLOADED_SO_FAR"
  type="java.lang.String"
  transient="false"
@@ -143318,6 +143340,67 @@
 </package>
 <package name="android.provider"
 >
+<class name="AlarmClock"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="AlarmClock"
+ type="android.provider.AlarmClock"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<field name="ACTION_SET_ALARM"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.action.SET_ALARM&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_HOUR"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.extra.alarm.HOUR&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_MESSAGE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.extra.alarm.MESSAGE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_MINUTES"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.extra.alarm.MINUTES&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 <interface name="BaseColumns"
  abstract="true"
  static="false"
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 4dee350..b073004 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -35,9 +35,7 @@
 import android.view.IWindowManager;
 
 import java.io.BufferedReader;
-import java.io.DataInputStream;
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStreamReader;
@@ -482,6 +480,8 @@
     }
 
     class MyActivityController extends IActivityController.Stub {
+        final String mGdbPort;
+
         static final int STATE_NORMAL = 0;
         static final int STATE_CRASHED = 1;
         static final int STATE_EARLY_ANR = 2;
@@ -503,6 +503,14 @@
 
         int mResult;
 
+        Process mGdbProcess;
+        Thread mGdbThread;
+        boolean mGotGdbPrint;
+
+        MyActivityController(String gdbPort) {
+            mGdbPort = gdbPort;
+        }
+
         @Override
         public boolean activityResuming(String pkg) throws RemoteException {
             synchronized (this) {
@@ -532,7 +540,7 @@
                 System.out.println("stack:");
                 System.out.print(stackTrace);
                 System.out.println("#");
-                int result = waitControllerLocked(STATE_CRASHED);
+                int result = waitControllerLocked(pid, STATE_CRASHED);
                 return result == RESULT_CRASH_KILL ? false : true;
             }
         }
@@ -545,7 +553,7 @@
                 System.out.println("processName: " + processName);
                 System.out.println("processPid: " + pid);
                 System.out.println("annotation: " + annotation);
-                int result = waitControllerLocked(STATE_EARLY_ANR);
+                int result = waitControllerLocked(pid, STATE_EARLY_ANR);
                 if (result == RESULT_EARLY_ANR_KILL) return -1;
                 return 0;
             }
@@ -561,14 +569,83 @@
                 System.out.println("processStats:");
                 System.out.print(processStats);
                 System.out.println("#");
-                int result = waitControllerLocked(STATE_ANR);
+                int result = waitControllerLocked(pid, STATE_ANR);
                 if (result == RESULT_ANR_KILL) return -1;
                 if (result == RESULT_ANR_WAIT) return 1;
                 return 0;
             }
         }
 
-        int waitControllerLocked(int state) {
+        void killGdbLocked() {
+            mGotGdbPrint = false;
+            if (mGdbProcess != null) {
+                System.out.println("Stopping gdbserver");
+                mGdbProcess.destroy();
+                mGdbProcess = null;
+            }
+            if (mGdbThread != null) {
+                mGdbThread.interrupt();
+                mGdbThread = null;
+            }
+        }
+
+        int waitControllerLocked(int pid, int state) {
+            if (mGdbPort != null) {
+                killGdbLocked();
+
+                try {
+                    System.out.println("Starting gdbserver on port " + mGdbPort);
+                    System.out.println("Do the following:");
+                    System.out.println("  adb forward tcp:" + mGdbPort + " tcp:" + mGdbPort);
+                    System.out.println("  gdbclient app_process :" + mGdbPort);
+
+                    mGdbProcess = Runtime.getRuntime().exec(new String[] {
+                            "gdbserver", ":" + mGdbPort, "--attach", Integer.toString(pid)
+                    });
+                    final InputStreamReader converter = new InputStreamReader(
+                            mGdbProcess.getInputStream());
+                    mGdbThread = new Thread() {
+                        @Override
+                        public void run() {
+                            BufferedReader in = new BufferedReader(converter);
+                            String line;
+                            int count = 0;
+                            while (true) {
+                                synchronized (MyActivityController.this) {
+                                    if (mGdbThread == null) {
+                                        return;
+                                    }
+                                    if (count == 2) {
+                                        mGotGdbPrint = true;
+                                        MyActivityController.this.notifyAll();
+                                    }
+                                }
+                                try {
+                                    line = in.readLine();
+                                    if (line == null) {
+                                        return;
+                                    }
+                                    System.out.println("GDB: " + line);
+                                    count++;
+                                } catch (IOException e) {
+                                    return;
+                                }
+                            }
+                        }
+                    };
+                    mGdbThread.start();
+
+                    // Stupid waiting for .5s.  Doesn't matter if we end early.
+                    try {
+                        this.wait(500);
+                    } catch (InterruptedException e) {
+                    }
+
+                } catch (IOException e) {
+                    System.err.println("Failure starting gdbserver: " + e);
+                    killGdbLocked();
+                }
+            }
             mState = state;
             System.out.println("");
             printMessageForState();
@@ -580,6 +657,8 @@
                 }
             }
 
+            killGdbLocked();
+
             return mResult;
         }
 
@@ -681,7 +760,19 @@
     }
 
     private void runMonitor() throws Exception {
-        MyActivityController controller = new MyActivityController();
+        String opt;
+        String gdbPort = null;
+        while ((opt=nextOption()) != null) {
+            if (opt.equals("--gdb")) {
+                gdbPort = nextArgRequired();
+            } else {
+                System.err.println("Error: Unknown option: " + opt);
+                showUsage();
+                return;
+            }
+        }
+
+        MyActivityController controller = new MyActivityController(gdbPort);
         controller.run();
     }
 
@@ -862,7 +953,8 @@
                 "    dump heap: am dumpheap [flags] <PROCESS> <FILE>\n" +
                 "        -n: dump native heap instead of managed heap\n" +
                 "\n" +
-                "    start monitoring: am monitor\n" +
+                "    start monitoring: am monitor [--gdb <port>]\n" +
+                "        --gdb: start gdbserv on the given port at crash/ANR\n" +
                 "\n" +
                 "    <INTENT> specifications include these flags:\n" +
                 "        [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index a09666e..67bd9f7 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -135,11 +135,11 @@
     run_command("PROCESSES AND THREADS", 10, "ps", "-t", "-p", "-P", NULL);
     run_command("LIBRANK", 10, "librank", NULL);
 
-    dump_file("BINDER FAILED TRANSACTION LOG", "/proc/binder/failed_transaction_log");
-    dump_file("BINDER TRANSACTION LOG", "/proc/binder/transaction_log");
-    dump_file("BINDER TRANSACTIONS", "/proc/binder/transactions");
-    dump_file("BINDER STATS", "/proc/binder/stats");
-    run_command("BINDER PROCESS STATE", 10, "sh", "-c", "cat /proc/binder/proc/*");
+    dump_file("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log");
+    dump_file("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log");
+    dump_file("BINDER TRANSACTIONS", "/sys/kernel/debug/binder/transactions");
+    dump_file("BINDER STATS", "/sys/kernel/debug/binder/stats");
+    dump_file("BINDER STATE", "/sys/kernel/debug/binder/state");
 
     run_command("FILESYSTEMS & FREE SPACE", 10, "df", NULL);
 
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index eb4b733..8b54871 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -31,6 +31,8 @@
 #include <media/IMediaPlayerService.h>
 #include <media/stagefright/foundation/ALooper.h>
 #include "include/ARTSPController.h"
+#include "include/LiveSource.h"
+#include "include/NuCachedSource2.h"
 #include <media/stagefright/AudioPlayer.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/JPEGSource.h>
@@ -285,19 +287,34 @@
             MediaBuffer **buffer, const ReadOptions *options);
 
 private:
+    enum StreamType {
+        AVC,
+        MPEG4,
+        H263,
+        OTHER,
+    };
+
     sp<MediaSource> mSource;
-    bool mIsAVC;
+    StreamType mStreamType;
 
     DISALLOW_EVIL_CONSTRUCTORS(DetectSyncSource);
 };
 
 DetectSyncSource::DetectSyncSource(const sp<MediaSource> &source)
     : mSource(source),
-      mIsAVC(false) {
+      mStreamType(OTHER) {
     const char *mime;
     CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
 
-    mIsAVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
+    if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+        mStreamType = AVC;
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)) {
+        mStreamType = MPEG4;
+        CHECK(!"sync frame detection not implemented yet for MPEG4");
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_H263)) {
+        mStreamType = H263;
+        CHECK(!"sync frame detection not implemented yet for H.263");
+    }
 }
 
 status_t DetectSyncSource::start(MetaData *params) {
@@ -336,10 +353,10 @@
         return err;
     }
 
-    if (mIsAVC && isIDRFrame(*buffer)) {
+    if (mStreamType == AVC && isIDRFrame(*buffer)) {
         (*buffer)->meta_data()->setInt32(kKeyIsSyncFrame, true);
     } else {
-        (*buffer)->meta_data()->setInt32(kKeyIsSyncFrame, false);
+        (*buffer)->meta_data()->setInt32(kKeyIsSyncFrame, true);
     }
 
     return OK;
@@ -347,19 +364,21 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-static void writeSourceToMP4(
-        sp<MediaSource> &source, bool syncInfoPresent) {
-    if (!syncInfoPresent) {
-        source = new DetectSyncSource(source);
-    }
-
+static void writeSourcesToMP4(
+        Vector<sp<MediaSource> > &sources, bool syncInfoPresent) {
     sp<MPEG4Writer> writer =
         new MPEG4Writer(gWriteMP4Filename.string());
 
     // at most one minute.
     writer->setMaxFileDuration(60000000ll);
 
-    CHECK_EQ(writer->addSource(source), (status_t)OK);
+    for (size_t i = 0; i < sources.size(); ++i) {
+        sp<MediaSource> source = sources.editItemAt(i);
+
+        CHECK_EQ(writer->addSource(
+                    syncInfoPresent ? source : new DetectSyncSource(source)),
+                (status_t)OK);
+    }
 
     sp<MetaData> params = new MetaData;
     params->setInt32(kKeyNotRealTime, true);
@@ -674,8 +693,10 @@
 
         sp<DataSource> dataSource = DataSource::CreateFromURI(filename);
 
-        if ((strncasecmp(filename, "sine:", 5)
-                && strncasecmp(filename, "rtsp://", 7)) && dataSource == NULL) {
+        if (strncasecmp(filename, "sine:", 5)
+                && strncasecmp(filename, "rtsp://", 7)
+                && strncasecmp(filename, "httplive://", 11)
+                && dataSource == NULL) {
             fprintf(stderr, "Unable to create data source.\n");
             return 1;
         }
@@ -687,10 +708,14 @@
             isJPEG = true;
         }
 
+        Vector<sp<MediaSource> > mediaSources;
         sp<MediaSource> mediaSource;
 
         if (isJPEG) {
             mediaSource = new JPEGSource(dataSource);
+            if (gWriteMP4) {
+                mediaSources.push(mediaSource);
+            }
         } else if (!strncasecmp("sine:", filename, 5)) {
             char *end;
             long sampleRate = strtol(filename + 5, &end, 10);
@@ -699,6 +724,9 @@
                 sampleRate = 44100;
             }
             mediaSource = new SineSource(sampleRate, 1);
+            if (gWriteMP4) {
+                mediaSources.push(mediaSource);
+            }
         } else {
             sp<MediaExtractor> extractor;
 
@@ -718,6 +746,18 @@
                 extractor = rtspController.get();
 
                 syncInfoPresent = false;
+            } else if (!strncasecmp("httplive://", filename, 11)) {
+                String8 uri("http://");
+                uri.append(filename + 11);
+
+                dataSource = new LiveSource(uri.string());
+                dataSource = new NuCachedSource2(dataSource);
+
+                extractor =
+                    MediaExtractor::Create(
+                            dataSource, MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
+
+                syncInfoPresent = false;
             } else {
                 extractor = MediaExtractor::Create(dataSource);
                 if (extractor == NULL) {
@@ -728,46 +768,75 @@
 
             size_t numTracks = extractor->countTracks();
 
-            sp<MetaData> meta;
-            size_t i;
-            for (i = 0; i < numTracks; ++i) {
-                meta = extractor->getTrackMetaData(
-                        i, MediaExtractor::kIncludeExtensiveMetaData);
+            if (gWriteMP4) {
+                bool haveAudio = false;
+                bool haveVideo = false;
+                for (size_t i = 0; i < numTracks; ++i) {
+                    sp<MediaSource> source = extractor->getTrack(i);
 
-                const char *mime;
-                meta->findCString(kKeyMIMEType, &mime);
+                    const char *mime;
+                    CHECK(source->getFormat()->findCString(
+                                kKeyMIMEType, &mime));
 
-                if (audioOnly && !strncasecmp(mime, "audio/", 6)) {
-                    break;
+                    bool useTrack = false;
+                    if (!haveAudio && !strncasecmp("audio/", mime, 6)) {
+                        haveAudio = true;
+                        useTrack = true;
+                    } else if (!haveVideo && !strncasecmp("video/", mime, 6)) {
+                        haveVideo = true;
+                        useTrack = true;
+                    }
+
+                    if (useTrack) {
+                        mediaSources.push(source);
+
+                        if (haveAudio && haveVideo) {
+                            break;
+                        }
+                    }
+                }
+            } else {
+                sp<MetaData> meta;
+                size_t i;
+                for (i = 0; i < numTracks; ++i) {
+                    meta = extractor->getTrackMetaData(
+                            i, MediaExtractor::kIncludeExtensiveMetaData);
+
+                    const char *mime;
+                    meta->findCString(kKeyMIMEType, &mime);
+
+                    if (audioOnly && !strncasecmp(mime, "audio/", 6)) {
+                        break;
+                    }
+
+                    if (!audioOnly && !strncasecmp(mime, "video/", 6)) {
+                        break;
+                    }
+
+                    meta = NULL;
                 }
 
-                if (!audioOnly && !strncasecmp(mime, "video/", 6)) {
-                    break;
+                if (meta == NULL) {
+                    fprintf(stderr,
+                            "No suitable %s track found. The '-a' option will "
+                            "target audio tracks only, the default is to target "
+                            "video tracks only.\n",
+                            audioOnly ? "audio" : "video");
+                    return -1;
                 }
 
-                meta = NULL;
-            }
+                int64_t thumbTimeUs;
+                if (meta->findInt64(kKeyThumbnailTime, &thumbTimeUs)) {
+                    printf("thumbnailTime: %lld us (%.2f secs)\n",
+                           thumbTimeUs, thumbTimeUs / 1E6);
+                }
 
-            if (meta == NULL) {
-                fprintf(stderr,
-                        "No suitable %s track found. The '-a' option will "
-                        "target audio tracks only, the default is to target "
-                        "video tracks only.\n",
-                        audioOnly ? "audio" : "video");
-                return -1;
+                mediaSource = extractor->getTrack(i);
             }
-
-            int64_t thumbTimeUs;
-            if (meta->findInt64(kKeyThumbnailTime, &thumbTimeUs)) {
-                printf("thumbnailTime: %lld us (%.2f secs)\n",
-                       thumbTimeUs, thumbTimeUs / 1E6);
-            }
-
-            mediaSource = extractor->getTrack(i);
         }
 
         if (gWriteMP4) {
-            writeSourceToMP4(mediaSource, syncInfoPresent);
+            writeSourcesToMP4(mediaSources, syncInfoPresent);
         } else if (seekTest) {
             performSeekTest(mediaSource);
         } else {
diff --git a/core/java/android/net/DownloadManager.java b/core/java/android/net/DownloadManager.java
index 12c05cc..3279e8f 100644
--- a/core/java/android/net/DownloadManager.java
+++ b/core/java/android/net/DownloadManager.java
@@ -214,6 +214,11 @@
             "android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED";
 
     /**
+     * Intent action to launch an activity to display all downloads.
+     */
+    public final static String ACTION_VIEW_DOWNLOADS = "android.intent.action.VIEW_DOWNLOADS";
+
+    /**
      * Intent extra included with {@link #ACTION_DOWNLOAD_COMPLETE} intents, indicating the ID (as a
      * long) of the download that just completed.
      */
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index 97c31fa..8afdcee 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -41,6 +41,12 @@
      * -------------------------------------------------------------
      */
 
+    // Share the event space with ConnectivityService (which we can't see, but
+    // must send events to).  If you change these, change ConnectivityService
+    // too.
+    static final int MIN_NETWORK_STATE_TRACKER_EVENT = 1;
+    static final int MAX_NETWORK_STATE_TRACKER_EVENT = 100;
+
     /**
      * The network state has changed and the NetworkInfo object
      * contains the new state.
@@ -63,26 +69,6 @@
     public static final int EVENT_RESTORE_DEFAULT_NETWORK = 6;
 
     /**
-     * USED by ConnectivityService only
-     *
-     * msg.what = EVENT_CLEAR_NET_TRANSITION_WAKELOCK
-     * msg.arg1 = mNetTransitionWakeLockSerialNumber
-     */
-    public static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK = 7;
-
-    /**
-     * msg.arg1 = network type
-     * msg.arg2 = condition (0 bad, 100 good)
-     */
-    public static final int EVENT_INET_CONDITION_CHANGE = 8;
-
-    /**
-     * msg.arg1 = network type
-     * msg.arg2 = default connection sequence number
-     */
-    public static final int EVENT_INET_CONDITION_HOLD_END = 9;
-
-    /**
      * -------------------------------------------------------------
      * Control Interface
      * -------------------------------------------------------------
diff --git a/core/java/android/provider/AlarmClock.java b/core/java/android/provider/AlarmClock.java
new file mode 100644
index 0000000..b93dfd8
--- /dev/null
+++ b/core/java/android/provider/AlarmClock.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      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.provider;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+
+/**
+ * The AlarmClock provider contains an Intent action and extras that can be used
+ * to start an Activity to set a new alarm in an alarm clock application.
+ *
+ * Applications that wish to receive the ACTION_SET_ALARM Intent should create
+ * an activity to handle the Intent that requires the permission
+ * com.android.alarm.permission.SET_ALARM.  Applications that wish to create a
+ * new alarm should use
+ * {@link android.content.Context#startActivity Context.startActivity()} so that
+ * the user has the option of choosing which alarm clock application to use.
+ */
+public final class AlarmClock {
+    /**
+     * Activity Action: Set an alarm.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_SET_ALARM = "android.intent.action.SET_ALARM";
+
+    /**
+     * Activity Extra: Provide a custom message for the alarm.
+     * <p>
+     * This can be passed as an extra field in the Intent created with
+     * ACTION_SET_ALARM.
+     */
+    public static final String EXTRA_MESSAGE = "android.intent.extra.alarm.MESSAGE";
+
+    /**
+     * Activity Extra: The hour of the alarm being set.
+     * <p>
+     * This value can be passed as an extra field to the Intent created with
+     * ACTION_SET_ALARM.  If it is not provided, the behavior is undefined and
+     * is up to the application.  The value is an integer and ranges from 0 to
+     * 23.
+     */
+    public static final String EXTRA_HOUR = "android.intent.extra.alarm.HOUR";
+
+    /**
+     * Activity Extra: The minutes of the alarm being set.
+     * <p>
+     * This value can be passed as an extra field to the Intent created with
+     * ACTION_SET_ALARM.  If it is not provided, the behavior is undefined and
+     * is up to the application.  The value is an integer and ranges from 0 to
+     * 59.
+     */
+    public static final String EXTRA_MINUTES = "android.intent.extra.alarm.MINUTES";
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3d5aff7..96066b7 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4176,6 +4176,7 @@
                 imm.focusOut(this);
             }
             removeLongPressCallback();
+            removeTapCallback();
             onFocusLost();
         } else if (imm != null && (mPrivateFlags & FOCUSED) != 0) {
             imm.focusIn(this);
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 6917bca..f6a06ce 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -2459,6 +2459,7 @@
             if (DBG) Log.d(TAG, "WindowLayout in layoutWindow:" + params);
         }
         mPendingConfiguration.seq = 0;
+        //Log.d(TAG, ">>>>>> CALLING relayout");
         int relayoutResult = sWindowSession.relayout(
                 mWindow, params,
                 (int) (mView.mMeasuredWidth * appScale + 0.5f),
@@ -2466,6 +2467,7 @@
                 viewVisibility, insetsPending, mWinFrame,
                 mPendingContentInsets, mPendingVisibleInsets,
                 mPendingConfiguration, mSurface);
+        //Log.d(TAG, "<<<<<< BACK FROM relayout");
         if (restore) {
             params.restore();
         }
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index bb13f1d..c694ff1 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -4367,9 +4367,13 @@
     public void setRemoteViewsAdapter(Intent intent) {
         // Ensure that we don't already have a RemoteViewsAdapter that is bound to an existing
         // service handling the specified intent.
-        Intent.FilterComparison fc = new Intent.FilterComparison(intent);
-        if (mRemoteAdapter != null && fc.equals(mRemoteAdapter.getRemoteViewsServiceIntent())) {
-            return;
+        if (mRemoteAdapter != null) {
+            Intent.FilterComparison fcNew = new Intent.FilterComparison(intent);
+            Intent.FilterComparison fcOld = new Intent.FilterComparison(
+                    mRemoteAdapter.getRemoteViewsServiceIntent());
+            if (fcNew.equals(fcOld)) {
+                return;
+            }
         }
 
         // Otherwise, create a new RemoteViewsAdapter for binding
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index c08adb2..f245933 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -389,7 +389,7 @@
         int newWindowStartUnbounded = childIndex - mActiveOffset;
         int newWindowEndUnbounded = newWindowStartUnbounded + mNumActiveViews - 1;
         int newWindowStart = Math.max(0, newWindowStartUnbounded);
-        int newWindowEnd = Math.min(mAdapter.getCount(), newWindowEndUnbounded);
+        int newWindowEnd = Math.min(mAdapter.getCount() - 1, newWindowEndUnbounded);
 
         // This section clears out any items that are in our mActiveViews list
         // but are outside the effective bounds of our window (this is becomes an issue
@@ -592,18 +592,18 @@
          */
         private SavedState(Parcel in) {
             super(in);
-            whichChild = in.readInt();
+            this.whichChild = in.readInt();
         }
 
         @Override
         public void writeToParcel(Parcel out, int flags) {
             super.writeToParcel(out, flags);
-            out.writeInt(whichChild);
+            out.writeInt(this.whichChild);
         }
 
         @Override
         public String toString() {
-            return "AdapterViewAnimator.SavedState{ whichChild = " + whichChild + " }";
+            return "AdapterViewAnimator.SavedState{ whichChild = " + this.whichChild + " }";
         }
 
         public static final Parcelable.Creator<SavedState> CREATOR
@@ -781,10 +781,13 @@
     public void setRemoteViewsAdapter(Intent intent) {
         // Ensure that we don't already have a RemoteViewsAdapter that is bound to an existing
         // service handling the specified intent.
-        Intent.FilterComparison fc = new Intent.FilterComparison(intent);
-        if (mRemoteViewsAdapter != null &&
-                fc.equals(mRemoteViewsAdapter.getRemoteViewsServiceIntent())) {
-            return;
+        if (mRemoteViewsAdapter != null) {
+            Intent.FilterComparison fcNew = new Intent.FilterComparison(intent);
+            Intent.FilterComparison fcOld = new Intent.FilterComparison(
+                    mRemoteViewsAdapter.getRemoteViewsServiceIntent());
+            if (fcNew.equals(fcOld)) {
+                return;
+            }
         }
 
         // Otherwise, create a new RemoteViewsAdapter for binding
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 945ffeb..9d214fc 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -74,6 +74,11 @@
      */
     private ArrayList<Action> mActions;
     
+    /**
+     * A class to keep track of memory usage by this RemoteViews
+     */
+    private MemoryUsageCounter mMemoryUsageCounter;
+
     
     /**
      * This flag indicates whether this RemoteViews object is being created from a
@@ -118,6 +123,15 @@
         public int describeContents() {
             return 0;
         }
+
+        /**
+         * Overridden by each class to report on it's own memory usage
+         */
+        public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
+            // We currently only calculate Bitmap memory usage, so by default, don't do anything
+            // here
+            return;
+        }
     }
 
     private class SetEmptyView extends Action {
@@ -525,7 +539,7 @@
                 }
             }
         }
-        
+
         int viewId;
         boolean targetBackground;
         int alpha;
@@ -817,6 +831,35 @@
                 throw new ActionException(ex);
             }
         }
+
+        @Override
+        public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
+            // We currently only calculate Bitmap memory usage
+            switch (this.type) {
+                case BITMAP:
+                    if (this.value != null) {
+                        final Bitmap b = (Bitmap) this.value;
+                        final Bitmap.Config c = b.getConfig();
+                        int bpp = 4;
+                        switch (c) {
+                        case ALPHA_8:
+                            bpp = 1;
+                            break;
+                        case RGB_565:
+                        case ARGB_4444:
+                            bpp = 2;
+                            break;
+                        case ARGB_8888:
+                            bpp = 4;
+                            break;
+                        }
+                        counter.bitmapIncrement(b.getWidth() * b.getHeight() * bpp);
+                    }
+                    break;
+                default:
+                    break;
+            }
+        }
     }
 
     /**
@@ -854,6 +897,13 @@
             }
         }
 
+        @Override
+        public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
+            if (nestedViews != null) {
+                counter.bitmapIncrement(nestedViews.estimateBitmapMemoryUsage());
+            }
+        }
+
         int viewId;
         RemoteViews nestedViews;
 
@@ -861,6 +911,26 @@
     }
 
     /**
+     * Simple class used to keep track of memory usage in a RemoteViews.
+     *
+     */
+    private class MemoryUsageCounter {
+        public void clear() {
+            mBitmapHeapMemoryUsage = 0;
+        }
+
+        public void bitmapIncrement(int numBytes) {
+            mBitmapHeapMemoryUsage += numBytes;
+        }
+
+        public int getBitmapHeapMemoryUsage() {
+            return mBitmapHeapMemoryUsage;
+        }
+
+        int mBitmapHeapMemoryUsage;
+    }
+
+    /**
      * Create a new RemoteViews object that will display the views contained
      * in the specified layout file.
      * 
@@ -870,6 +940,10 @@
     public RemoteViews(String packageName, int layoutId) {
         mPackage = packageName;
         mLayoutId = layoutId;
+
+        // setup the memory usage statistics
+        mMemoryUsageCounter = new MemoryUsageCounter();
+        recalculateMemoryUsage();
     }
 
     /**
@@ -920,6 +994,10 @@
                 }
             }
         }
+
+        // setup the memory usage statistics
+        mMemoryUsageCounter = new MemoryUsageCounter();
+        recalculateMemoryUsage();
     }
 
     @Override
@@ -928,6 +1006,9 @@
         if (mActions != null) {
             that.mActions = (ArrayList<Action>)mActions.clone();
         }
+
+        // update the memory usage stats of the cloned RemoteViews
+        that.recalculateMemoryUsage();
         return that;
     }
 
@@ -939,7 +1020,7 @@
         return mLayoutId;
     }
 
-    /**
+    /*
      * This flag indicates whether this RemoteViews object is being created from a
      * RemoteViewsService for use as a child of a widget collection. This flag is used
      * to determine whether or not certain features are available, in particular,
@@ -951,6 +1032,28 @@
     }
 
     /**
+     * Updates the memory usage statistics.
+     */
+    private void recalculateMemoryUsage() {
+        mMemoryUsageCounter.clear();
+
+        // Accumulate the memory usage for each action
+        if (mActions != null) {
+            final int count = mActions.size();
+            for (int i= 0; i < count; ++i) {
+                mActions.get(i).updateMemoryUsageEstimate(mMemoryUsageCounter);
+            }
+        }
+    }
+
+    /**
+     * Returns an estimate of the bitmap heap memory usage for this RemoteViews.
+     */
+    int estimateBitmapMemoryUsage() {
+        return mMemoryUsageCounter.getBitmapHeapMemoryUsage();
+    }
+
+    /**
      * Add an action to be executed on the remote side when apply is called.
      * 
      * @param a The action to add
@@ -960,6 +1063,9 @@
             mActions = new ArrayList<Action>();
         }
         mActions.add(a);
+
+        // update the memory usage stats
+        a.updateMemoryUsageEstimate(mMemoryUsageCounter);
     }
 
     /**
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 91b4afa..afb56fc 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -16,7 +16,9 @@
 
 package android.widget;
 
+import java.lang.ref.WeakReference;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.Map;
 
@@ -32,8 +34,8 @@
 import android.util.Log;
 import android.view.Gravity;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
 
 import com.android.internal.widget.IRemoteViewsFactory;
 
@@ -48,15 +50,17 @@
     private Context mContext;
     private Intent mIntent;
     private RemoteViewsAdapterServiceConnection mServiceConnection;
-    private RemoteViewsCache mViewCache;
+    private WeakReference<RemoteAdapterConnectionCallback> mCallback;
+    private FixedSizeRemoteViewsCache mCache;
+
+    // The set of requested views that are to be notified when the associated RemoteViews are
+    // loaded.
+    private RemoteViewsFrameLayoutRefSet mRequestedViews;
 
     private HandlerThread mWorkerThread;
     // items may be interrupted within the normally processed queues
     private Handler mWorkerQueue;
     private Handler mMainQueue;
-    // items are never dequeued from the priority queue and must run
-    private Handler mWorkerPriorityQueue;
-    private Handler mMainPriorityQueue;
 
     /**
      * An interface for the RemoteAdapter to notify other classes when adapters
@@ -70,60 +74,89 @@
 
     /**
      * The service connection that gets populated when the RemoteViewsService is
-     * bound.
+     * bound.  This must be a static inner class to ensure that no references to the outer
+     * RemoteViewsAdapter instance is retained (this would prevent the RemoteViewsAdapter from being
+     * garbage collected, and would cause us to leak activities due to the caching mechanism for
+     * FrameLayouts in the adapter).
      */
-    private class RemoteViewsAdapterServiceConnection implements ServiceConnection {
+    private static class RemoteViewsAdapterServiceConnection implements ServiceConnection {
         private boolean mConnected;
+        private WeakReference<RemoteViewsAdapter> mAdapter;
         private IRemoteViewsFactory mRemoteViewsFactory;
-        private RemoteAdapterConnectionCallback mCallback;
 
-        public RemoteViewsAdapterServiceConnection(RemoteAdapterConnectionCallback callback) {
-            mCallback = callback;
+        public RemoteViewsAdapterServiceConnection(RemoteViewsAdapter adapter) {
+            mAdapter = new WeakReference<RemoteViewsAdapter>(adapter);
         }
 
-        public void onServiceConnected(ComponentName name, IBinder service) {
+        public void onServiceConnected(ComponentName name,
+                IBinder service) {
             mRemoteViewsFactory = IRemoteViewsFactory.Stub.asInterface(service);
             mConnected = true;
 
-            // notifyDataSetChanged should be called first, to ensure that the
-            // views are not updated twice
-            notifyDataSetChanged();
-
-            // post a new runnable to load the appropriate data, then callback
-            mWorkerPriorityQueue.post(new Runnable() {
+            // Queue up work that we need to do for the callback to run
+            final RemoteViewsAdapter adapter = mAdapter.get();
+            if (adapter == null) return;
+            adapter.mWorkerQueue.post(new Runnable() {
                 @Override
                 public void run() {
-                    // we need to get the viewTypeCount specifically, so just get all the
-                    // metadata
-                    mViewCache.requestMetaData();
+                    // Call back to the service to notify that the data set changed
+                    if (adapter.mServiceConnection.isConnected()) {
+                        IRemoteViewsFactory factory =
+                            adapter.mServiceConnection.getRemoteViewsFactory();
+                        try {
+                            // call back to the factory
+                            factory.onDataSetChanged();
+                        } catch (Exception e) {
+                            Log.e(TAG, "Error notifying factory of data set changed in " +
+                                        "onServiceConnected(): " + e.getMessage());
+                            e.printStackTrace();
 
-                    // post a runnable to call the callback on the main thread
-                    mMainPriorityQueue.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            if (mCallback != null)
-                                mCallback.onRemoteAdapterConnected();
+                            // Return early to prevent anything further from being notified
+                            // (effectively nothing has changed)
+                            return;
                         }
-                    });
+
+                        // Request meta data so that we have up to date data when calling back to
+                        // the remote adapter callback
+                        adapter.updateMetaData();
+
+                        // Post a runnable to call back to the view to notify it that we have
+                        // connected
+                        adapter. mMainQueue.post(new Runnable() {
+                            @Override
+                            public void run() {
+                                final RemoteAdapterConnectionCallback callback =
+                                    adapter.mCallback.get();
+                                if (callback != null) {
+                                    callback.onRemoteAdapterConnected();
+                                }
+                            }
+                        });
+                    }
                 }
             });
-
-            // start the background loader
-            mViewCache.startBackgroundLoader();
         }
 
         public void onServiceDisconnected(ComponentName name) {
-            mRemoteViewsFactory = null;
             mConnected = false;
+            mRemoteViewsFactory = null;
 
-            // clear the main/worker queues
-            mMainQueue.removeMessages(0);
+            final RemoteViewsAdapter adapter = mAdapter.get();
+            if (adapter == null) return;
             
-            // stop the background loader
-            mViewCache.stopBackgroundLoader();
+            // Clear the main/worker queues
+            adapter.mMainQueue.removeMessages(0);
+            adapter.mWorkerQueue.removeMessages(0);
 
-            if (mCallback != null)
-                mCallback.onRemoteAdapterDisconnected();
+            // Clear the cache
+            synchronized (adapter.mCache) {
+                adapter.mCache.reset();
+            }
+
+            final RemoteAdapterConnectionCallback callback = adapter.mCallback.get();
+            if (callback != null) {
+                callback.onRemoteAdapterDisconnected();
+            }
         }
 
         public IRemoteViewsFactory getRemoteViewsFactory() {
@@ -136,532 +169,400 @@
     }
 
     /**
-     * An internal cache of remote views.
+     * A FrameLayout which contains a loading view, and manages the re/applying of RemoteViews when
+     * they are loaded.
      */
-    private class RemoteViewsCache {
-        private static final String TAG = "RemoteViewsCache";
-
-        private RemoteViewsInfo mViewCacheInfo;
-        private RemoteViewsIndexInfo[] mViewCache;
-        private int[] mTmpViewCacheLoadIndices;
-        private LinkedList<Integer> mViewCacheLoadIndices;
-        private boolean mBackgroundLoaderEnabled;
-
-        // if a user loading view is not provided, then we create a temporary one
-        // for the user using the height of the first view
-        private RemoteViews mUserLoadingView;
-        private RemoteViews mFirstView;
-        private int mFirstViewHeight;
-
-        // determines when the current cache window needs to be updated with new
-        // items (ie. when there is not enough slack)
-        private int mViewCacheStartPosition;
-        private int mViewCacheEndPosition;
-        private int mHalfCacheSize;
-        private int mCacheSlack;
-        private final float mCacheSlackPercentage = 0.75f;
-
-        /**
-         * The data structure stored at each index of the cache. Any member 
-         * that is not invalidated persists throughout the lifetime of the cache.
-         */
-        private class RemoteViewsIndexInfo {
-            FrameLayout flipper;
-            RemoteViews view;
-            long itemId;
-            int typeId;
-
-            RemoteViewsIndexInfo() {
-                invalidate();
-            }
-
-            void set(RemoteViews v, long id) {
-                view = v;
-                itemId = id;
-                if (v != null)
-                    typeId = v.getLayoutId();
-                else
-                    typeId = 0;
-            }
-
-            void invalidate() {
-                view = null;
-                itemId = 0;
-                typeId = 0;
-            }
-
-            final boolean isValid() {
-                return (view != null);
-            }
+    private class RemoteViewsFrameLayout extends FrameLayout {
+        public RemoteViewsFrameLayout(Context context) {
+            super(context);
         }
 
         /**
-         * Remote adapter metadata. Useful for when we have to lock on something
-         * before updating the metadata.
+         * Updates this RemoteViewsFrameLayout depending on the view that was loaded.
+         * @param view the RemoteViews that was loaded. If null, the RemoteViews was not loaded
+         *             successfully.
          */
-        private class RemoteViewsInfo {
-            int count;
-            int viewTypeCount;
-            boolean hasStableIds;
-            boolean isDataDirty;
-            Map<Integer, Integer> mTypeIdIndexMap;
+        public void onRemoteViewsLoaded(RemoteViews view) {
+            // Remove all the children of this layout first
+            removeAllViews();
+            addView(view.apply(getContext(), this));
+        }
+    }
 
-            RemoteViewsInfo() {
-                count = 0;
-                // by default there is at least one dummy view type
-                viewTypeCount = 1;
-                hasStableIds = true;
-                isDataDirty = false;
-                mTypeIdIndexMap = new HashMap<Integer, Integer>();
+    /**
+     * Stores the references of all the RemoteViewsFrameLayouts that have been returned by the
+     * adapter that have not yet had their RemoteViews loaded.
+     */
+    private class RemoteViewsFrameLayoutRefSet {
+        private HashMap<Integer, LinkedList<RemoteViewsFrameLayout>> mReferences;
+
+        public RemoteViewsFrameLayoutRefSet() {
+            mReferences = new HashMap<Integer, LinkedList<RemoteViewsFrameLayout>>();
+        }
+
+        /**
+         * Adds a new reference to a RemoteViewsFrameLayout returned by the adapter.
+         */
+        public void add(int position, RemoteViewsFrameLayout layout) {
+            final Integer pos = position;
+            LinkedList<RemoteViewsFrameLayout> refs;
+
+            // Create the list if necessary
+            if (mReferences.containsKey(pos)) {
+                refs = mReferences.get(pos);
+            } else {
+                refs = new LinkedList<RemoteViewsFrameLayout>();
+                mReferences.put(pos, refs);
             }
+
+            // Add the references to the list
+            refs.add(layout);
         }
 
-        public RemoteViewsCache(int halfCacheSize) {
-            mHalfCacheSize = halfCacheSize;
-            mCacheSlack = Math.round(mCacheSlackPercentage * mHalfCacheSize);
-            mViewCacheStartPosition = 0;
-            mViewCacheEndPosition = -1;
-            mBackgroundLoaderEnabled = false;
-
-            // initialize the cache
-            int cacheSize = 2 * mHalfCacheSize + 1;
-            mViewCacheInfo = new RemoteViewsInfo();
-            mViewCache = new RemoteViewsIndexInfo[cacheSize];
-            for (int i = 0; i < mViewCache.length; ++i) {
-                mViewCache[i] = new RemoteViewsIndexInfo();
-            }
-            mTmpViewCacheLoadIndices = new int[cacheSize];
-            mViewCacheLoadIndices = new LinkedList<Integer>();
-        }
-
-        private final boolean contains(int position) {
-            return (mViewCacheStartPosition <= position) && (position <= mViewCacheEndPosition);
-        }
-
-        private final boolean containsAndIsValid(int position) {
-            if (contains(position)) {
-                RemoteViewsIndexInfo indexInfo = mViewCache[getCacheIndex(position)];
-                if (indexInfo.isValid()) {
-                    return true;
+        /**
+         * Notifies each of the RemoteViewsFrameLayouts associated with a particular position that
+         * the associated RemoteViews has loaded.
+         */
+        public void notifyOnRemoteViewsLoaded(int position, RemoteViews view, int typeId) {
+            final Integer pos = position;
+            if (mReferences.containsKey(pos)) {
+                // Notify all the references for that position of the newly loaded RemoteViews
+                final LinkedList<RemoteViewsFrameLayout> refs = mReferences.get(pos);
+                for (final RemoteViewsFrameLayout ref : refs) {
+                    ref.onRemoteViewsLoaded(view);
                 }
+                refs.clear();
+
+                // Remove this set from the original mapping
+                mReferences.remove(pos);
             }
-            return false;
         }
 
-        private final int getCacheIndex(int position) {
-            // take the modulo of the position
-            final int cacheSize = mViewCache.length;
-            return (cacheSize + (position % cacheSize)) % cacheSize;
+        /**
+         * Removes all references to all RemoteViewsFrameLayouts returned by the adapter.
+         */
+        public void clear() {
+            // We currently just clear the references, and leave all the previous layouts returned
+            // in their default state of the loading view.
+            mReferences.clear();
+        }
+    }
+
+    /**
+     * The meta-data associated with the cache in it's current state.
+     */
+    private class RemoteViewsMetaData {
+        int count;
+        int viewTypeCount;
+        boolean hasStableIds;
+        boolean isDataDirty;
+
+        // Used to determine how to construct loading views.  If a loading view is not specified
+        // by the user, then we try and load the first view, and use its height as the height for
+        // the default loading view.
+        RemoteViews mUserLoadingView;
+        RemoteViews mFirstView;
+        int mFirstViewHeight;
+
+        // A mapping from type id to a set of unique type ids
+        private Map<Integer, Integer> mTypeIdIndexMap;
+
+        public RemoteViewsMetaData() {
+            reset();
         }
 
-        public void requestMetaData() {
-            if (mServiceConnection.isConnected()) {
-                try {
-                    IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
+        public void reset() {
+            count = 0;
+            // by default there is at least one dummy view type
+            viewTypeCount = 1;
+            hasStableIds = true;
+            isDataDirty = false;
+            mUserLoadingView = null;
+            mFirstView = null;
+            mFirstViewHeight = 0;
+            mTypeIdIndexMap = new HashMap<Integer, Integer>();
+        }
 
-                    // get the properties/first view (so that we can use it to
-                    // measure our dummy views)
-                    boolean hasStableIds = factory.hasStableIds();
-                    int viewTypeCount = factory.getViewTypeCount();
-                    int count = factory.getCount();
-                    RemoteViews loadingView = factory.getLoadingView();
-                    RemoteViews firstView = null;
-                    if ((count > 0) && (loadingView == null)) {
-                        firstView = factory.getViewAt(0);
-                    }
-                    synchronized (mViewCacheInfo) {
-                        RemoteViewsInfo info = mViewCacheInfo;
-                        info.hasStableIds = hasStableIds;
-                        info.viewTypeCount = viewTypeCount + 1;
-                        info.count = count;
-                        mUserLoadingView = loadingView;
-                        if (firstView != null) {
-                            mFirstView = firstView;
-                            mFirstViewHeight = -1;
-                        }
-                    }
-                } catch (Exception e) {
-                    // print the error
-                    Log.e(TAG, "Error in requestMetaData(): " + e.getMessage());
+        public void setLoadingViewTemplates(RemoteViews loadingView, RemoteViews firstView) {
+            mUserLoadingView = loadingView;
+            if (firstView != null) {
+                mFirstView = firstView;
+                mFirstViewHeight = -1;
+            }
+        }
 
-                    // reset any members after the failed call
-                    synchronized (mViewCacheInfo) {
-                        RemoteViewsInfo info = mViewCacheInfo;
-                        info.hasStableIds = false;
-                        info.viewTypeCount = 1;
-                        info.count = 0;
-                        mUserLoadingView = null;
+        public int getMappedViewType(int typeId) {
+            if (mTypeIdIndexMap.containsKey(typeId)) {
+                return mTypeIdIndexMap.get(typeId);
+            } else {
+                // We +1 because the loading view always has view type id of 0
+                int incrementalTypeId = mTypeIdIndexMap.size() + 1;
+                mTypeIdIndexMap.put(typeId, incrementalTypeId);
+                return incrementalTypeId;
+            }
+        }
+
+        private RemoteViewsFrameLayout createLoadingView(int position, View convertView,
+                ViewGroup parent) {
+            // Create and return a new FrameLayout, and setup the references for this position
+            final Context context = parent.getContext();
+            RemoteViewsFrameLayout layout = new RemoteViewsFrameLayout(context);
+
+            // Create a new loading view
+            synchronized (mCache) {
+                if (mUserLoadingView != null) {
+                    // A user-specified loading view
+                    View loadingView = mUserLoadingView.apply(parent.getContext(), parent);
+                    loadingView.setTag(new Integer(0));
+                    layout.addView(loadingView);
+                } else {
+                    // A default loading view
+                    // Use the size of the first row as a guide for the size of the loading view
+                    if (mFirstViewHeight < 0) {
+                        View firstView = mFirstView.apply(parent.getContext(), parent);
+                        firstView.measure(
+                                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+                                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+                        mFirstViewHeight = firstView.getMeasuredHeight();
                         mFirstView = null;
-                        mFirstViewHeight = -1;
-                    }
-                }
-            }
-        }
-
-        protected void onNotifyDataSetChanged() {
-            // we mark the data as dirty so that the next call to fetch views will result in
-            // an onDataSetDirty() call from the adapter
-            synchronized (mViewCacheInfo) {
-                mViewCacheInfo.isDataDirty = true;
-            }
-        }
-
-        private void updateNotifyDataSetChanged() {
-            // actually calls through to the factory to notify it to update
-            if (mServiceConnection.isConnected()) {
-                IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
-                try {
-                    // call back to the factory
-                    factory.onDataSetChanged();
-                } catch (Exception e) {
-                    // print the error
-                    Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage());
-
-                    // return early to prevent container from being notified (nothing has changed)
-                    return;
-                }
-            }
-
-            // re-request the new metadata (only after the notification to the factory)
-            requestMetaData();
-
-            // post a new runnable on the main thread to propagate the notification back
-            // to the base adapter
-            mMainQueue.post(new Runnable() {
-                @Override
-                public void run() {
-                   completeNotifyDataSetChanged();
-                }
-            });
-        }
-
-        protected void updateRemoteViewsInfo(int position) {
-            if (mServiceConnection.isConnected()) {
-                IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
-
-                // load the item information
-                RemoteViews remoteView = null;
-                long itemId = 0;
-                try {
-                    remoteView = factory.getViewAt(position);
-                    itemId = factory.getItemId(position);
-                } catch (Exception e) {
-                    // print the error
-                    Log.e(TAG, "Error in updateRemoteViewsInfo(" + position + "): " +
-                            e.getMessage());
-                    e.printStackTrace();
-
-                    // return early to prevent additional work in re-centering the view cache, and
-                    // swapping from the loading view
-                    return;
-                }
-
-                synchronized (mViewCache) {
-                    // skip if the window has moved
-                    if (position < mViewCacheStartPosition || position > mViewCacheEndPosition)
-                        return;
-
-                    final int positionIndex = position;
-                    final int cacheIndex = getCacheIndex(position);
-                    mViewCache[cacheIndex].set(remoteView, itemId);
-
-                    // notify the main thread when done loading
-                    // flush pending updates
-                    mMainQueue.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            // swap the loader view for this view
-                            synchronized (mViewCache) {
-                                if (containsAndIsValid(positionIndex)) {
-                                    RemoteViewsIndexInfo indexInfo = mViewCache[cacheIndex];
-                                    FrameLayout flipper = indexInfo.flipper;
-
-                                    // update the flipper
-                                    flipper.getChildAt(0).setVisibility(View.GONE);
-                                    boolean addNewView = true;
-                                    if (flipper.getChildCount() > 1) {
-                                        View v = flipper.getChildAt(1);
-                                        int typeId = ((Integer) v.getTag()).intValue();
-                                        if (typeId == indexInfo.typeId) {
-                                            // we can reapply since it is the same type
-                                            indexInfo.view.reapply(mContext, v);
-                                            v.setVisibility(View.VISIBLE);
-                                            if (v.getAnimation() != null) 
-                                                v.buildDrawingCache();
-                                            addNewView = false;
-                                        } else {
-                                            flipper.removeViewAt(1);
-                                        }
-                                    }
-                                    if (addNewView) {
-                                        View v = indexInfo.view.apply(mContext, flipper);
-                                        v.setTag(new Integer(indexInfo.typeId));
-                                        flipper.addView(v);
-                                    }
-                                }
-                            }
-                        }
-                    });
-                }
-            }
-        }
-
-        private RemoteViewsIndexInfo requestCachedIndexInfo(final int position) {
-            int indicesToLoadCount = 0;
-
-            synchronized (mViewCache) {
-                if (containsAndIsValid(position)) {
-                    // return the info if it exists in the window and is loaded
-                    return mViewCache[getCacheIndex(position)];
-                }
-
-                // if necessary update the window and load the new information
-                int centerPosition = (mViewCacheEndPosition + mViewCacheStartPosition) / 2;
-                if ((mViewCacheEndPosition <= mViewCacheStartPosition) || (Math.abs(position - centerPosition) > mCacheSlack)) {
-                    int newStartPosition = position - mHalfCacheSize;
-                    int newEndPosition = position + mHalfCacheSize;
-                    int frameSize = mHalfCacheSize / 4;
-                    int frameCount = (int) Math.ceil(mViewCache.length / (float) frameSize);
-
-                    // prune/add before the current start position
-                    int effectiveStart = Math.max(newStartPosition, 0);
-                    int effectiveEnd = Math.min(newEndPosition, getCount() - 1);
-
-                    // invalidate items in the queue
-                    int overlapStart = Math.max(mViewCacheStartPosition, effectiveStart);
-                    int overlapEnd = Math.min(Math.max(mViewCacheStartPosition, mViewCacheEndPosition), effectiveEnd);
-                    for (int i = 0; i < (frameSize * frameCount); ++i) {
-                        int index = newStartPosition + ((i % frameSize) * frameCount + (i / frameSize));
-                        
-                        if (index <= newEndPosition) {
-                            if ((overlapStart <= index) && (index <= overlapEnd)) {
-                                // load the stuff in the middle that has not already
-                                // been loaded
-                                if (!mViewCache[getCacheIndex(index)].isValid()) {
-                                    mTmpViewCacheLoadIndices[indicesToLoadCount++] = index;
-                                }
-                            } else if ((effectiveStart <= index) && (index <= effectiveEnd)) {
-                                // invalidate and load all new effective items
-                                mViewCache[getCacheIndex(index)].invalidate();
-                                mTmpViewCacheLoadIndices[indicesToLoadCount++] = index;
-                            } else {
-                                // invalidate all other cache indices (outside the effective start/end)
-                                // but don't load
-                                mViewCache[getCacheIndex(index)].invalidate();
-                            }
-                        }
                     }
 
-                    mViewCacheStartPosition = newStartPosition;
-                    mViewCacheEndPosition = newEndPosition;
+                    // Compose the loading view text
+                    TextView textView = new TextView(parent.getContext());
+                    textView.setText(com.android.internal.R.string.loading);
+                    textView.setHeight(mFirstViewHeight);
+                    textView.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);
+                    textView.setTextSize(18.0f);
+                    textView.setTextColor(Color.argb(96, 255, 255, 255));
+                    textView.setShadowLayer(2.0f, 0.0f, 1.0f, Color.BLACK);
+                    textView.setTag(new Integer(0));
+
+                    layout.addView(textView);
                 }
             }
 
-            // post items to be loaded
-            int length = 0;
-            synchronized (mViewCacheInfo) {
-                length = mViewCacheInfo.count;
-            }
-            if (indicesToLoadCount > 0) {
-                synchronized (mViewCacheLoadIndices) {
-                    mViewCacheLoadIndices.clear();
-                    for (int i = 0; i < indicesToLoadCount; ++i) {
-                        final int index = mTmpViewCacheLoadIndices[i];
-                        if (0 <= index && index < length) {
-                            mViewCacheLoadIndices.addLast(index);
-                        }
-                    }
-                }
+            return layout;
+        }
+    }
+
+    /**
+     * The meta-data associated with a single item in the cache.
+     */
+    private class RemoteViewsIndexMetaData {
+        int typeId;
+        long itemId;
+
+        public RemoteViewsIndexMetaData(RemoteViews v, long itemId) {
+            set(v, itemId);
+        }
+
+        public void set(RemoteViews v, long id) {
+            itemId = id;
+            if (v != null)
+                typeId = v.getLayoutId();
+            else
+                typeId = 0;
+        }
+    }
+
+    /**
+     *
+     */
+    private class FixedSizeRemoteViewsCache {
+        private static final String TAG = "FixedSizeRemoteViewsCache";
+
+        // The meta data related to all the RemoteViews, ie. count, is stable, etc.
+        private RemoteViewsMetaData mMetaData;
+
+        // The cache/mapping of position to RemoteViewsMetaData.  This set is guaranteed to be
+        // greater than or equal to the set of RemoteViews.
+        // Note: The reason that we keep this separate from the RemoteViews cache below is that this
+        // we still need to be able to access the mapping of position to meta data, without keeping
+        // the heavy RemoteViews around.  The RemoteViews cache is trimmed to fixed constraints wrt.
+        // memory and size, but this metadata cache will retain information until the data at the
+        // position is guaranteed as not being necessary any more (usually on notifyDataSetChanged).
+        private HashMap<Integer, RemoteViewsIndexMetaData> mIndexMetaData;
+
+        // The cache of actual RemoteViews, which may be pruned if the cache gets too large, or uses
+        // too much memory.
+        private HashMap<Integer, RemoteViews> mIndexRemoteViews;
+
+        // The set of indices that have been explicitly requested by the collection view
+        private HashSet<Integer> mRequestedIndices;
+
+        // The set of indices to load, including those explicitly requested, as well as those
+        // determined by the preloading algorithm to be prefetched
+        private HashSet<Integer> mLoadIndices;
+
+        // The lower and upper bounds of the preloaded range
+        private int mPreloadLowerBound;
+        private int mPreloadUpperBound;
+
+        // The bounds of this fixed cache, we will try and fill as many items into the cache up to
+        // the maxCount number of items, or the maxSize memory usage.
+        // The maxCountSlack is used to determine if a new position in the cache to be loaded is
+        // sufficiently ouside the old set, prompting a shifting of the "window" of items to be
+        // preloaded.
+        private int mMaxCount;
+        private int mMaxCountSlack;
+        private static final float sMaxCountSlackPercent = 0.75f;
+        private static final int sMaxMemoryUsage = 1024 * 1024;
+
+        public FixedSizeRemoteViewsCache(int maxCacheSize) {
+            mMaxCount = maxCacheSize;
+            mMaxCountSlack = Math.round(sMaxCountSlackPercent * (mMaxCount / 2));
+            mPreloadLowerBound = 0;
+            mPreloadUpperBound = -1;
+            mMetaData = new RemoteViewsMetaData();
+            mIndexMetaData = new HashMap<Integer, RemoteViewsIndexMetaData>();
+            mIndexRemoteViews = new HashMap<Integer, RemoteViews>();
+            mRequestedIndices = new HashSet<Integer>();
+            mLoadIndices = new HashSet<Integer>();
+        }
+
+        public void insert(int position, RemoteViews v, long itemId) {
+            // Trim the cache if we go beyond the count
+            if (mIndexRemoteViews.size() >= mMaxCount) {
+                mIndexRemoteViews.remove(getFarthestPositionFrom(position));
             }
 
-            // return null so that a dummy view can be retrieved
+            // Trim the cache if we go beyond the available memory size constraints
+            while (getRemoteViewsBitmapMemoryUsage() >= sMaxMemoryUsage) {
+                // Note: This is currently the most naive mechanism for deciding what to prune when
+                // we hit the memory limit.  In the future, we may want to calculate which index to
+                // remove based on both its position as well as it's current memory usage, as well
+                // as whether it was directly requested vs. whether it was preloaded by our caching
+                // mechanism.
+                mIndexRemoteViews.remove(getFarthestPositionFrom(position));
+            }
+
+            // Update the metadata cache
+            if (mIndexMetaData.containsKey(position)) {
+                final RemoteViewsIndexMetaData metaData = mIndexMetaData.get(position);
+                metaData.set(v, itemId);
+            } else {
+                mIndexMetaData.put(position, new RemoteViewsIndexMetaData(v, itemId));
+            }
+            mIndexRemoteViews.put(position, v);
+        }
+
+        public RemoteViewsMetaData getMetaData() {
+            return mMetaData;
+        }
+        public RemoteViews getRemoteViewsAt(int position) {
+            if (mIndexRemoteViews.containsKey(position)) {
+                return mIndexRemoteViews.get(position);
+            }
+            return null;
+        }
+        public RemoteViewsIndexMetaData getMetaDataAt(int position) {
+            if (mIndexMetaData.containsKey(position)) {
+                return mIndexMetaData.get(position);
+            }
             return null;
         }
 
-        public View getView(int position, View convertView, ViewGroup parent) {
-            if (mServiceConnection.isConnected()) {
-                // create the flipper views if necessary (we have to do this now
-                // for all the flippers while we have the reference to the parent)
-                initializeLoadingViews(parent);
-
-                // request the item from the cache (queueing it to load if not
-                // in the cache already)
-                RemoteViewsIndexInfo indexInfo = requestCachedIndexInfo(position);
-
-                // update the flipper appropriately
-                synchronized (mViewCache) {
-                    int cacheIndex = getCacheIndex(position);
-                    FrameLayout flipper = mViewCache[cacheIndex].flipper;
-                    flipper.setVisibility(View.VISIBLE);
-                    flipper.setAlpha(1.0f);
-
-                    if (indexInfo == null) {
-                        // hide the item view and show the loading view
-                        flipper.getChildAt(0).setVisibility(View.VISIBLE);
-                        for (int i = 1; i < flipper.getChildCount(); ++i) {
-                            flipper.getChildAt(i).setVisibility(View.GONE);
-                        }
-                    } else {
-                        // hide the loading view and show the item view
-                        for (int i = 0; i < flipper.getChildCount() - 1; ++i) {
-                            flipper.getChildAt(i).setVisibility(View.GONE);
-                        }
-                        flipper.getChildAt(flipper.getChildCount() - 1).setVisibility(View.VISIBLE);
-                    }
-                    return flipper;
+        private int getRemoteViewsBitmapMemoryUsage() {
+            // Calculate the memory usage of all the RemoteViews bitmaps being cached
+            int mem = 0;
+            for (Integer i : mIndexRemoteViews.keySet()) {
+                final RemoteViews v = mIndexRemoteViews.get(i);
+                mem += v.estimateBitmapMemoryUsage();
+            }
+            return mem;
+        }
+        private int getFarthestPositionFrom(int pos) {
+            // Find the index farthest away and remove that
+            int maxDist = 0;
+            int maxDistIndex = -1;
+            for (int i : mIndexRemoteViews.keySet()) {
+                int dist = Math.abs(i-pos);
+                if (dist > maxDist) {
+                    maxDistIndex = i;
+                    maxDist = dist;
                 }
             }
-            return new View(mContext);
+            return maxDistIndex;
         }
 
-        private void initializeLoadingViews(ViewGroup parent) {
-            // ensure that the cache has the appropriate initial flipper
-            synchronized (mViewCache) {
-                if (mViewCache[0].flipper == null) {
-                    for (int i = 0; i < mViewCache.length; ++i) {
-                        FrameLayout flipper = new FrameLayout(mContext);
-                        if (mUserLoadingView != null) {
-                            // use the user-specified loading view
-                            flipper.addView(mUserLoadingView.apply(mContext, parent));
-                        } else {
-                            // calculate the original size of the first row for the loader view
-                            synchronized (mViewCacheInfo) {
-                                if (mFirstViewHeight < 0) {
-                                    View firstView = mFirstView.apply(mContext, parent);
-                                    firstView.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
-                                            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
-                                    mFirstViewHeight = firstView.getMeasuredHeight();
-                                }
-                            }
-
-                            // construct a new loader and add it to the flipper as the fallback
-                            // default view
-                            TextView textView = new TextView(mContext);
-                            textView.setText("Loading...");
-                            textView.setHeight(mFirstViewHeight);
-                            textView.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);
-                            textView.setTextSize(18.0f);
-                            textView.setTextColor(Color.argb(96, 255, 255, 255));
-                            textView.setShadowLayer(2.0f, 0.0f, 1.0f, Color.BLACK);
-
-                            flipper.addView(textView);
-                        }
-                        mViewCache[i].flipper = flipper;
-                    }
+        public void queueRequestedPositionToLoad(int position) {
+            synchronized (mLoadIndices) {
+                mRequestedIndices.add(position);
+                mLoadIndices.add(position);
+            }
+        }
+        public void queuePositionsToBePreloadedFromRequestedPosition(int position) {
+            // Check if we need to preload any items
+            if (mPreloadLowerBound <= position && position <= mPreloadUpperBound) {
+                int center = (mPreloadUpperBound + mPreloadLowerBound) / 2;
+                if (Math.abs(position - center) < mMaxCountSlack) {
+                    return;
                 }
             }
-        }
 
-        public void startBackgroundLoader() {
-            // initialize the worker runnable
-            mBackgroundLoaderEnabled = true;
-            mWorkerQueue.post(new Runnable() {
-                @Override
-                public void run() {
-                    while (mBackgroundLoaderEnabled) {
-                        // notify the RemoteViews factory if necessary
-                        boolean isDataDirty = false;
-                        synchronized (mViewCacheInfo) {
-                            isDataDirty = mViewCacheInfo.isDataDirty;
-                            mViewCacheInfo.isDataDirty = false;
-                        }
-                        if (isDataDirty) {
-                            updateNotifyDataSetChanged();
-                        }
-
-                        int index = -1;
-                        synchronized (mViewCacheLoadIndices) {
-                            if (!mViewCacheLoadIndices.isEmpty()) {
-                                index = mViewCacheLoadIndices.removeFirst();
-                            }
-                        }
-                        if (index < 0) {
-                            // there were no items to load, so sleep for a bit
-                            try {
-                                Thread.sleep(10);
-                            } catch (InterruptedException e) {
-                                e.printStackTrace();
-                            }
-                        } else {
-                            // otherwise, try and load the item
-                            updateRemoteViewsInfo(index);
-                        }
-                    }
-                }
-            });
-        }
-
-        public void stopBackgroundLoader() {
-            // clear the items to be loaded
-            mBackgroundLoaderEnabled = false;
-            synchronized (mViewCacheLoadIndices) {
-                mViewCacheLoadIndices.clear();
+            int count = 0;
+            synchronized (mMetaData) {
+                count = mMetaData.count;
             }
-        }
+            synchronized (mLoadIndices) {
+                mLoadIndices.clear();
 
-        public long getItemId(int position) {
-            synchronized (mViewCache) {
-                if (containsAndIsValid(position)) {
-                    return mViewCache[getCacheIndex(position)].itemId;
-                }
-            }
-            return 0;
-        }
+                // Add all the requested indices
+                mLoadIndices.addAll(mRequestedIndices);
 
-        public int getItemViewType(int position) {
-            // synchronize to ensure that the type id/index map is updated synchronously
-            synchronized (mViewCache) {
-                if (containsAndIsValid(position)) {
-                    int viewId = mViewCache[getCacheIndex(position)].typeId;
-                    Map<Integer, Integer> typeMap = mViewCacheInfo.mTypeIdIndexMap;
-                    // we +1 because the default dummy view get view type 0
-                    if (typeMap.containsKey(viewId)) {
-                        return typeMap.get(viewId);
-                    } else {
-                        int newIndex = typeMap.size() + 1;
-                        typeMap.put(viewId, newIndex);
-                        return newIndex;
-                    }
-                }
-            }
-            // return the type of the default item
-            return 0;
-        }
-
-        public int getCount() {
-            synchronized (mViewCacheInfo) {
-                return mViewCacheInfo.count;
-            }
-        }
-
-        public int getViewTypeCount() {
-            synchronized (mViewCacheInfo) {
-                return mViewCacheInfo.viewTypeCount;
-            }
-        }
-
-        public boolean hasStableIds() {
-            synchronized (mViewCacheInfo) {
-                return mViewCacheInfo.hasStableIds;
-            }
-        }
-
-        public void flushCache() {
-            // clear the items to be loaded
-            synchronized (mViewCacheLoadIndices) {
-                mViewCacheLoadIndices.clear();
-            }
-
-            synchronized (mViewCache) {
-                // flush the internal cache and invalidate the adapter for future loads
-                mMainQueue.removeMessages(0);
-
-                for (int i = 0; i < mViewCache.length; ++i) {
-                    mViewCache[i].invalidate();
+                // Add all the preload indices
+                int halfMaxCount = mMaxCount / 2;
+                mPreloadLowerBound = position - halfMaxCount;
+                mPreloadUpperBound = position + halfMaxCount;
+                int effectiveLowerBound = Math.max(0, mPreloadLowerBound);
+                int effectiveUpperBound = Math.min(mPreloadUpperBound, count - 1);
+                for (int i = effectiveLowerBound; i <= effectiveUpperBound; ++i) {
+                    mLoadIndices.add(i);
                 }
 
-                mViewCacheStartPosition = 0;
-                mViewCacheEndPosition = -1;
+                // But remove all the indices that have already been loaded and are cached
+                mLoadIndices.removeAll(mIndexRemoteViews.keySet());
+            }
+        }
+        public int getNextIndexToLoad() {
+            // We try and prioritize items that have been requested directly, instead
+            // of items that are loaded as a result of the caching mechanism
+            synchronized (mLoadIndices) {
+                // Prioritize requested indices to be loaded first
+                if (!mRequestedIndices.isEmpty()) {
+                    Integer i = mRequestedIndices.iterator().next();
+                    mRequestedIndices.remove(i);
+                    mLoadIndices.remove(i);
+                    return i.intValue();
+                }
+
+                // Otherwise, preload other indices as necessary
+                if (!mLoadIndices.isEmpty()) {
+                    Integer i = mLoadIndices.iterator().next();
+                    mLoadIndices.remove(i);
+                    return i.intValue();
+                }
+
+                return -1;
+            }
+        }
+
+        public boolean containsRemoteViewAt(int position) {
+            return mIndexRemoteViews.containsKey(position);
+        }
+        public boolean containsMetaDataAt(int position) {
+            return mIndexMetaData.containsKey(position);
+        }
+
+        public void reset() {
+            mPreloadLowerBound = 0;
+            mPreloadUpperBound = -1;
+            mIndexRemoteViews.clear();
+            mIndexMetaData.clear();
+            mMetaData.reset();
+            synchronized (mLoadIndices) {
+                mRequestedIndices.clear();
+                mLoadIndices.clear();
             }
         }
     }
@@ -672,24 +573,126 @@
         if (mIntent == null) {
             throw new IllegalArgumentException("Non-null Intent must be specified.");
         }
+        mRequestedViews = new RemoteViewsFrameLayoutRefSet();
 
         // initialize the worker thread
         mWorkerThread = new HandlerThread("RemoteViewsCache-loader");
         mWorkerThread.start();
         mWorkerQueue = new Handler(mWorkerThread.getLooper());
-        mWorkerPriorityQueue = new Handler(mWorkerThread.getLooper());
         mMainQueue = new Handler(Looper.myLooper());
-        mMainPriorityQueue = new Handler(Looper.myLooper());
 
         // initialize the cache and the service connection on startup
-        mViewCache = new RemoteViewsCache(25);
-        mServiceConnection = new RemoteViewsAdapterServiceConnection(callback);
+        mCache = new FixedSizeRemoteViewsCache(50);
+        mCallback = new WeakReference<RemoteAdapterConnectionCallback>(callback);
+        mServiceConnection = new RemoteViewsAdapterServiceConnection(this);
         requestBindService();
     }
 
-    protected void finalize() throws Throwable {
-        // remember to unbind from the service when finalizing
-        unbindService();
+    private void loadNextIndexInBackground() {
+        mWorkerQueue.post(new Runnable() {
+            @Override
+            public void run() {
+                boolean isDataDirty = false;
+
+                // If the data set has changed, then notify the remote factory so that it can
+                // update its internals first.
+                final RemoteViewsMetaData metaData = mCache.getMetaData();
+                synchronized (metaData) {
+                    isDataDirty = metaData.isDataDirty;
+                    metaData.isDataDirty = false;
+                }
+                if (isDataDirty) {
+                    completeNotifyDataSetChanged();
+                }
+
+                // Get the next index to load
+                int position = -1;
+                synchronized (mCache) {
+                    position = mCache.getNextIndexToLoad();
+                }
+                if (position > -1) {
+                    // Load the item, and notify any existing RemoteViewsFrameLayouts
+                    updateRemoteViews(position);
+
+                    // Queue up for the next one to load
+                    loadNextIndexInBackground();
+                }
+            }
+        });
+    }
+
+    private void updateMetaData() {
+        if (mServiceConnection.isConnected()) {
+            try {
+                IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
+
+                // get the properties/first view (so that we can use it to
+                // measure our dummy views)
+                boolean hasStableIds = factory.hasStableIds();
+                int viewTypeCount = factory.getViewTypeCount();
+                int count = factory.getCount();
+                RemoteViews loadingView = factory.getLoadingView();
+                RemoteViews firstView = null;
+                if ((count > 0) && (loadingView == null)) {
+                    firstView = factory.getViewAt(0);
+                }
+                final RemoteViewsMetaData metaData = mCache.getMetaData();
+                synchronized (metaData) {
+                    metaData.hasStableIds = hasStableIds;
+                    metaData.viewTypeCount = viewTypeCount + 1;
+                    metaData.count = count;
+                    metaData.setLoadingViewTemplates(loadingView, firstView);
+                }
+            } catch (Exception e) {
+                // print the error
+                Log.e(TAG, "Error in requestMetaData(): " + e.getMessage());
+
+                // reset any members after the failed call
+                final RemoteViewsMetaData metaData = mCache.getMetaData();
+                synchronized (metaData) {
+                    metaData.reset();
+                }
+            }
+        }
+    }
+
+    private void updateRemoteViews(final int position) {
+        if (mServiceConnection.isConnected()) {
+            IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
+
+            // Load the item information from the remote service
+            RemoteViews remoteViews = null;
+            long itemId = 0;
+            try {
+                remoteViews = factory.getViewAt(position);
+                itemId = factory.getItemId(position);
+            } catch (Exception e) {
+                // Print the error
+                Log.e(TAG, "Error in updateRemoteViewsInfo(" + position + "): " +
+                        e.getMessage());
+                e.printStackTrace();
+
+                // Return early to prevent additional work in re-centering the view cache, and
+                // swapping from the loading view
+                return;
+            }
+
+            synchronized (mCache) {
+                // Cache the RemoteViews we loaded
+                mCache.insert(position, remoteViews, itemId);
+
+                // Notify all the views that we have previously returned for this index that
+                // there is new data for it.
+                final RemoteViews rv = remoteViews;
+                final int typeId = mCache.getMetaDataAt(position).typeId;
+                mMainQueue.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        mRequestedViews.notifyOnRemoteViewsLoaded(position, rv, typeId);
+                    }
+                });
+            }
+        }
     }
 
     public Intent getRemoteViewsServiceIntent() {
@@ -698,37 +701,132 @@
 
     public int getCount() {
         requestBindService();
-        return mViewCache.getCount();
+        final RemoteViewsMetaData metaData = mCache.getMetaData();
+        synchronized (metaData) {
+            return metaData.count;
+        }
     }
 
     public Object getItem(int position) {
-        // disallow arbitrary object to be associated with an item for the time being
+        // Disallow arbitrary object to be associated with an item for the time being
         return null;
     }
 
     public long getItemId(int position) {
         requestBindService();
-        return mViewCache.getItemId(position);
+        synchronized (mCache) {
+            if (mCache.containsMetaDataAt(position)) {
+                return mCache.getMetaDataAt(position).itemId;
+            }
+            return 0;
+        }
     }
 
     public int getItemViewType(int position) {
         requestBindService();
-        return mViewCache.getItemViewType(position);
+        int typeId = 0;
+        synchronized (mCache) {
+            if (mCache.containsMetaDataAt(position)) {
+                typeId = mCache.getMetaDataAt(position).typeId;
+            } else {
+                return 0;
+            }
+        }
+
+        final RemoteViewsMetaData metaData = mCache.getMetaData();
+        synchronized (metaData) {
+            return metaData.getMappedViewType(typeId);
+        }
+    }
+
+    /**
+     * Returns the item type id for the specified convert view.  Returns -1 if the convert view
+     * is invalid.
+     */
+    private int getConvertViewTypeId(View convertView) {
+        int typeId = -1;
+        if (convertView != null && convertView.getTag() != null) {
+            typeId = (Integer) convertView.getTag();
+        }
+        return typeId;
     }
 
     public View getView(int position, View convertView, ViewGroup parent) {
         requestBindService();
-        return mViewCache.getView(position, convertView, parent);
+        if (mServiceConnection.isConnected()) {
+            // "Request" an index so that we can queue it for loading, initiate subsequent
+            // preloading, etc.
+            synchronized (mCache) {
+                // Queue up other indices to be preloaded based on this position
+                mCache.queuePositionsToBePreloadedFromRequestedPosition(position);
+
+                RemoteViewsFrameLayout layout = (RemoteViewsFrameLayout) convertView;
+                View convertViewChild = null;
+                int convertViewTypeId = 0;
+                if (convertView != null) {
+                    convertViewChild = layout.getChildAt(0);
+                    convertViewTypeId = getConvertViewTypeId(convertViewChild);
+                }
+
+                // Second, we try and retrieve the RemoteViews from the cache, returning a loading
+                // view and queueing it to be loaded if it has not already been loaded.
+                if (mCache.containsRemoteViewAt(position)) {
+                    Context context = parent.getContext();
+                    RemoteViews rv = mCache.getRemoteViewsAt(position);
+                    int typeId = mCache.getMetaDataAt(position).typeId;
+
+                    // Reuse the convert view where possible
+                    if (convertView != null) {
+                        if (convertViewTypeId == typeId) {
+                            rv.reapply(context, convertViewChild);
+                            return convertView;
+                        }
+                    }
+
+                    // Otherwise, create a new view to be returned
+                    View newView = rv.apply(context, parent);
+                    newView.setTag(new Integer(typeId));
+                    if (convertView != null) {
+                        layout.removeAllViews();
+                    } else {
+                        layout = new RemoteViewsFrameLayout(context);
+                    }
+                    layout.addView(newView);
+                    return layout;
+                } else {
+                    // If the cache does not have the RemoteViews at this position, then create a
+                    // loading view and queue the actual position to be loaded in the background
+                    RemoteViewsFrameLayout loadingView = null;
+                    final RemoteViewsMetaData metaData = mCache.getMetaData();
+                    synchronized (metaData) {
+                        loadingView = metaData.createLoadingView(position, convertView, parent);
+                    }
+
+                    mRequestedViews.add(position, loadingView);
+                    mCache.queueRequestedPositionToLoad(position);
+                    loadNextIndexInBackground();
+
+                    return loadingView;
+                }
+            }
+        }
+        return new View(parent.getContext());
     }
 
     public int getViewTypeCount() {
         requestBindService();
-        return mViewCache.getViewTypeCount();
+        final RemoteViewsMetaData metaData = mCache.getMetaData();
+        synchronized (metaData) {
+            return metaData.viewTypeCount;
+        }
     }
 
     public boolean hasStableIds() {
         requestBindService();
-        return mViewCache.hasStableIds();
+        final RemoteViewsMetaData metaData = mCache.getMetaData();
+        synchronized (metaData) {
+            return metaData.hasStableIds;
+        }
     }
 
     public boolean isEmpty() {
@@ -736,14 +834,49 @@
     }
 
     public void notifyDataSetChanged() {
-        // flush the cache so that we can reload new items from the service
-        mViewCache.flushCache();
+        synchronized (mCache) {
+            // Flush the cache so that we can reload new items from the service
+            mCache.reset();
+        }
 
-        // notify the factory that it's data may no longer be valid
-        mViewCache.onNotifyDataSetChanged();
+        final RemoteViewsMetaData metaData = mCache.getMetaData();
+        synchronized (metaData) {
+            // Set flag to calls the remote factory's onDataSetChanged() on the next worker loop
+            metaData.isDataDirty = true;
+        }
+
+        // Note: we do not call super.notifyDataSetChanged() until the RemoteViewsFactory has had
+        // a chance to update itself, and return new meta data associated with the new data.  After
+        // which completeNotifyDataSetChanged() is called.
     }
 
-    public void completeNotifyDataSetChanged() {
+    private void completeNotifyDataSetChanged() {
+        // Complete the actual notifyDataSetChanged() call initiated earlier
+        if (mServiceConnection.isConnected()) {
+            IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
+            try {
+                factory.onDataSetChanged();
+            } catch (Exception e) {
+                Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage());
+
+                // Return early to prevent from further being notified (since nothing has changed)
+                return;
+            }
+        }
+
+        // Re-request the new metadata (only after the notification to the factory)
+        updateMetaData();
+
+        // Propagate the notification back to the base adapter
+        mMainQueue.post(new Runnable() {
+            @Override
+            public void run() {
+                superNotifyDataSetChanged();
+            }
+        });
+    }
+
+    private void superNotifyDataSetChanged() {
         super.notifyDataSetChanged();
     }
 
@@ -755,10 +888,4 @@
 
         return mServiceConnection.isConnected();
     }
-
-    private void unbindService() {
-        if (mServiceConnection.isConnected()) {
-            mContext.unbindService(mServiceConnection);
-        }
-    }
 }
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 9d215b7..8409adc 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -252,21 +252,23 @@
     lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
     
     lpJniStorage->mStreamType = atStreamType;
-    
-    jint* nSession = NULL;
-    if (jSession) {
-        nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
-        if (nSession == NULL) {
-            LOGE("Error creating AudioTrack: Error retrieving session id pointer");
-            delete lpJniStorage;
-            return AUDIOTRACK_ERROR;
-        }
-    } else {
+
+    if (jSession == NULL) {
         LOGE("Error creating AudioTrack: invalid session ID pointer");
         delete lpJniStorage;
         return AUDIOTRACK_ERROR;
     }
 
+    jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
+    if (nSession == NULL) {
+        LOGE("Error creating AudioTrack: Error retrieving session id pointer");
+        delete lpJniStorage;
+        return AUDIOTRACK_ERROR;
+    }
+    int sessionId = nSession[0];
+    env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
+    nSession = NULL;
+
     // create the native AudioTrack object
     AudioTrack* lpTrack = new AudioTrack();
     if (lpTrack == NULL) {
@@ -288,7 +290,7 @@
             0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
             0,// shared mem
             true,// thread can call Java
-            nSession[0]);// audio session ID
+            sessionId);// audio session ID
             
     } else if (memoryMode == javaAudioTrackFields.MODE_STATIC) {
         // AudioTrack is using shared memory
@@ -309,7 +311,7 @@
             0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack 
             lpJniStorage->mMemBase,// shared mem
             true,// thread can call Java
-            nSession[0]);// audio session ID
+            sessionId);// audio session ID
     }
 
     if (lpTrack->initCheck() != NO_ERROR) {
@@ -317,9 +319,13 @@
         goto native_init_failure;
     }
 
+    nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
+    if (nSession == NULL) {
+        LOGE("Error creating AudioTrack: Error retrieving session id pointer");
+        goto native_init_failure;
+    }
     // read the audio session ID back from AudioTrack in case we create a new session
     nSession[0] = lpTrack->getSessionId();
-
     env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
     nSession = NULL;
 
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 3a85bc1..cd57ab2 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -30,6 +30,7 @@
 #include <SkTemplates.h>
 #include <SkXfermode.h>
 
+#include <OpenGLDebugRenderer.h>
 #include <OpenGLRenderer.h>
 #include <SkiaShader.h>
 #include <SkiaColorFilter.h>
@@ -48,6 +49,8 @@
  */
 #ifdef USE_OPENGL_RENDERER
 
+#define DEBUG_RENDERER 0
+
 // ----------------------------------------------------------------------------
 // Java APIs
 // ----------------------------------------------------------------------------
@@ -62,7 +65,11 @@
 // ----------------------------------------------------------------------------
 
 static OpenGLRenderer* android_view_GLES20Canvas_createRenderer(JNIEnv* env, jobject canvas) {
+#if DEBUG_RENDERER
+    return new OpenGLDebugRenderer;
+#else
     return new OpenGLRenderer;
+#endif
 }
 
 static void android_view_GLES20Canvas_destroyRenderer(JNIEnv* env, jobject canvas,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 06c8423..d5065f6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -244,6 +244,14 @@
         android:description="@string/permdesc_writeHistoryBookmarks"
         android:protectionLevel="dangerous" />
 
+    <!-- Allows an application to broadcast an Intent to set an alarm for the
+         user. -->
+    <permission android:name="com.android.alarm.permission.SET_ALARM"
+        android:permissionGroup="android.permission-group.PERSONAL_INFO"
+        android:label="@string/permlab_setAlarm"
+        android:description="@string/permdesc_setAlarm"
+        android:protectionLevel="normal" />
+
     <!-- ======================================= -->
     <!-- Permissions for accessing location info -->
     <!-- ======================================= -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 2d4de8b..153dd29 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1650,6 +1650,15 @@
 
     <!-- Title of an application permission, listed so the user can choose whether
         they want to allow the application to do this. -->
+    <string name="permlab_setAlarm">set alarm in alarm clock</string>
+    <!-- Description of an application permission, listed so the user can choose whether
+        they want to allow the application to do this. -->
+    <string name="permdesc_setAlarm">Allows the application to set an alarm in
+      an installed alarm clock application. Some alarm clock applications may
+      not implement this feature.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether
+        they want to allow the application to do this. -->
     <string name="permlab_writeGeolocationPermissions">Modify Browser geolocation permissions</string>
     <!-- Description of an application permission, listed so the user can choose whether
         they want to allow the application to do this. -->
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 9a98d53..23efcd1 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -164,15 +164,17 @@
                     sdensity, tdensity);
             mBitmapHeight = Bitmap.scaleFromDensity(mNinePatch.getHeight(),
                     sdensity, tdensity);
-            Rect dest = mPadding;
-            Rect src = mNinePatchState.mPadding;
-            if (dest == src) {
-                mPadding = dest = new Rect(src);
+            if (mNinePatchState.mPadding != null && mPadding != null) {
+                Rect dest = mPadding;
+                Rect src = mNinePatchState.mPadding;
+                if (dest == src) {
+                    mPadding = dest = new Rect(src);
+                }
+                dest.left = Bitmap.scaleFromDensity(src.left, sdensity, tdensity);
+                dest.top = Bitmap.scaleFromDensity(src.top, sdensity, tdensity);
+                dest.right = Bitmap.scaleFromDensity(src.right, sdensity, tdensity);
+                dest.bottom = Bitmap.scaleFromDensity(src.bottom, sdensity, tdensity);
             }
-            dest.left = Bitmap.scaleFromDensity(src.left, sdensity, tdensity);
-            dest.top = Bitmap.scaleFromDensity(src.top, sdensity, tdensity);
-            dest.right = Bitmap.scaleFromDensity(src.right, sdensity, tdensity);
-            dest.bottom = Bitmap.scaleFromDensity(src.bottom, sdensity, tdensity);
         }
     }
 
diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h
index dd44aa5..76307b2 100644
--- a/include/surfaceflinger/ISurfaceComposer.h
+++ b/include/surfaceflinger/ISurfaceComposer.h
@@ -110,6 +110,13 @@
      */
     virtual void bootFinished() = 0;
 
+    /* Capture the specified screen. requires READ_FRAME_BUFFER permission
+     * This function will fail if there is a secure window on screen.
+     */
+    virtual status_t captureScreen(DisplayID dpy,
+            sp<IMemoryHeap>* heap,
+            uint32_t* width, uint32_t* height, PixelFormat* format) = 0;
+
     /* Signal surfaceflinger that there might be some work to do
      * This is an ASYNCHRONOUS call.
      */
@@ -133,7 +140,8 @@
         SET_ORIENTATION,
         FREEZE_DISPLAY,
         UNFREEZE_DISPLAY,
-        SIGNAL
+        SIGNAL,
+        CAPTURE_SCREEN
     };
 
     virtual status_t    onTransact( uint32_t code,
diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h
index 97d31f4..9668bde 100644
--- a/include/utils/ZipFileRO.h
+++ b/include/utils/ZipFileRO.h
@@ -24,8 +24,9 @@
 #ifndef __LIBS_ZIPFILERO_H
 #define __LIBS_ZIPFILERO_H
 
-#include "Errors.h"
-#include "FileMap.h"
+#include <utils/Errors.h>
+#include <utils/FileMap.h>
+#include <utils/threads.h>
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -211,6 +212,9 @@
     /* open Zip archive */
     int         mFd;
 
+    /* Lock for handling the file descriptor (seeks, etc) */
+    mutable Mutex mFdLock;
+
     /* zip file name */
     char*       mFileName;
 
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index a3e117f..13c58f0 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -517,12 +517,26 @@
     }
     
     if ((flags & TF_ONE_WAY) == 0) {
+        #if 0
+        if (code == 4) { // relayout
+            LOGI(">>>>>> CALLING transaction 4");
+        } else {
+            LOGI(">>>>>> CALLING transaction %d", code);
+        }
+        #endif
         if (reply) {
             err = waitForResponse(reply);
         } else {
             Parcel fakeReply;
             err = waitForResponse(&fakeReply);
         }
+        #if 0
+        if (code == 4) { // relayout
+            LOGI("<<<<<< RETURNING transaction 4");
+        } else {
+            LOGI("<<<<<< RETURNING transaction %d", code);
+        }
+        #endif
         
         IF_LOG_TRANSACTIONS() {
             TextOutput::Bundle _b(alog);
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 0469508..e8ffd99 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -5,11 +5,13 @@
 # defined in the current device/board configuration
 ifeq ($(USE_OPENGL_RENDERER),true)
 	LOCAL_SRC_FILES:= \
+		FboCache.cpp \
 		FontRenderer.cpp \
 		GammaFontRenderer.cpp \
 		GradientCache.cpp \
 		LayerCache.cpp \
 		Matrix.cpp \
+		OpenGLDebugRenderer.cpp \
 		OpenGLRenderer.cpp \
 		Patch.cpp \
 		PatchCache.cpp \
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 5ef66f3..a4933c0 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -31,6 +31,7 @@
 #include "ProgramCache.h"
 #include "PathCache.h"
 #include "TextDropShadowCache.h"
+#include "FboCache.h"
 #include "Line.h"
 
 namespace android {
@@ -65,6 +66,7 @@
     PatchCache patchCache;
     TextDropShadowCache dropShadowCache;
     GammaFontRenderer fontRenderer;
+    FboCache fboCache;
 
     Line line;
 }; // class Caches
diff --git a/libs/hwui/FboCache.cpp b/libs/hwui/FboCache.cpp
new file mode 100644
index 0000000..77fbda2
--- /dev/null
+++ b/libs/hwui/FboCache.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include "FboCache.h"
+#include "Properties.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+FboCache::FboCache(): mMaxSize(DEFAULT_FBO_CACHE_SIZE) {
+    char property[PROPERTY_VALUE_MAX];
+    if (property_get(PROPERTY_FBO_CACHE_SIZE, property, NULL) > 0) {
+        LOGD("  Setting fbo cache size to %s", property);
+        mMaxSize = atoi(property);
+    } else {
+        LOGD("  Using default fbo cache size of %d", DEFAULT_FBO_CACHE_SIZE);
+    }
+}
+
+FboCache::~FboCache() {
+    clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Size management
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t FboCache::getSize() {
+    return mCache.size();
+}
+
+uint32_t FboCache::getMaxSize() {
+    return mMaxSize;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
+
+void FboCache::clear() {
+
+}
+
+GLuint FboCache::get() {
+    return 0;
+}
+
+bool FboCache::put(GLuint fbo) {
+    return false;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/FboCache.h b/libs/hwui/FboCache.h
new file mode 100644
index 0000000..66f66ea
--- /dev/null
+++ b/libs/hwui/FboCache.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      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_UI_FBO_CACHE_H
+#define ANDROID_UI_FBO_CACHE_H
+
+#include <GLES2/gl2.h>
+
+#include <utils/SortedVector.h>
+
+#include "GenerationCache.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Cache
+///////////////////////////////////////////////////////////////////////////////
+
+class FboCache {
+public:
+    FboCache();
+    ~FboCache();
+
+    /**
+     * Returns an FBO from the cache. If no FBO is available, a new one
+     * is created. If creating a new FBO fails, 0 is returned.
+     *
+     * When an FBO is obtained from the cache, it is removed and the
+     * total number of FBOs available in the cache decreases.
+     *
+     * @return The name of the FBO, or 0 if no FBO can be obtained.
+     */
+    GLuint get();
+
+    /**
+     * Adds the specified FBO to the cache.
+     *
+     * @param fbo The FBO to add to the cache.
+     *
+     * @return True if the FBO was added, false otherwise.
+     */
+    bool put(GLuint fbo);
+
+    /**
+     * Clears the cache. This causes all FBOs to be deleted.
+     */
+    void clear();
+
+    /**
+     * Returns the current size of the cache.
+     */
+    uint32_t getSize();
+
+    /**
+     * Returns the maximum number of FBOs that the cache can hold.
+     */
+    uint32_t getMaxSize();
+
+private:
+    SortedVector<GLuint> mCache;
+    uint32_t mMaxSize;
+}; // class FboCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_FBO_CACHE_H
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 2959814..b66696d 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -19,6 +19,7 @@
 #include <SkUtils.h>
 
 #include <cutils/properties.h>
+
 #include <utils/Log.h>
 
 #include "FontRenderer.h"
diff --git a/libs/hwui/OpenGLDebugRenderer.cpp b/libs/hwui/OpenGLDebugRenderer.cpp
new file mode 100644
index 0000000..f5a4286
--- /dev/null
+++ b/libs/hwui/OpenGLDebugRenderer.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <utils/StopWatch.h>
+
+#include "OpenGLDebugRenderer.h"
+
+namespace android {
+namespace uirenderer {
+
+void OpenGLDebugRenderer::prepare() {
+    mPrimitivesCount = 0;
+    LOGD("========= Frame start =========");
+    OpenGLRenderer::prepare();
+}
+
+void OpenGLDebugRenderer::finish() {
+    LOGD("========= Frame end =========");
+    LOGD("Primitives draw count = %d", mPrimitivesCount);
+    OpenGLRenderer::finish();
+}
+
+void OpenGLDebugRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
+    mPrimitivesCount++;
+    StopWatch w("composeLayer");
+    return OpenGLRenderer::composeLayer(current, previous);
+}
+
+int OpenGLDebugRenderer::saveLayer(float left, float top, float right, float bottom,
+        const SkPaint* p, int flags) {
+    mPrimitivesCount++;
+    StopWatch w("saveLayer");
+    return OpenGLRenderer::saveLayer(left, top, right, bottom, p, flags);
+}
+
+void OpenGLDebugRenderer::drawBitmap(SkBitmap* bitmap, float left, float top,
+        const SkPaint* paint) {
+    mPrimitivesCount++;
+    StopWatch w("drawBitmap");
+    OpenGLRenderer::drawBitmap(bitmap, left, top, paint);
+}
+
+void OpenGLDebugRenderer::drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix,
+        const SkPaint* paint) {
+    mPrimitivesCount++;
+    StopWatch w("drawBitmapMatrix");
+    OpenGLRenderer::drawBitmap(bitmap, matrix, paint);
+}
+
+void OpenGLDebugRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
+        float srcRight, float srcBottom, float dstLeft, float dstTop,
+        float dstRight, float dstBottom, const SkPaint* paint) {
+    mPrimitivesCount++;
+    StopWatch w("drawBitmapRect");
+    OpenGLRenderer::drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
+            dstLeft, dstTop, dstRight, dstBottom, paint);
+}
+
+void OpenGLDebugRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
+        float left, float top, float right, float bottom, const SkPaint* paint) {
+    mPrimitivesCount++;
+    StopWatch w("drawPatch");
+    OpenGLRenderer::drawPatch(bitmap, patch, left, top, right, bottom, paint);
+}
+
+void OpenGLDebugRenderer::drawColor(int color, SkXfermode::Mode mode) {
+    mPrimitivesCount++;
+    StopWatch w("drawColor");
+    OpenGLRenderer::drawColor(color, mode);
+}
+
+void OpenGLDebugRenderer::drawRect(float left, float top, float right, float bottom,
+        const SkPaint* paint) {
+    mPrimitivesCount++;
+    StopWatch w("drawRect");
+    OpenGLRenderer::drawRect(left, top, right, bottom, paint);
+}
+
+void OpenGLDebugRenderer::drawPath(SkPath* path, SkPaint* paint) {
+    mPrimitivesCount++;
+    StopWatch w("drawPath");
+    OpenGLRenderer::drawPath(path, paint);
+}
+
+void OpenGLDebugRenderer::drawLines(float* points, int count, const SkPaint* paint) {
+    mPrimitivesCount++;
+    StopWatch w("drawLines");
+    OpenGLRenderer::drawLines(points, count, paint);
+}
+
+void OpenGLDebugRenderer::drawText(const char* text, int bytesCount, int count, float x, float y,
+        SkPaint* paint) {
+    mPrimitivesCount++;
+    StopWatch w("drawText");
+    OpenGLRenderer::drawText(text, bytesCount, count, x, y, paint);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/OpenGLDebugRenderer.h b/libs/hwui/OpenGLDebugRenderer.h
new file mode 100644
index 0000000..37fac93
--- /dev/null
+++ b/libs/hwui/OpenGLDebugRenderer.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      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_UI_OPENGL_DEBUG_RENDERER_H
+#define ANDROID_UI_OPENGL_DEBUG_RENDERER_H
+
+#include "OpenGLRenderer.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Renderer
+///////////////////////////////////////////////////////////////////////////////
+
+class OpenGLDebugRenderer: public OpenGLRenderer {
+public:
+    OpenGLDebugRenderer(): mPrimitivesCount(0) {
+    }
+
+    ~OpenGLDebugRenderer() {
+    }
+
+    void prepare();
+    void finish();
+
+    int saveLayer(float left, float top, float right, float bottom,
+            const SkPaint* p, int flags);
+
+    void drawBitmap(SkBitmap* bitmap, float left, float top, const SkPaint* paint);
+    void drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint);
+    void drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
+            float srcRight, float srcBottom, float dstLeft, float dstTop,
+            float dstRight, float dstBottom, const SkPaint* paint);
+    void drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, float left, float top,
+            float right, float bottom, const SkPaint* paint);
+    void drawColor(int color, SkXfermode::Mode mode);
+    void drawRect(float left, float top, float right, float bottom, const SkPaint* paint);
+    void drawPath(SkPath* path, SkPaint* paint);
+    void drawLines(float* points, int count, const SkPaint* paint);
+    void drawText(const char* text, int bytesCount, int count, float x, float y,
+            SkPaint* paint);
+
+protected:
+    void composeLayer(sp<Snapshot> current, sp<Snapshot> previous);
+
+private:
+    uint32_t mPrimitivesCount;
+
+}; // class OpenGLDebugRenderer
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_OPENGL_DEBUG_RENDERER_H
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index e032085..dbd499e 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -24,6 +24,7 @@
 #include <SkTypeface.h>
 
 #include <utils/Log.h>
+#include <utils/StopWatch.h>
 
 #include "OpenGLRenderer.h"
 
@@ -710,6 +711,7 @@
     if (text == NULL || count == 0 || (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) {
         return;
     }
+
     paint->setAntiAlias(true);
 
     float length = -1.0f;
@@ -739,6 +741,7 @@
     FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer(paint);
     fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()),
             paint->getTextSize());
+
     if (mHasShadow) {
         glActiveTexture(gTextureUnits[0]);
         mCaches.dropShadowCache.setFontRenderer(fontRenderer);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index f903505..af2a70b 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -62,11 +62,13 @@
 class OpenGLRenderer {
 public:
     OpenGLRenderer();
-    ~OpenGLRenderer();
+    virtual ~OpenGLRenderer();
 
     void setViewport(int width, int height);
-    void prepare();
-    void finish();
+
+    virtual void prepare();
+    virtual void finish();
+
     void acquireContext();
     void releaseContext();
 
@@ -75,8 +77,10 @@
     void restore();
     void restoreToCount(int saveCount);
 
-    int saveLayer(float left, float top, float right, float bottom, const SkPaint* p, int flags);
-    int saveLayerAlpha(float left, float top, float right, float bottom, int alpha, int flags);
+    virtual int saveLayer(float left, float top, float right, float bottom,
+            const SkPaint* p, int flags);
+    virtual int saveLayerAlpha(float left, float top, float right, float bottom,
+            int alpha, int flags);
 
     void translate(float dx, float dy);
     void rotate(float degrees);
@@ -90,16 +94,19 @@
     bool quickReject(float left, float top, float right, float bottom);
     bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
 
-    void drawBitmap(SkBitmap* bitmap, float left, float top, const SkPaint* paint);
-    void drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint);
-    void drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom,
-            float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint);
-    void drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, float left, float top,
+    virtual void drawBitmap(SkBitmap* bitmap, float left, float top, const SkPaint* paint);
+    virtual void drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint);
+    virtual void drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
+            float srcRight, float srcBottom, float dstLeft, float dstTop,
+            float dstRight, float dstBottom, const SkPaint* paint);
+    virtual void drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, float left, float top,
             float right, float bottom, const SkPaint* paint);
-    void drawColor(int color, SkXfermode::Mode mode);
-    void drawRect(float left, float top, float right, float bottom, const SkPaint* paint);
-    void drawPath(SkPath* path, SkPaint* paint);
-    void drawLines(float* points, int count, const SkPaint* paint);
+    virtual void drawColor(int color, SkXfermode::Mode mode);
+    virtual void drawRect(float left, float top, float right, float bottom, const SkPaint* paint);
+    virtual void drawPath(SkPath* path, SkPaint* paint);
+    virtual void drawLines(float* points, int count, const SkPaint* paint);
+    virtual void drawText(const char* text, int bytesCount, int count, float x, float y,
+            SkPaint* paint);
 
     void resetShader();
     void setupShader(SkiaShader* shader);
@@ -110,7 +117,17 @@
     void resetShadow();
     void setupShadow(float radius, float dx, float dy, int color);
 
-    void drawText(const char* text, int bytesCount, int count, float x, float y, SkPaint* paint);
+protected:
+    /**
+     * Compose the layer defined in the current snapshot with the layer
+     * defined by the previous snapshot.
+     *
+     * The current snapshot *must* be a layer (flag kFlagIsLayer set.)
+     *
+     * @param curent The current snapshot containing the layer to compose
+     * @param previous The previous snapshot to compose the current layer with
+     */
+    virtual void composeLayer(sp<Snapshot> current, sp<Snapshot> previous);
 
 private:
     /**
@@ -138,17 +155,6 @@
     void setScissorFromClip();
 
     /**
-     * Compose the layer defined in the current snapshot with the layer
-     * defined by the previous snapshot.
-     *
-     * The current snapshot *must* be a layer (flag kFlagIsLayer set.)
-     *
-     * @param curent The current snapshot containing the layer to compose
-     * @param previous The previous snapshot to compose the current layer with
-     */
-    void composeLayer(sp<Snapshot> current, sp<Snapshot> previous);
-
-    /**
      * Creates a new layer stored in the specified snapshot.
      *
      * @param snapshot The snapshot associated with the new layer
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 4e2f091..d573805 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -30,6 +30,7 @@
 #define PROPERTY_GRADIENT_CACHE_SIZE "ro.hwui.gradient_cache_size"
 #define PROPERTY_PATH_CACHE_SIZE "ro.hwui.path_cache_size"
 #define PROPERTY_DROP_SHADOW_CACHE_SIZE "ro.hwui.drop_shadow_cache_size"
+#define PROPERTY_FBO_CACHE_SIZE "ro.hwui.fbo_cache_size"
 
 // These properties are defined in pixels
 #define PROPERTY_TEXT_CACHE_WIDTH "ro.hwui.text_cache_width"
@@ -43,12 +44,13 @@
 // Converts a number of mega-bytes into bytes
 #define MB(s) s * 1024 * 1024
 
-#define DEFAULT_TEXTURE_CACHE_SIZE 20.0f
-#define DEFAULT_LAYER_CACHE_SIZE 6.0f
-#define DEFAULT_PATH_CACHE_SIZE 6.0f
+#define DEFAULT_TEXTURE_CACHE_SIZE 18.0f
+#define DEFAULT_LAYER_CACHE_SIZE 4.0f
+#define DEFAULT_PATH_CACHE_SIZE 5.0f
 #define DEFAULT_PATCH_CACHE_SIZE 100
 #define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
 #define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f
+#define DEFAULT_FBO_CACHE_SIZE 25
 
 #define DEFAULT_TEXT_GAMMA 1.4f
 #define DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD 64
diff --git a/libs/surfaceflinger_client/ISurfaceComposer.cpp b/libs/surfaceflinger_client/ISurfaceComposer.cpp
index 5c111f6..040060e 100644
--- a/libs/surfaceflinger_client/ISurfaceComposer.cpp
+++ b/libs/surfaceflinger_client/ISurfaceComposer.cpp
@@ -124,6 +124,21 @@
         remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply);
     }
 
+    virtual status_t captureScreen(DisplayID dpy,
+            sp<IMemoryHeap>* heap,
+            uint32_t* width, uint32_t* height, PixelFormat* format)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        data.writeInt32(dpy);
+        remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
+        *heap = interface_cast<IMemoryHeap>(reply.readStrongBinder());
+        *width = reply.readInt32();
+        *height = reply.readInt32();
+        *format = reply.readInt32();
+        return reply.readInt32();
+    }
+
     virtual void signal() const
     {
         Parcel data, reply;
@@ -190,6 +205,19 @@
             sp<IBinder> b = getCblk()->asBinder();
             reply->writeStrongBinder(b);
         } break;
+        case CAPTURE_SCREEN: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            DisplayID dpy = data.readInt32();
+            sp<IMemoryHeap> heap;
+            uint32_t w, h;
+            PixelFormat f;
+            status_t res = captureScreen(dpy, &heap, &w, &h, &f);
+            reply->writeStrongBinder(heap->asBinder());
+            reply->writeInt32(w);
+            reply->writeInt32(h);
+            reply->writeInt32(f);
+            reply->writeInt32(res);
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp
index 2d53136..9fcae72 100644
--- a/libs/utils/ZipFileRO.cpp
+++ b/libs/utils/ZipFileRO.cpp
@@ -22,6 +22,7 @@
 #include <utils/ZipFileRO.h>
 #include <utils/Log.h>
 #include <utils/misc.h>
+#include <utils/threads.h>
 
 #include <zlib.h>
 
@@ -195,7 +196,7 @@
             free(scanBuf);
             return false;
         } else if (header != kLFHSignature) {
-            LOGV("Not a Zip archive (found 0x%08x)\n", val);
+            LOGV("Not a Zip archive (found 0x%08x)\n", header);
             free(scanBuf);
             return false;
         }
@@ -496,15 +497,21 @@
         }
 
         unsigned char lfhBuf[kLFHLen];
-        if (lseek(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) {
-            LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset);
-            return false;
-        }
-        ssize_t actual =
-            TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf)));
-        if (actual != sizeof(lfhBuf)) {
-            LOGW("failed reading lfh from offset %ld\n", localHdrOffset);
-            return false;
+
+        {
+            AutoMutex _l(mFdLock);
+
+            if (lseek(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) {
+                LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset);
+                return false;
+            }
+
+            ssize_t actual =
+                TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf)));
+            if (actual != sizeof(lfhBuf)) {
+                LOGW("failed reading lfh from offset %ld\n", localHdrOffset);
+                return false;
+            }
         }
 
         if (get4LE(lfhBuf) != kLFHSignature) {
@@ -636,7 +643,7 @@
         memcpy(buffer, ptr, uncompLen);
     } else {
         if (!inflateBuffer(buffer, ptr, uncompLen, compLen))
-            goto bail;
+            goto unmap;
     }
 
     if (compLen > kSequentialMin)
@@ -644,6 +651,8 @@
 
     result = true;
 
+unmap:
+    file->release();
 bail:
     return result;
 }
@@ -667,7 +676,7 @@
 
     getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL);
 
-    const FileMap* file = createEntryFileMap(entry);
+    FileMap* file = createEntryFileMap(entry);
     if (file == NULL) {
         goto bail;
     }
@@ -678,21 +687,23 @@
         ssize_t actual = write(fd, ptr, uncompLen);
         if (actual < 0) {
             LOGE("Write failed: %s\n", strerror(errno));
-            goto bail;
+            goto unmap;
         } else if ((size_t) actual != uncompLen) {
             LOGE("Partial write during uncompress (%zd of %zd)\n",
                 (size_t)actual, (size_t)uncompLen);
-            goto bail;
+            goto unmap;
         } else {
             LOGI("+++ successful write\n");
         }
     } else {
         if (!inflateBuffer(fd, ptr, uncompLen, compLen))
-            goto bail;
+            goto unmap;
     }
 
     result = true;
 
+unmap:
+    file->release();
 bail:
     return result;
 }
diff --git a/media/java/android/media/videoeditor/MediaImageItem.java b/media/java/android/media/videoeditor/MediaImageItem.java
index db7585a..9c39b20 100755
--- a/media/java/android/media/videoeditor/MediaImageItem.java
+++ b/media/java/android/media/videoeditor/MediaImageItem.java
@@ -116,10 +116,30 @@
     }

 

     /**

+     * This method will adjust the duration of bounding transitions if the

+     * current duration of the transactions become greater than the maximum

+     * allowable duration.

+     *

      * @param durationMs The duration of the image in the storyboard timeline

      */

     public void setDuration(long durationMs) {

         mDurationMs = durationMs;

+

+        // Check if the duration of transitions need to be adjusted

+        if (mBeginTransition != null) {

+            final long maxDurationMs = mBeginTransition.getMaximumDuration();

+            if (mBeginTransition.getDuration() > maxDurationMs) {

+                mBeginTransition.setDuration(maxDurationMs);

+            }

+        }

+

+        if (mEndTransition != null) {

+            final long maxDurationMs = mEndTransition.getMaximumDuration();

+            if (mEndTransition.getDuration() > maxDurationMs) {

+                mEndTransition.setDuration(maxDurationMs);

+            }

+        }

+

         // TODO: Validate/modify the start and the end time of effects and overlays

     }

 

diff --git a/media/java/android/media/videoeditor/MediaItem.java b/media/java/android/media/videoeditor/MediaItem.java
index e7be35d..12fbe540 100755
--- a/media/java/android/media/videoeditor/MediaItem.java
+++ b/media/java/android/media/videoeditor/MediaItem.java
@@ -90,7 +90,7 @@
     }

 

     /**

-     * @return The of the media item

+     * @return The id of the media item

      */

     public String getId() {

         return mUniqueId;

diff --git a/media/java/android/media/videoeditor/MediaVideoItem.java b/media/java/android/media/videoeditor/MediaVideoItem.java
index af50b83..afca55c 100755
--- a/media/java/android/media/videoeditor/MediaVideoItem.java
+++ b/media/java/android/media/videoeditor/MediaVideoItem.java
@@ -245,7 +245,10 @@
     }

 

     /**

-     * Sets the start and end marks for trimming a video media item

+     * Sets the start and end marks for trimming a video media item.

+     * This method will adjust the duration of bounding transitions if the

+     * current duration of the transactions become greater than the maximum

+     * allowable duration.

      *

      * @param beginMs Start time in milliseconds. Set to 0 to extract from the

      *           beginning

@@ -265,18 +268,35 @@
         }

 

         if (beginMs != mBeginBoundaryTimeMs) {

-            mBeginBoundaryTimeMs = beginMs;

             if (mBeginTransition != null) {

                 mBeginTransition.invalidate();

             }

         }

 

-        if (endMs == mEndBoundaryTimeMs) {

-            mEndBoundaryTimeMs = endMs;

+        if (endMs != mEndBoundaryTimeMs) {

             if (mEndTransition != null) {

                 mEndTransition.invalidate();

             }

         }

+

+        mBeginBoundaryTimeMs = beginMs;

+        mEndBoundaryTimeMs = endMs;

+

+        // Check if the duration of transitions need to be adjusted

+        if (mBeginTransition != null) {

+            final long maxDurationMs = mBeginTransition.getMaximumDuration();

+            if (mBeginTransition.getDuration() > maxDurationMs) {

+                mBeginTransition.setDuration(maxDurationMs);

+            }

+        }

+

+        if (mEndTransition != null) {

+            final long maxDurationMs = mEndTransition.getMaximumDuration();

+            if (mEndTransition.getDuration() > maxDurationMs) {

+                mEndTransition.setDuration(maxDurationMs);

+            }

+        }

+

         // TODO: Validate/modify the start and the end time of effects and overlays

     }

 

diff --git a/media/java/android/media/videoeditor/Transition.java b/media/java/android/media/videoeditor/Transition.java
index eb71285..e972aeb 100755
--- a/media/java/android/media/videoeditor/Transition.java
+++ b/media/java/android/media/videoeditor/Transition.java
@@ -124,7 +124,12 @@
      * @param durationMs the duration of the transition in milliseconds

      */

     public void setDuration(long durationMs) {

+        if (durationMs > getMaximumDuration()) {

+            throw new IllegalArgumentException("The duration is too large");

+        }

+

         mDurationMs = durationMs;

+        invalidate();

     }

 

     /**

@@ -135,6 +140,22 @@
     }

 

     /**

+     * The duration of a transition cannot be greater than half of the minimum

+     * duration of the bounding media items.

+     *

+     * @return The maximum duration of this transition

+     */

+    public long getMaximumDuration() {

+        if (mAfterMediaItem == null) {

+            return mBeforeMediaItem.getDuration() / 2;

+        } else if (mBeforeMediaItem == null) {

+            return mAfterMediaItem.getDuration() / 2;

+        } else {

+            return (Math.min(mAfterMediaItem.getDuration(), mBeforeMediaItem.getDuration()) / 2);

+        }

+    }

+

+    /**

      * @return The behavior

      */

     public int getBehavior() {

diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index b16372d..cb2f0f9 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -304,14 +304,7 @@
             lpJniStorage->mCallbackData.audioEffect_class,
             &lpJniStorage->mCallbackData);
 
-    if (jId) {
-        nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL);
-        if (nId == NULL) {
-            LOGE("setup: Error retrieving id pointer");
-            lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
-            goto setup_failure;
-        }
-    } else {
+    if (jId == NULL) {
         LOGE("setup: NULL java array for id pointer");
         lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
         goto setup_failure;
@@ -336,8 +329,13 @@
         goto setup_failure;
     }
 
+    nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL);
+    if (nId == NULL) {
+        LOGE("setup: Error retrieving id pointer");
+        lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
+        goto setup_failure;
+    }
     nId[0] = lpAudioEffect->id();
-
     env->ReleasePrimitiveArrayCritical(jId, nId, 0);
     nId = NULL;
 
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index 7b271ce..57cafd4 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -246,14 +246,7 @@
             lpJniStorage->mCallbackData.visualizer_class,
             &lpJniStorage->mCallbackData);
 
-    if (jId) {
-        nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL);
-        if (nId == NULL) {
-            LOGE("setup: Error retrieving id pointer");
-            lStatus = VISUALIZER_ERROR_BAD_VALUE;
-            goto setup_failure;
-        }
-    } else {
+    if (jId == NULL) {
         LOGE("setup: NULL java array for id pointer");
         lStatus = VISUALIZER_ERROR_BAD_VALUE;
         goto setup_failure;
@@ -275,8 +268,13 @@
         goto setup_failure;
     }
 
+    nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL);
+    if (nId == NULL) {
+        LOGE("setup: Error retrieving id pointer");
+        lStatus = VISUALIZER_ERROR_BAD_VALUE;
+        goto setup_failure;
+    }
     nId[0] = lpVisualizer->id();
-
     env->ReleasePrimitiveArrayCritical(jId, nId, 0);
     nId = NULL;
 
@@ -424,7 +422,6 @@
     jint status = translateError(lpVisualizer->getWaveForm((uint8_t *)nWaveform));
 
     env->ReleasePrimitiveArrayCritical(jWaveform, nWaveform, 0);
-
     return status;
 }
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index ade93da..6e2bfdb 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -327,10 +327,7 @@
         try {
             final String value = c.moveToNext() ? c.getString(0) : null;
             if (value == null) {
-                final SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
-                String serial = SystemProperties.get("ro.serialno", "");
-                random.setSeed(
-                    (serial + System.nanoTime() + new SecureRandom().nextLong()).getBytes());
+                final SecureRandom random = new SecureRandom();
                 final String newAndroidIdValue = Long.toHexString(random.nextLong());
                 Log.d(TAG, "Generated and saved new ANDROID_ID [" + newAndroidIdValue + "]");
                 final ContentValues values = new ContentValues();
@@ -342,8 +339,6 @@
                 }
             }
             return true;
-        } catch (NoSuchAlgorithmException e) {
-            return false;
         } finally {
             c.close();
         }
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index aae3cff..6095117 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -114,6 +114,71 @@
     private boolean mTestMode;
     private static ConnectivityService sServiceInstance;
 
+    private static final int ENABLED  = 1;
+    private static final int DISABLED = 0;
+
+    // Share the event space with NetworkStateTracker (which can't see this
+    // internal class but sends us events).  If you change these, change
+    // NetworkStateTracker.java too.
+    private static final int MIN_NETWORK_STATE_TRACKER_EVENT = 1;
+    private static final int MAX_NETWORK_STATE_TRACKER_EVENT = 100;
+
+    /**
+     * used internally as a delayed event to make us switch back to the
+     * default network
+     */
+    private static final int EVENT_RESTORE_DEFAULT_NETWORK =
+            MAX_NETWORK_STATE_TRACKER_EVENT + 1;
+
+    /**
+     * used internally to change our mobile data enabled flag
+     */
+    private static final int EVENT_CHANGE_MOBILE_DATA_ENABLED =
+            MAX_NETWORK_STATE_TRACKER_EVENT + 2;
+
+    /**
+     * used internally to change our network preference setting
+     * arg1 = networkType to prefer
+     */
+    private static final int EVENT_SET_NETWORK_PREFERENCE =
+            MAX_NETWORK_STATE_TRACKER_EVENT + 3;
+
+    /**
+     * used internally to synchronize inet condition reports
+     * arg1 = networkType
+     * arg2 = condition (0 bad, 100 good)
+     */
+    private static final int EVENT_INET_CONDITION_CHANGE =
+            MAX_NETWORK_STATE_TRACKER_EVENT + 4;
+
+    /**
+     * used internally to mark the end of inet condition hold periods
+     * arg1 = networkType
+     */
+    private static final int EVENT_INET_CONDITION_HOLD_END =
+            MAX_NETWORK_STATE_TRACKER_EVENT + 5;
+
+    /**
+     * used internally to set the background data preference
+     * arg1 = TRUE for enabled, FALSE for disabled
+     */
+    private static final int EVENT_SET_BACKGROUND_DATA =
+            MAX_NETWORK_STATE_TRACKER_EVENT + 6;
+
+    /**
+     * used internally to set enable/disable cellular data
+     * arg1 = ENBALED or DISABLED
+     */
+    private static final int EVENT_SET_MOBILE_DATA =
+            MAX_NETWORK_STATE_TRACKER_EVENT + 7;
+
+    /**
+     * used internally to clear a wakelock when transitioning
+     * from one net to another
+     */
+    private static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK =
+            MAX_NETWORK_STATE_TRACKER_EVENT + 8;
+
     private Handler mHandler;
 
     // list of DeathRecipients used to make sure features are turned off when
@@ -354,28 +419,34 @@
      * Sets the preferred network.
      * @param preference the new preference
      */
-    public synchronized void setNetworkPreference(int preference) {
+    public void setNetworkPreference(int preference) {
         enforceChangePermission();
-        if (ConnectivityManager.isNetworkTypeValid(preference) &&
-                mNetAttributes[preference] != null &&
-                mNetAttributes[preference].isDefault()) {
-            if (mNetworkPreference != preference) {
-                persistNetworkPreference(preference);
-                mNetworkPreference = preference;
-                enforcePreference();
-            }
-        }
+
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_NETWORK_PREFERENCE, preference, 0));
     }
 
     public int getNetworkPreference() {
         enforceAccessPermission();
-        return mNetworkPreference;
+        int preference;
+        synchronized(this) {
+            preference = mNetworkPreference;
+        }
+        return preference;
     }
 
-    private void persistNetworkPreference(int networkPreference) {
-        final ContentResolver cr = mContext.getContentResolver();
-        Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE,
-                networkPreference);
+    private void handleSetNetworkPreference(int preference) {
+        if (ConnectivityManager.isNetworkTypeValid(preference) &&
+                mNetAttributes[preference] != null &&
+                mNetAttributes[preference].isDefault()) {
+            if (mNetworkPreference != preference) {
+                final ContentResolver cr = mContext.getContentResolver();
+                Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE, preference);
+                synchronized(this) {
+                    mNetworkPreference = preference;
+                }
+                enforcePreference();
+            }
+        }
     }
 
     private int getPersistedNetworkPreference() {
@@ -628,8 +699,7 @@
                         mNetRequestersPids[usedNetworkType].add(currentPid);
                     }
                 }
-                mHandler.sendMessageDelayed(mHandler.obtainMessage(
-                        NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK,
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_RESTORE_DEFAULT_NETWORK,
                         f), getRestoreDefaultNetworkDelay());
 
 
@@ -871,15 +941,18 @@
                 android.Manifest.permission.CHANGE_BACKGROUND_DATA_SETTING,
                 "ConnectivityService");
 
-        if (getBackgroundDataSetting() == allowBackgroundDataUsage) return;
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_BACKGROUND_DATA,
+                (allowBackgroundDataUsage ? ENABLED : DISABLED), 0));
+    }
 
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.BACKGROUND_DATA,
-                allowBackgroundDataUsage ? 1 : 0);
-
-        Intent broadcast = new Intent(
-                ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
-        mContext.sendBroadcast(broadcast);
+    private void handleSetBackgroundData(boolean enabled) {
+        if (enabled != getBackgroundDataSetting()) {
+            Settings.Secure.putInt(mContext.getContentResolver(),
+                    Settings.Secure.BACKGROUND_DATA, enabled ? 1 : 0);
+            Intent broadcast = new Intent(
+                    ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
+            mContext.sendBroadcast(broadcast);
+        }
     }
 
     /**
@@ -896,10 +969,15 @@
     /**
      * @see ConnectivityManager#setMobileDataEnabled(boolean)
      */
-    public synchronized void setMobileDataEnabled(boolean enabled) {
+    public void setMobileDataEnabled(boolean enabled) {
         enforceChangePermission();
         if (DBG) Slog.d(TAG, "setMobileDataEnabled(" + enabled + ")");
 
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_MOBILE_DATA,
+            (enabled ? ENABLED : DISABLED), 0));
+    }
+
+    private void handleSetMobileData(boolean enabled) {
         if (getMobileDataEnabled() == enabled) return;
 
         Settings.Secure.putInt(mContext.getContentResolver(),
@@ -907,7 +985,9 @@
 
         if (enabled) {
             if (mNetTrackers[ConnectivityManager.TYPE_MOBILE] != null) {
-                if (DBG) Slog.d(TAG, "starting up " + mNetTrackers[ConnectivityManager.TYPE_MOBILE]);
+                if (DBG) {
+                    Slog.d(TAG, "starting up " + mNetTrackers[ConnectivityManager.TYPE_MOBILE]);
+                }
                 mNetTrackers[ConnectivityManager.TYPE_MOBILE].reconnect();
             }
         } else {
@@ -1267,7 +1347,7 @@
                 // new network
                 if (mNetTransitionWakeLock.isHeld()) {
                     mHandler.sendMessageDelayed(mHandler.obtainMessage(
-                            NetworkStateTracker.EVENT_CLEAR_NET_TRANSITION_WAKELOCK,
+                            EVENT_CLEAR_NET_TRANSITION_WAKELOCK,
                             mNetTransitionWakeLockSerialNumber, 0),
                             1000);
                 }
@@ -1705,11 +1785,7 @@
                     type = info.getType();
                     handleDnsConfigurationChange(type);
                     break;
-                case NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK:
-                    FeatureUser u = (FeatureUser)msg.obj;
-                    u.expire();
-                    break;
-                case NetworkStateTracker.EVENT_CLEAR_NET_TRANSITION_WAKELOCK:
+                case EVENT_CLEAR_NET_TRANSITION_WAKELOCK:
                     String causedBy = null;
                     synchronized (ConnectivityService.this) {
                         if (msg.arg1 == mNetTransitionWakeLockSerialNumber &&
@@ -1723,70 +1799,42 @@
                                 causedBy + " released by timeout");
                     }
                     break;
-                case NetworkStateTracker.EVENT_INET_CONDITION_CHANGE:
-                    if (DBG) {
-                        Slog.d(TAG, "Inet connectivity change, net=" +
-                                msg.arg1 + ", condition=" + msg.arg2 +
-                                ",mActiveDefaultNetwork=" + mActiveDefaultNetwork);
-                    }
-                    if (mActiveDefaultNetwork == -1) {
-                        if (DBG) Slog.d(TAG, "no active default network - aborting");
-                        break;
-                    }
-                    if (mActiveDefaultNetwork != msg.arg1) {
-                        if (DBG) Slog.d(TAG, "given net not default - aborting");
-                        break;
-                    }
-                    mDefaultInetCondition = msg.arg2;
-                    int delay;
-                    if (mInetConditionChangeInFlight == false) {
-                        if (DBG) Slog.d(TAG, "starting a change hold");
-                        // setup a new hold to debounce this
-                        if (mDefaultInetCondition > 50) {
-                            delay = Settings.Secure.getInt(mContext.getContentResolver(),
-                                    Settings.Secure.INET_CONDITION_DEBOUNCE_UP_DELAY, 500);
-                        } else {
-                            delay = Settings.Secure.getInt(mContext.getContentResolver(),
-                                    Settings.Secure.INET_CONDITION_DEBOUNCE_DOWN_DELAY, 3000);
-                        }
-                        mInetConditionChangeInFlight = true;
-                        sendMessageDelayed(obtainMessage(
-                                NetworkStateTracker.EVENT_INET_CONDITION_HOLD_END,
-                                mActiveDefaultNetwork, mDefaultConnectionSequence), delay);
-                    } else {
-                        // we've set the new condition, when this hold ends that will get
-                        // picked up
-                        if (DBG) Slog.d(TAG, "currently in hold - not setting new end evt");
-                    }
+                case EVENT_RESTORE_DEFAULT_NETWORK:
+                    FeatureUser u = (FeatureUser)msg.obj;
+                    u.expire();
                     break;
-                case NetworkStateTracker.EVENT_INET_CONDITION_HOLD_END:
-                    if (DBG) {
-                        Slog.d(TAG, "Inet hold end, net=" + msg.arg1 +
-                                ", condition =" + mDefaultInetCondition +
-                                ", published condition =" + mDefaultInetConditionPublished);
-                    }
-                    mInetConditionChangeInFlight = false;
-
-                    if (mActiveDefaultNetwork == -1) {
-                        if (DBG) Slog.d(TAG, "no active default network - aborting");
-                        break;
-                    }
-                    if (mDefaultConnectionSequence != msg.arg2) {
-                        if (DBG) Slog.d(TAG, "event hold for obsolete network - aborting");
-                        break;
-                    }
-                    if (mDefaultInetConditionPublished == mDefaultInetCondition) {
-                        if (DBG) Slog.d(TAG, "no change in condition - aborting");
-                        break;
-                    }
-                    NetworkInfo networkInfo = mNetTrackers[mActiveDefaultNetwork].getNetworkInfo();
-                    if (networkInfo.isConnected() == false) {
-                        if (DBG) Slog.d(TAG, "default network not connected - aborting");
-                        break;
-                    }
-                    mDefaultInetConditionPublished = mDefaultInetCondition;
-                    sendInetConditionBroadcast(networkInfo);
+                case EVENT_INET_CONDITION_CHANGE:
+                {
+                    int netType = msg.arg1;
+                    int condition = msg.arg2;
+                    handleInetConditionChange(netType, condition);
                     break;
+                }
+                case EVENT_INET_CONDITION_HOLD_END:
+                {
+                    int netType = msg.arg1;
+                    int sequence = msg.arg2;
+                    handleInetConditionHoldEnd(netType, sequence);
+                    break;
+                }
+                case EVENT_SET_NETWORK_PREFERENCE:
+                {
+                    int preference = msg.arg1;
+                    handleSetNetworkPreference(preference);
+                    break;
+                }
+                case EVENT_SET_BACKGROUND_DATA:
+                {
+                    boolean enabled = (msg.arg1 == ENABLED);
+                    handleSetBackgroundData(enabled);
+                    break;
+                }
+                case EVENT_SET_MOBILE_DATA:
+                {
+                    boolean enabled = (msg.arg1 == ENABLED);
+                    handleSetMobileData(enabled);
+                    break;
+                }
             }
         }
     }
@@ -1893,7 +1941,7 @@
             mNetTransitionWakeLockCausedBy = forWhom;
         }
         mHandler.sendMessageDelayed(mHandler.obtainMessage(
-                NetworkStateTracker.EVENT_CLEAR_NET_TRANSITION_WAKELOCK,
+                EVENT_CLEAR_NET_TRANSITION_WAKELOCK,
                 mNetTransitionWakeLockSerialNumber, 0),
                 mNetTransitionWakeLockTimeout);
         return;
@@ -1918,6 +1966,72 @@
             }
         }
         mHandler.sendMessage(mHandler.obtainMessage(
-            NetworkStateTracker.EVENT_INET_CONDITION_CHANGE, networkType, percentage));
+            EVENT_INET_CONDITION_CHANGE, networkType, percentage));
+    }
+
+    private void handleInetConditionChange(int netType, int condition) {
+        if (DBG) {
+            Slog.d(TAG, "Inet connectivity change, net=" +
+                    netType + ", condition=" + condition +
+                    ",mActiveDefaultNetwork=" + mActiveDefaultNetwork);
+        }
+        if (mActiveDefaultNetwork == -1) {
+            if (DBG) Slog.d(TAG, "no active default network - aborting");
+            return;
+        }
+        if (mActiveDefaultNetwork != netType) {
+            if (DBG) Slog.d(TAG, "given net not default - aborting");
+            return;
+        }
+        mDefaultInetCondition = condition;
+        int delay;
+        if (mInetConditionChangeInFlight == false) {
+            if (DBG) Slog.d(TAG, "starting a change hold");
+            // setup a new hold to debounce this
+            if (mDefaultInetCondition > 50) {
+                delay = Settings.Secure.getInt(mContext.getContentResolver(),
+                        Settings.Secure.INET_CONDITION_DEBOUNCE_UP_DELAY, 500);
+            } else {
+                delay = Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.INET_CONDITION_DEBOUNCE_DOWN_DELAY, 3000);
+            }
+            mInetConditionChangeInFlight = true;
+            mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_INET_CONDITION_HOLD_END,
+                    mActiveDefaultNetwork, mDefaultConnectionSequence), delay);
+        } else {
+            // we've set the new condition, when this hold ends that will get
+            // picked up
+            if (DBG) Slog.d(TAG, "currently in hold - not setting new end evt");
+        }
+    }
+
+    private void handleInetConditionHoldEnd(int netType, int sequence) {
+        if (DBG) {
+            Slog.d(TAG, "Inet hold end, net=" + netType +
+                    ", condition =" + mDefaultInetCondition +
+                    ", published condition =" + mDefaultInetConditionPublished);
+        }
+        mInetConditionChangeInFlight = false;
+
+        if (mActiveDefaultNetwork == -1) {
+            if (DBG) Slog.d(TAG, "no active default network - aborting");
+            return;
+        }
+        if (mDefaultConnectionSequence != sequence) {
+            if (DBG) Slog.d(TAG, "event hold for obsolete network - aborting");
+            return;
+        }
+        if (mDefaultInetConditionPublished == mDefaultInetCondition) {
+            if (DBG) Slog.d(TAG, "no change in condition - aborting");
+            return;
+        }
+        NetworkInfo networkInfo = mNetTrackers[mActiveDefaultNetwork].getNetworkInfo();
+        if (networkInfo.isConnected() == false) {
+            if (DBG) Slog.d(TAG, "default network not connected - aborting");
+            return;
+        }
+        mDefaultInetConditionPublished = mDefaultInetCondition;
+        sendInetConditionBroadcast(networkInfo);
+        return;
     }
 }
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 3e4f522..7100cc5 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -5687,9 +5687,12 @@
                 int requestedWidth, int requestedHeight, int viewFlags,
                 boolean insetsPending, Rect outFrame, Rect outContentInsets,
                 Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
-            return relayoutWindow(this, window, attrs,
+            //Log.d(TAG, ">>>>>> ENTERED relayout from " + Binder.getCallingPid());
+            int res = relayoutWindow(this, window, attrs,
                     requestedWidth, requestedHeight, viewFlags, insetsPending,
                     outFrame, outContentInsets, outVisibleInsets, outConfig, outSurface);
+            //Log.d(TAG, "<<<<<< EXITING relayout to " + Binder.getCallingPid());
+            return res;
         }
 
         public void setTransparentRegion(IWindow window, Region region) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 8e22652..9685fb7 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -566,6 +566,11 @@
     private final StringBuilder mStrictModeBuffer = new StringBuilder();
 
     /**
+     * True if we have a pending unexpired BROADCAST_TIMEOUT_MSG posted to our handler.
+     */
+    private boolean mPendingBroadcastTimeoutMessage;
+
+    /**
      * Intent broadcast that we have tried to start, but are
      * waiting for its application's process to be created.  We only
      * need one (instead of a list) because we always process broadcasts
@@ -1071,17 +1076,8 @@
                 processNextBroadcast(true);
             } break;
             case BROADCAST_TIMEOUT_MSG: {
-                if (mDidDexOpt) {
-                    mDidDexOpt = false;
-                    Message nmsg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG);
-                    mHandler.sendMessageDelayed(nmsg, BROADCAST_TIMEOUT);
-                    return;
-                }
-                // Only process broadcast timeouts if the system is ready. That way
-                // PRE_BOOT_COMPLETED broadcasts can't timeout as they are intended
-                // to do heavy lifting for system up
-                if (mProcessesReady) {
-                    broadcastTimeout();
+                synchronized (ActivityManagerService.this) {
+                    broadcastTimeoutLocked(true);
                 }
             } break;
             case SERVICE_TIMEOUT_MSG: {
@@ -2810,6 +2806,21 @@
         }
     }
 
+    private final class AppNotResponding implements Runnable {
+        private final ProcessRecord mApp;
+        private final String mAnnotation;
+
+        public AppNotResponding(ProcessRecord app, String annotation) {
+            mApp = app;
+            mAnnotation = annotation;
+        }
+
+        @Override
+        public void run() {
+            appNotResponding(mApp, null, null, mAnnotation);
+        }
+    }
+
     final void appNotResponding(ProcessRecord app, ActivityRecord activity,
             ActivityRecord parent, final String annotation) {
         ArrayList<Integer> firstPids = new ArrayList<Integer>(5);
@@ -3678,7 +3689,7 @@
                 Slog.w(TAG, "Exception in new application when starting receiver "
                       + br.curComponent.flattenToShortString(), e);
                 badApp = true;
-                logBroadcastReceiverDiscard(br);
+                logBroadcastReceiverDiscardLocked(br);
                 finishReceiverLocked(br.receiver, br.resultCode, br.resultData,
                         br.resultExtras, br.resultAbort, true);
                 scheduleBroadcastsLocked();
@@ -6406,7 +6417,7 @@
             // The current broadcast is waiting for this app's receiver
             // to be finished.  Looks like that's not going to happen, so
             // let the broadcast continue.
-            logBroadcastReceiverDiscard(r);
+            logBroadcastReceiverDiscardLocked(r);
             finishReceiverLocked(r.receiver, r.resultCode, r.resultData,
                     r.resultExtras, r.resultAbort, true);
             reschedule = true;
@@ -6415,7 +6426,7 @@
         if (r != null && r.curApp == app) {
             if (DEBUG_BROADCAST) Slog.v(TAG,
                     "skip & discard pending app " + r);
-            logBroadcastReceiverDiscard(r);
+            logBroadcastReceiverDiscardLocked(r);
             finishReceiverLocked(r.receiver, r.resultCode, r.resultData,
                     r.resultExtras, r.resultAbort, true);
             reschedule = true;
@@ -10633,7 +10644,7 @@
         Binder.restoreCallingIdentity(origId);
     }
 
-    private final void logBroadcastReceiverDiscard(BroadcastRecord r) {
+    private final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) {
         if (r.nextReceiver > 0) {
             Object curReceiver = r.receivers.get(r.nextReceiver-1);
             if (curReceiver instanceof BroadcastFilter) {
@@ -10661,67 +10672,108 @@
         }
     }
 
-    private final void broadcastTimeout() {
+    private final void setBroadcastTimeoutLocked(long timeoutTime) {
+        if (! mPendingBroadcastTimeoutMessage) {
+            Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG);
+            mHandler.sendMessageAtTime(msg, timeoutTime);
+            mPendingBroadcastTimeoutMessage = true;
+        }
+    }
+
+    private final void cancelBroadcastTimeoutLocked() {
+        if (mPendingBroadcastTimeoutMessage) {
+            mHandler.removeMessages(BROADCAST_TIMEOUT_MSG);
+            mPendingBroadcastTimeoutMessage = false;
+        }
+    }
+
+    private final void broadcastTimeoutLocked(boolean fromMsg) {
+        if (fromMsg) {
+            mPendingBroadcastTimeoutMessage = false;
+        }
+
+        if (mOrderedBroadcasts.size() == 0) {
+            return;
+        }
+
+        long now = SystemClock.uptimeMillis();
+        BroadcastRecord r = mOrderedBroadcasts.get(0);
+        if (fromMsg) {
+            if (mDidDexOpt) {
+                // Delay timeouts until dexopt finishes.
+                mDidDexOpt = false;
+                long timeoutTime = SystemClock.uptimeMillis() + BROADCAST_TIMEOUT;
+                setBroadcastTimeoutLocked(timeoutTime);
+                return;
+            }
+            if (! mProcessesReady) {
+                // Only process broadcast timeouts if the system is ready. That way
+                // PRE_BOOT_COMPLETED broadcasts can't timeout as they are intended
+                // to do heavy lifting for system up.
+                return;
+            }
+
+            long timeoutTime = r.receiverTime + BROADCAST_TIMEOUT;
+            if (timeoutTime > now) {
+                // We can observe premature timeouts because we do not cancel and reset the
+                // broadcast timeout message after each receiver finishes.  Instead, we set up
+                // an initial timeout then kick it down the road a little further as needed
+                // when it expires.
+                if (DEBUG_BROADCAST) Slog.v(TAG,
+                        "Premature timeout @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
+                        + timeoutTime);
+                setBroadcastTimeoutLocked(timeoutTime);
+                return;
+            }
+        }
+
+        Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver
+                + ", started " + (now - r.receiverTime) + "ms ago");
+        r.receiverTime = now;
+        r.anrCount++;
+
+        // Current receiver has passed its expiration date.
+        if (r.nextReceiver <= 0) {
+            Slog.w(TAG, "Timeout on receiver with nextReceiver <= 0");
+            return;
+        }
+
         ProcessRecord app = null;
         String anrMessage = null;
 
-        synchronized (this) {
-            if (mOrderedBroadcasts.size() == 0) {
-                return;
-            }
-            long now = SystemClock.uptimeMillis();
-            BroadcastRecord r = mOrderedBroadcasts.get(0);
-            if ((r.receiverTime+BROADCAST_TIMEOUT) > now) {
-                if (DEBUG_BROADCAST) Slog.v(TAG,
-                        "Premature timeout @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
-                        + (r.receiverTime + BROADCAST_TIMEOUT));
-                Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG);
-                mHandler.sendMessageAtTime(msg, r.receiverTime+BROADCAST_TIMEOUT);
-                return;
-            }
-
-            Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver);
-            r.receiverTime = now;
-            r.anrCount++;
-
-            // Current receiver has passed its expiration date.
-            if (r.nextReceiver <= 0) {
-                Slog.w(TAG, "Timeout on receiver with nextReceiver <= 0");
-                return;
-            }
-
-            Object curReceiver = r.receivers.get(r.nextReceiver-1);
-            Slog.w(TAG, "Receiver during timeout: " + curReceiver);
-            logBroadcastReceiverDiscard(r);
-            if (curReceiver instanceof BroadcastFilter) {
-                BroadcastFilter bf = (BroadcastFilter)curReceiver;
-                if (bf.receiverList.pid != 0
-                        && bf.receiverList.pid != MY_PID) {
-                    synchronized (this.mPidsSelfLocked) {
-                        app = this.mPidsSelfLocked.get(
-                                bf.receiverList.pid);
-                    }
+        Object curReceiver = r.receivers.get(r.nextReceiver-1);
+        Slog.w(TAG, "Receiver during timeout: " + curReceiver);
+        logBroadcastReceiverDiscardLocked(r);
+        if (curReceiver instanceof BroadcastFilter) {
+            BroadcastFilter bf = (BroadcastFilter)curReceiver;
+            if (bf.receiverList.pid != 0
+                    && bf.receiverList.pid != MY_PID) {
+                synchronized (this.mPidsSelfLocked) {
+                    app = this.mPidsSelfLocked.get(
+                            bf.receiverList.pid);
                 }
-            } else {
-                app = r.curApp;
             }
-            
-            if (app != null) {
-                anrMessage = "Broadcast of " + r.intent.toString();
-            }
-
-            if (mPendingBroadcast == r) {
-                mPendingBroadcast = null;
-            }
-
-            // Move on to the next receiver.
-            finishReceiverLocked(r.receiver, r.resultCode, r.resultData,
-                    r.resultExtras, r.resultAbort, true);
-            scheduleBroadcastsLocked();
+        } else {
+            app = r.curApp;
         }
         
+        if (app != null) {
+            anrMessage = "Broadcast of " + r.intent.toString();
+        }
+
+        if (mPendingBroadcast == r) {
+            mPendingBroadcast = null;
+        }
+
+        // Move on to the next receiver.
+        finishReceiverLocked(r.receiver, r.resultCode, r.resultData,
+                r.resultExtras, r.resultAbort, true);
+        scheduleBroadcastsLocked();
+
         if (anrMessage != null) {
-            appNotResponding(app, null, null, anrMessage);
+            // Post the ANR to the handler since we do not want to process ANRs while
+            // potentially holding our lock.
+            mHandler.post(new AppNotResponding(app, anrMessage));
         }
     }
 
@@ -10763,9 +10815,10 @@
 
     }
 
-    static void performReceive(ProcessRecord app, IIntentReceiver receiver,
+    static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
             Intent intent, int resultCode, String data, Bundle extras,
             boolean ordered, boolean sticky) throws RemoteException {
+        // Send the intent to the receiver asynchronously using one-way binder calls.
         if (app != null && app.thread != null) {
             // If we have an app thread, do the call through that so it is
             // correctly ordered with other one-way calls.
@@ -10776,7 +10829,7 @@
         }
     }
     
-    private final void deliverToRegisteredReceiver(BroadcastRecord r,
+    private final void deliverToRegisteredReceiverLocked(BroadcastRecord r,
             BroadcastFilter filter, boolean ordered) {
         boolean skip = false;
         if (filter.requiredPermission != null) {
@@ -10834,7 +10887,7 @@
                     Slog.i(TAG, "Delivering to " + filter
                             + " (seq=" + seq + "): " + r);
                 }
-                performReceive(filter.receiverList.app, filter.receiverList.receiver,
+                performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
                     new Intent(r.intent), r.resultCode,
                     r.resultData, r.resultExtras, r.ordered, r.initialSticky);
                 if (ordered) {
@@ -10891,7 +10944,7 @@
                     if (DEBUG_BROADCAST)  Slog.v(TAG,
                             "Delivering non-ordered to registered "
                             + target + ": " + r);
-                    deliverToRegisteredReceiver(r, (BroadcastFilter)target, false);
+                    deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
                 }
                 addBroadcastToHistoryLocked(r);
                 if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Done with parallel broadcast "
@@ -10947,7 +11000,7 @@
                 // and continue to make progress.
                 //
                 // This is only done if the system is ready so that PRE_BOOT_COMPLETED
-                // receivers don't get executed with with timeouts. They're intended for
+                // receivers don't get executed with timeouts. They're intended for
                 // one time heavy lifting after system upgrades and can take
                 // significant amounts of time.
                 int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
@@ -10963,7 +11016,7 @@
                                 + " numReceivers=" + numReceivers
                                 + " nextReceiver=" + r.nextReceiver
                                 + " state=" + r.state);
-                        broadcastTimeout(); // forcibly finish this broadcast
+                        broadcastTimeoutLocked(false); // forcibly finish this broadcast
                         forceReceive = true;
                         r.state = BroadcastRecord.IDLE;
                     }
@@ -10987,7 +11040,7 @@
                                 Slog.i(TAG, "Finishing broadcast " + r.intent.getAction()
                                         + " seq=" + seq + " app=" + r.callerApp);
                             }
-                            performReceive(r.callerApp, r.resultTo,
+                            performReceiveLocked(r.callerApp, r.resultTo,
                                 new Intent(r.intent), r.resultCode,
                                 r.resultData, r.resultExtras, false, false);
                         } catch (RemoteException e) {
@@ -10996,7 +11049,7 @@
                     }
                     
                     if (DEBUG_BROADCAST) Slog.v(TAG, "Cancelling BROADCAST_TIMEOUT_MSG");
-                    mHandler.removeMessages(BROADCAST_TIMEOUT_MSG);
+                    cancelBroadcastTimeoutLocked();
 
                     if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Finished with ordered broadcast "
                             + r);
@@ -11021,11 +11074,12 @@
 
                 if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing ordered broadcast "
                         + r);
+            }
+            if (! mPendingBroadcastTimeoutMessage) {
+                long timeoutTime = r.receiverTime + BROADCAST_TIMEOUT;
                 if (DEBUG_BROADCAST) Slog.v(TAG,
-                        "Submitting BROADCAST_TIMEOUT_MSG for " + r + " at "
-                        + (r.receiverTime + BROADCAST_TIMEOUT));
-                Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG);
-                mHandler.sendMessageAtTime(msg, r.receiverTime+BROADCAST_TIMEOUT);
+                        "Submitting BROADCAST_TIMEOUT_MSG for " + r + " at " + timeoutTime);
+                setBroadcastTimeoutLocked(timeoutTime);
             }
 
             Object nextReceiver = r.receivers.get(recIdx);
@@ -11036,7 +11090,7 @@
                 if (DEBUG_BROADCAST)  Slog.v(TAG,
                         "Delivering ordered to registered "
                         + filter + ": " + r);
-                deliverToRegisteredReceiver(r, filter, r.ordered);
+                deliverToRegisteredReceiverLocked(r, filter, r.ordered);
                 if (r.receiver == null || !r.ordered) {
                     // The receiver has already finished, so schedule to
                     // process the next one.
@@ -11144,7 +11198,7 @@
                         + info.activityInfo.applicationInfo.packageName + "/"
                         + info.activityInfo.applicationInfo.uid + " for broadcast "
                         + r.intent + ": process is bad");
-                logBroadcastReceiverDiscard(r);
+                logBroadcastReceiverDiscardLocked(r);
                 finishReceiverLocked(r.receiver, r.resultCode, r.resultData,
                         r.resultExtras, r.resultAbort, true);
                 scheduleBroadcastsLocked();
diff --git a/services/java/com/android/server/sip/SipSessionGroup.java b/services/java/com/android/server/sip/SipSessionGroup.java
index 66a2c05..fa3f64a 100644
--- a/services/java/com/android/server/sip/SipSessionGroup.java
+++ b/services/java/com/android/server/sip/SipSessionGroup.java
@@ -84,7 +84,7 @@
     private static final String ANONYMOUS = "anonymous";
     private static final String SERVER_ERROR_PREFIX = "Response: ";
     private static final int EXPIRY_TIME = 3600; // in seconds
-    private static final int CANCEL_CALL_TIMER = 5; // in seconds
+    private static final int CANCEL_CALL_TIMER = 3; // in seconds
 
     private static final EventObject DEREGISTER = new EventObject("Deregister");
     private static final EventObject END_CALL = new EventObject("End call");
diff --git a/services/surfaceflinger/GLExtensions.cpp b/services/surfaceflinger/GLExtensions.cpp
index 850866a..493122d 100644
--- a/services/surfaceflinger/GLExtensions.cpp
+++ b/services/surfaceflinger/GLExtensions.cpp
@@ -92,6 +92,10 @@
         // hack for Adreno 200
         mHaveTextureExternal = true;
     }
+
+    if (hasExtension("GL_OES_framebuffer_object")) {
+        mHaveFramebufferObject = true;
+    }
 }
 
 bool GLExtensions::hasExtension(char const* extension) const
diff --git a/services/surfaceflinger/GLExtensions.h b/services/surfaceflinger/GLExtensions.h
index bbb284e..c86c66a 100644
--- a/services/surfaceflinger/GLExtensions.h
+++ b/services/surfaceflinger/GLExtensions.h
@@ -39,6 +39,7 @@
     bool mHaveTextureExternal   : 1;
     bool mHaveNpot              : 1;
     bool mHaveDirectTexture     : 1;
+    bool mHaveFramebufferObject : 1;
 
     String8 mVendor;
     String8 mRenderer;
@@ -66,6 +67,10 @@
         return mHaveDirectTexture;
     }
 
+    inline bool haveFramebufferObject() const {
+        return mHaveFramebufferObject;
+    }
+
     void initWithGLStrings(
             GLubyte const* vendor,
             GLubyte const* renderer,
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index b353bff..17b98a6 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -76,6 +76,7 @@
         mBootTime(systemTime()),
         mHardwareTest("android.permission.HARDWARE_TEST"),
         mAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER"),
+        mReadFramebuffer("android.permission.READ_FRAME_BUFFER"),
         mDump("android.permission.DUMP"),
         mVisibleRegionsDirty(false),
         mHwWorkListDirty(false),
@@ -1567,8 +1568,23 @@
                         "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
                 return PERMISSION_DENIED;
             }
+            break;
+        }
+        case CAPTURE_SCREEN:
+        {
+            // codes that require permission check
+            IPCThreadState* ipc = IPCThreadState::self();
+            const int pid = ipc->getCallingPid();
+            const int uid = ipc->getCallingUid();
+            if ((uid != AID_GRAPHICS) && !mReadFramebuffer.check(pid, uid)) {
+                LOGE("Permission Denial: "
+                        "can't read framebuffer pid=%d, uid=%d", pid, uid);
+                return PERMISSION_DENIED;
+            }
+            break;
         }
     }
+
     status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags);
     if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) {
         CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -1637,6 +1653,139 @@
 
 // ---------------------------------------------------------------------------
 
+status_t SurfaceFlinger::captureScreen(DisplayID dpy,
+        sp<IMemoryHeap>* heap,
+        uint32_t* width, uint32_t* height, PixelFormat* format)
+{
+    // only one display supported for now
+    if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
+        return BAD_VALUE;
+
+    if (!GLExtensions::getInstance().haveFramebufferObject())
+        return INVALID_OPERATION;
+
+    class MessageCaptureScreen : public MessageBase {
+        SurfaceFlinger* flinger;
+        DisplayID dpy;
+        sp<IMemoryHeap>* heap;
+        uint32_t* w;
+        uint32_t* h;
+        PixelFormat* f;
+        status_t result;
+    public:
+        MessageCaptureScreen(SurfaceFlinger* flinger, DisplayID dpy,
+                sp<IMemoryHeap>* heap, uint32_t* w, uint32_t* h, PixelFormat* f)
+            : flinger(flinger), dpy(dpy),
+              heap(heap), w(w), h(h), f(f), result(PERMISSION_DENIED)
+        {
+        }
+        status_t getResult() const {
+            return result;
+        }
+        virtual bool handler() {
+            Mutex::Autolock _l(flinger->mStateLock);
+
+            // if we have secure windows, never allow the screen capture
+            if (flinger->mSecureFrameBuffer)
+                return true;
+
+            // make sure to clear all GL error flags
+            while ( glGetError() != GL_NO_ERROR ) ;
+
+            // get screen geometry
+            const DisplayHardware& hw(flinger->graphicPlane(dpy).displayHardware());
+            const uint32_t sw = hw.getWidth();
+            const uint32_t sh = hw.getHeight();
+            const Region screenBounds(hw.bounds());
+            const size_t size = sw * sh * 4;
+
+            // create a FBO
+            GLuint name, tname;
+            glGenRenderbuffersOES(1, &tname);
+            glBindRenderbufferOES(GL_RENDERBUFFER_OES, tname);
+            glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_RGBA8_OES, sw, sh);
+            glGenFramebuffersOES(1, &name);
+            glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
+            glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
+                    GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname);
+
+            GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
+            if (status == GL_FRAMEBUFFER_COMPLETE_OES) {
+
+                // invert everything, b/c glReadPixel() below will invert the FB
+                glMatrixMode(GL_PROJECTION);
+                glPushMatrix();
+                glLoadIdentity();
+                glOrthof(0, sw, 0, sh, 0, 1);
+                glMatrixMode(GL_MODELVIEW);
+
+                // redraw the screen entirely...
+                glClearColor(0,0,0,1);
+                glClear(GL_COLOR_BUFFER_BIT);
+                const Vector< sp<LayerBase> >& layers(
+                        flinger->mVisibleLayersSortedByZ);
+                const size_t count = layers.size();
+                for (size_t i=0 ; i<count ; ++i) {
+                    const sp<LayerBase>& layer(layers[i]);
+                    if (!strcmp(layer->getTypeId(), "LayerBuffer")) {
+                        // we cannot render LayerBuffer because it doens't
+                        // use OpenGL, and won't show-up in the FBO.
+                        continue;
+                    }
+                    layer->draw(screenBounds);
+                }
+
+                glMatrixMode(GL_PROJECTION);
+                glPopMatrix();
+                glMatrixMode(GL_MODELVIEW);
+
+                // check for errors and return screen capture
+                if (glGetError() != GL_NO_ERROR) {
+                    // error while rendering
+                    result = INVALID_OPERATION;
+                } else {
+                    // allocate shared memory large enough to hold the
+                    // screen capture
+                    sp<MemoryHeapBase> base(
+                            new MemoryHeapBase(size, 0, "screen-capture") );
+                    void* const ptr = base->getBase();
+                    if (ptr) {
+                        // capture the screen with glReadPixels()
+                        glReadPixels(0, 0, sw, sh, GL_RGBA, GL_UNSIGNED_BYTE, ptr);
+                        if (glGetError() == GL_NO_ERROR) {
+                            *heap = base;
+                            *w = sw;
+                            *h = sh;
+                            *f = PIXEL_FORMAT_RGBA_8888;
+                            result = NO_ERROR;
+                        }
+                    } else {
+                        result = NO_MEMORY;
+                    }
+                }
+            } else {
+                result = BAD_VALUE;
+            }
+
+            // release FBO resources
+            glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
+            glDeleteRenderbuffersOES(1, &tname);
+            glDeleteFramebuffersOES(1, &name);
+            return true;
+        }
+    };
+
+    sp<MessageBase> msg = new MessageCaptureScreen(this,
+            dpy, heap, width, height, format);
+    status_t res = postMessageSync(msg);
+    if (res == NO_ERROR) {
+        res = static_cast<MessageCaptureScreen*>( msg.get() )->getResult();
+    }
+    return res;
+}
+
+// ---------------------------------------------------------------------------
+
 sp<Layer> SurfaceFlinger::getLayer(const sp<ISurface>& sur) const
 {
     sp<Layer> result;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 551e8e7..6e9ecbd 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -193,6 +193,11 @@
     virtual status_t                    unfreezeDisplay(DisplayID dpy, uint32_t flags);
     virtual int                         setOrientation(DisplayID dpy, int orientation, uint32_t flags);
     virtual void                        signal() const;
+    virtual status_t                    captureScreen(DisplayID dpy,
+                                                      sp<IMemoryHeap>* heap,
+                                                      uint32_t* width,
+                                                      uint32_t* height,
+                                                      PixelFormat* format);
 
             void                        screenReleased(DisplayID dpy);
             void                        screenAcquired(DisplayID dpy);
@@ -362,6 +367,7 @@
                 nsecs_t                     mBootTime;
                 Permission                  mHardwareTest;
                 Permission                  mAccessSurfaceFlinger;
+                Permission                  mReadFramebuffer;
                 Permission                  mDump;
                 
                 // Can only accessed from the main thread, these members
diff --git a/services/surfaceflinger/tests/screencap/Android.mk b/services/surfaceflinger/tests/screencap/Android.mk
new file mode 100644
index 0000000..1cfb471
--- /dev/null
+++ b/services/surfaceflinger/tests/screencap/Android.mk
@@ -0,0 +1,26 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	screencap.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+	libutils \
+	libbinder \
+	libskia \
+    libui \
+    libsurfaceflinger_client
+
+LOCAL_MODULE:= test-screencap
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_C_INCLUDES += \
+	external/skia/include/core \
+	external/skia/include/effects \
+	external/skia/include/images \
+	external/skia/src/ports \
+	external/skia/include/utils
+
+include $(BUILD_EXECUTABLE)
diff --git a/services/surfaceflinger/tests/screencap/screencap.cpp b/services/surfaceflinger/tests/screencap/screencap.cpp
new file mode 100644
index 0000000..9e893f4
--- /dev/null
+++ b/services/surfaceflinger/tests/screencap/screencap.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      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 <utils/Log.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+
+#include <binder/IMemory.h>
+#include <surfaceflinger/ISurfaceComposer.h>
+
+#include <SkImageEncoder.h>
+#include <SkBitmap.h>
+
+using namespace android;
+
+int main(int argc, char** argv)
+{
+    if (argc != 2) {
+        printf("usage: %s path\n", argv[0]);
+        exit(0);
+    }
+
+    const String16 name("SurfaceFlinger");
+    sp<ISurfaceComposer> composer;
+    getService(name, &composer);
+
+    sp<IMemoryHeap> heap;
+    uint32_t w, h;
+    PixelFormat f;
+    status_t err = composer->captureScreen(0, &heap, &w, &h, &f);
+    if (err != NO_ERROR) {
+        fprintf(stderr, "screen capture failed: %s\n", strerror(-err));
+        exit(0);
+    }
+
+    printf("screen capture success: w=%u, h=%u, pixels=%p\n",
+            w, h, heap->getBase());
+
+    printf("saving file as PNG in %s ...\n", argv[1]);
+
+    SkBitmap b;
+    b.setConfig(SkBitmap::kARGB_8888_Config, w, h);
+    b.setPixels(heap->getBase());
+    SkImageEncoder::EncodeFile(argv[1], b,
+            SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality);
+
+    return 0;
+}
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index 4791fbd..08194d4 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -73,7 +73,9 @@
 public class SipPhone extends SipPhoneBase {
     private static final String LOG_TAG = "SipPhone";
     private static final boolean LOCAL_DEBUG = true;
-    private static final int SESSION_TIMEOUT = 8; // in seconds
+    private static final int TIMEOUT_MAKE_CALL = 15; // in seconds
+    private static final int TIMEOUT_ANSWER_CALL = 8; // in seconds
+    private static final int TIMEOUT_HOLD_CALL = 15; // in seconds
 
     // A call that is ringing or (call) waiting
     private SipCall ringingCall = new SipCall();
@@ -693,7 +695,7 @@
 
         void acceptCall() throws CallStateException {
             try {
-                mSipAudioCall.answerCall(SESSION_TIMEOUT);
+                mSipAudioCall.answerCall(TIMEOUT_ANSWER_CALL);
             } catch (SipException e) {
                 throw new CallStateException("acceptCall(): " + e);
             }
@@ -711,7 +713,7 @@
         void dial() throws SipException {
             setState(Call.State.DIALING);
             mSipAudioCall = mSipManager.makeAudioCall(mProfile, mPeer, null,
-                    SESSION_TIMEOUT);
+                    TIMEOUT_MAKE_CALL);
             mSipAudioCall.setRingbackToneEnabled(false);
             mSipAudioCall.setListener(mAdapter);
         }
@@ -719,7 +721,7 @@
         void hold() throws CallStateException {
             setState(Call.State.HOLDING);
             try {
-                mSipAudioCall.holdCall(SESSION_TIMEOUT);
+                mSipAudioCall.holdCall(TIMEOUT_HOLD_CALL);
             } catch (SipException e) {
                 throw new CallStateException("hold(): " + e);
             }
@@ -729,7 +731,7 @@
             mSipAudioCall.setAudioGroup(audioGroup);
             setState(Call.State.ACTIVE);
             try {
-                mSipAudioCall.continueCall(SESSION_TIMEOUT);
+                mSipAudioCall.continueCall(TIMEOUT_HOLD_CALL);
             } catch (SipException e) {
                 throw new CallStateException("unhold(): " + e);
             }
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/Nullable.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/Nullable.java
new file mode 100755
index 0000000..0689c92
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/Nullable.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      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.tools.layoutlib.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Denotes a parameter or field can be null.
+ * <p/>
+ * When decorating a method call parameter, this denotes the parameter can
+ * legitimately be null and the method will gracefully deal with it. Typically used
+ * on optional parameters.
+ * <p/>
+ * When decorating a method, this denotes the method might legitimately return null.
+ * <p/>
+ * This is a marker annotation and it has no specific attributes.
+ */
+@Retention(RetentionPolicy.SOURCE)
+public @interface Nullable {
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/VisibleForTesting.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/VisibleForTesting.java
new file mode 100755
index 0000000..e4e016b
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/VisibleForTesting.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      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.tools.layoutlib.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Denotes that the class, method or field has its visibility relaxed so
+ * that unit tests can access it.
+ * <p/>
+ * The <code>visibility</code> argument can be used to specific what the original
+ * visibility should have been if it had not been made public or package-private for testing.
+ * The default is to consider the element private.
+ */
+@Retention(RetentionPolicy.SOURCE)
+public @interface VisibleForTesting {
+    /**
+     * Intended visibility if the element had not been made public or package-private for
+     * testing.
+     */
+    enum Visibility {
+        /** The element should be considered protected. */
+        PROTECTED,
+        /** The element should be considered package-private. */
+        PACKAGE,
+        /** The element should be considered private. */
+        PRIVATE
+    }
+
+    /**
+     * Intended visibility if the element had not been made public or package-private for testing.
+     * If not specified, one should assume the element originally intended to be private.
+     */
+    Visibility visibility() default Visibility.PRIVATE;
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java
index 5424efa..722dce2 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java
@@ -16,6 +16,9 @@
 
 package com.android.tools.layoutlib.create;
 
+import com.android.tools.layoutlib.annotations.VisibleForTesting;
+import com.android.tools.layoutlib.annotations.VisibleForTesting.Visibility;
+
 import org.objectweb.asm.AnnotationVisitor;
 import org.objectweb.asm.Attribute;
 import org.objectweb.asm.ClassVisitor;
@@ -27,13 +30,18 @@
  * Indicates if a class contains any native methods.
  */
 public class ClassHasNativeVisitor implements ClassVisitor {
-    
+
     private boolean mHasNativeMethods = false;
-    
+
     public boolean hasNativeMethods() {
         return mHasNativeMethods;
     }
 
+    @VisibleForTesting(visibility=Visibility.PRIVATE)
+    protected void setHasNativeMethods(boolean hasNativeMethods, String methodName) {
+        mHasNativeMethods = hasNativeMethods;
+    }
+
     public void visit(int version, int access, String name, String signature,
             String superName, String[] interfaces) {
         // pass
@@ -65,7 +73,9 @@
 
     public MethodVisitor visitMethod(int access, String name, String desc,
             String signature, String[] exceptions) {
-        mHasNativeMethods |= ((access & Opcodes.ACC_NATIVE) != 0);
+        if ((access & Opcodes.ACC_NATIVE) != 0) {
+            setHasNativeMethods(true, name);
+        }
         return null;
     }
 
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java
new file mode 100644
index 0000000..d6916ae
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      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.tools.layoutlib.create;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.objectweb.asm.ClassReader;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+
+/**
+ * Tests {@link ClassHasNativeVisitor}.
+ */
+public class ClassHasNativeVisitorTest {
+
+    @Test
+    public void testHasNative() throws IOException {
+        MockClassHasNativeVisitor cv = new MockClassHasNativeVisitor();
+        ClassReader cr = new ClassReader(
+                "com.android.tools.layoutlib.create.ClassHasNativeVisitorTest$ClassWithNative");
+
+        cr.accept(cv, 0 /* flags */);
+        assertArrayEquals(new String[] { "native_method" }, cv.getMethodsFound());
+        assertTrue(cv.hasNativeMethods());
+    }
+
+    @Test
+    public void testHasNoNative() throws IOException {
+        MockClassHasNativeVisitor cv = new MockClassHasNativeVisitor();
+        ClassReader cr = new ClassReader(
+                "com.android.tools.layoutlib.create.ClassHasNativeVisitorTest$ClassWithoutNative");
+
+        cr.accept(cv, 0 /* flags */);
+        assertArrayEquals(new String[0], cv.getMethodsFound());
+        assertFalse(cv.hasNativeMethods());
+    }
+
+    /**
+     * Overrides {@link ClassHasNativeVisitor} to collec the name of the native methods found.
+     */
+    private static class MockClassHasNativeVisitor extends ClassHasNativeVisitor {
+        private ArrayList<String> mMethodsFound = new ArrayList<String>();
+
+        public String[] getMethodsFound() {
+            return mMethodsFound.toArray(new String[mMethodsFound.size()]);
+        }
+
+        @Override
+        protected void setHasNativeMethods(boolean hasNativeMethods, String methodName) {
+            if (hasNativeMethods) {
+                mMethodsFound.add(methodName);
+            }
+            super.setHasNativeMethods(hasNativeMethods, methodName);
+        }
+    }
+
+    /**
+     * Dummy test class with a native method.
+     */
+    public static class ClassWithNative {
+        public ClassWithNative() {
+        }
+
+        public void callTheNativeMethod() {
+            native_method();
+        }
+
+        private native void native_method();
+    }
+
+    /**
+     * Dummy test class with no native method.
+     */
+    public static class ClassWithoutNative {
+        public ClassWithoutNative() {
+        }
+
+        public void someMethod() {
+        }
+    }
+}