Merge "Move the IME navigation guard view up to decor." into klp-dev
diff --git a/Android.mk b/Android.mk
index 166db4ee..4e5427a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -165,6 +165,7 @@
 	core/java/android/print/ILayoutResultCallback.aidl \
 	core/java/android/print/IPrinterDiscoveryObserver.aidl \
 	core/java/android/print/IPrintDocumentAdapter.aidl \
+	core/java/android/print/IPrintDocumentAdapterObserver.aidl \
 	core/java/android/print/IPrintJobStateChangeListener.aidl \
 	core/java/android/print/IPrintManager.aidl \
 	core/java/android/print/IPrintSpooler.aidl \
diff --git a/core/java/android/print/IPrintDocumentAdapter.aidl b/core/java/android/print/IPrintDocumentAdapter.aidl
index b12c922..9d384fb 100644
--- a/core/java/android/print/IPrintDocumentAdapter.aidl
+++ b/core/java/android/print/IPrintDocumentAdapter.aidl
@@ -19,6 +19,7 @@
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.print.ILayoutResultCallback;
+import android.print.IPrintDocumentAdapterObserver;
 import android.print.IWriteResultCallback;
 import android.print.PageRange;
 import android.print.PrintAttributes;
@@ -29,6 +30,7 @@
  * @hide
  */
 oneway interface IPrintDocumentAdapter {
+    void setObserver(in IPrintDocumentAdapterObserver observer);
     void start();
     void layout(in PrintAttributes oldAttributes, in PrintAttributes newAttributes,
             ILayoutResultCallback callback, in Bundle metadata, int sequence);
diff --git a/core/java/android/print/IPrintDocumentAdapterObserver.aidl b/core/java/android/print/IPrintDocumentAdapterObserver.aidl
new file mode 100644
index 0000000..4443df0
--- /dev/null
+++ b/core/java/android/print/IPrintDocumentAdapterObserver.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2013 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.print;
+
+/**
+ * Interface for observing the state of a print document adapter.
+ *
+ * @hide
+ */
+oneway interface IPrintDocumentAdapterObserver {
+    void onDestroy();
+}
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java
index dbd8278..955b4d8 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -16,6 +16,8 @@
 
 package android.print;
 
+import android.app.Activity;
+import android.app.Application.ActivityLifecycleCallbacks;
 import android.content.Context;
 import android.content.IntentSender;
 import android.content.IntentSender.SendIntentException;
@@ -302,8 +304,8 @@
         if (TextUtils.isEmpty(printJobName)) {
             throw new IllegalArgumentException("priintJobName cannot be empty");
         }
-        PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate(documentAdapter,
-                mContext.getMainLooper());
+        PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate(
+                mContext, documentAdapter);
         try {
             Bundle result = mService.print(printJobName, delegate,
                     attributes, mContext.getPackageName(), mAppId, mUserId);
@@ -369,17 +371,21 @@
         return new PrinterDiscoverySession(mService, mContext, mUserId);
     }
 
-    private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub {
+    private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub
+            implements ActivityLifecycleCallbacks {
 
         private final Object mLock = new Object();
 
         private CancellationSignal mLayoutOrWriteCancellation;
 
-        private PrintDocumentAdapter mDocumentAdapter; // Strong reference OK -
-                                                       // cleared in finish()
+        private Activity mActivity; // Strong reference OK - cleared in finish()
+
+        private PrintDocumentAdapter mDocumentAdapter; // Strong reference OK - cleared in finish
 
         private Handler mHandler; // Strong reference OK - cleared in finish()
 
+        private IPrintDocumentAdapterObserver mObserver; // Strong reference OK - cleared in finish
+
         private LayoutSpec mLastLayoutSpec;
 
         private WriteSpec mLastWriteSpec;
@@ -390,16 +396,42 @@
         private boolean mFinishRequested;
         private boolean mFinished;
 
-        public PrintDocumentAdapterDelegate(PrintDocumentAdapter documentAdapter, Looper looper) {
+        private boolean mDestroyed;
+
+        public PrintDocumentAdapterDelegate(Context context,
+                PrintDocumentAdapter documentAdapter) {
+            if (!(context instanceof Activity)) {
+                throw new IllegalStateException("Can print only from an activity");
+            }
+            mActivity = (Activity) context;
             mDocumentAdapter = documentAdapter;
-            mHandler = new MyHandler(looper);
+            mHandler = new MyHandler(mActivity.getMainLooper());
+            mActivity.getApplication().registerActivityLifecycleCallbacks(this);
+        }
+
+        @Override
+        public void setObserver(IPrintDocumentAdapterObserver observer) {
+            final boolean destroyed;
+            synchronized (mLock) {
+                if (!mDestroyed) {
+                    mObserver = observer;
+                }
+                destroyed = mDestroyed;
+            }
+            if (destroyed) {
+                try {
+                    observer.onDestroy();
+                } catch (RemoteException re) {
+                    Log.e(LOG_TAG, "Error announcing destroyed state", re);
+                }
+            }
         }
 
         @Override
         public void start() {
             synchronized (mLock) {
-                // Started or finished - nothing to do.
-                if (mStartReqeusted || mFinishRequested) {
+                // Started called or finish called or destroyed - nothing to do.
+                if (mStartReqeusted || mFinishRequested || mDestroyed) {
                     return;
                 }
 
@@ -412,71 +444,85 @@
         @Override
         public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
                 ILayoutResultCallback callback, Bundle metadata, int sequence) {
+            final boolean destroyed;
             synchronized (mLock) {
-                // Start not called or finish called - nothing to do.
-                if (!mStartReqeusted || mFinishRequested) {
-                    return;
+                destroyed = mDestroyed;
+                // If start called and not finished called and not destroyed - do some work.
+                if (mStartReqeusted && !mFinishRequested && !mDestroyed) {
+                    // Layout cancels write and overrides layout.
+                    if (mLastWriteSpec != null) {
+                        IoUtils.closeQuietly(mLastWriteSpec.fd);
+                        mLastWriteSpec = null;
+                    }
+
+                    mLastLayoutSpec = new LayoutSpec();
+                    mLastLayoutSpec.callback = callback;
+                    mLastLayoutSpec.oldAttributes = oldAttributes;
+                    mLastLayoutSpec.newAttributes = newAttributes;
+                    mLastLayoutSpec.metadata = metadata;
+                    mLastLayoutSpec.sequence = sequence;
+
+                    // Cancel the previous cancellable operation.When the
+                    // cancellation completes we will do the pending work.
+                    if (cancelPreviousCancellableOperationLocked()) {
+                        return;
+                    }
+
+                    doPendingWorkLocked();
                 }
-
-                // Layout cancels write and overrides layout.
-                if (mLastWriteSpec != null) {
-                    IoUtils.closeQuietly(mLastWriteSpec.fd);
-                    mLastWriteSpec = null;
+            }
+            if (destroyed) {
+                try {
+                    callback.onLayoutFailed(null, sequence);
+                } catch (RemoteException re) {
+                    Log.i(LOG_TAG, "Error notifying for cancelled layout", re);
                 }
-
-                mLastLayoutSpec = new LayoutSpec();
-                mLastLayoutSpec.callback = callback;
-                mLastLayoutSpec.oldAttributes = oldAttributes;
-                mLastLayoutSpec.newAttributes = newAttributes;
-                mLastLayoutSpec.metadata = metadata;
-                mLastLayoutSpec.sequence = sequence;
-
-                // Cancel the previous cancellable operation.When the
-                // cancellation completes we will do the pending work.
-                if (cancelPreviousCancellableOperationLocked()) {
-                    return;
-                }
-
-                doPendingWorkLocked();
             }
         }
 
         @Override
         public void write(PageRange[] pages, ParcelFileDescriptor fd,
                 IWriteResultCallback callback, int sequence) {
+            final boolean destroyed;
             synchronized (mLock) {
-                // Start not called or finish called - nothing to do.
-                if (!mStartReqeusted || mFinishRequested) {
-                    return;
+                destroyed = mDestroyed;
+                // If start called and not finished called and not destroyed - do some work.
+                if (mStartReqeusted && !mFinishRequested && !mDestroyed) {
+                    // Write cancels previous writes.
+                    if (mLastWriteSpec != null) {
+                        IoUtils.closeQuietly(mLastWriteSpec.fd);
+                        mLastWriteSpec = null;
+                    }
+
+                    mLastWriteSpec = new WriteSpec();
+                    mLastWriteSpec.callback = callback;
+                    mLastWriteSpec.pages = pages;
+                    mLastWriteSpec.fd = fd;
+                    mLastWriteSpec.sequence = sequence;
+
+                    // Cancel the previous cancellable operation.When the
+                    // cancellation completes we will do the pending work.
+                    if (cancelPreviousCancellableOperationLocked()) {
+                        return;
+                    }
+
+                    doPendingWorkLocked();
                 }
-
-                // Write cancels previous writes.
-                if (mLastWriteSpec != null) {
-                    IoUtils.closeQuietly(mLastWriteSpec.fd);
-                    mLastWriteSpec = null;
+            }
+            if (destroyed) {
+                try {
+                    callback.onWriteFailed(null, sequence);
+                } catch (RemoteException re) {
+                    Log.i(LOG_TAG, "Error notifying for cancelled write", re);
                 }
-
-                mLastWriteSpec = new WriteSpec();
-                mLastWriteSpec.callback = callback;
-                mLastWriteSpec.pages = pages;
-                mLastWriteSpec.fd = fd;
-                mLastWriteSpec.sequence = sequence;
-
-                // Cancel the previous cancellable operation.When the
-                // cancellation completes we will do the pending work.
-                if (cancelPreviousCancellableOperationLocked()) {
-                    return;
-                }
-
-                doPendingWorkLocked();
             }
         }
 
         @Override
         public void finish() {
             synchronized (mLock) {
-                // Start not called or finish called - nothing to do.
-                if (!mStartReqeusted || mFinishRequested) {
+                // Start not called or finish called or destroyed - nothing to do.
+                if (!mStartReqeusted || mFinishRequested || mDestroyed) {
                     return;
                 }
 
@@ -495,15 +541,78 @@
             }
         }
 
+        @Override
+        public void onActivityPaused(Activity activity) {
+            /* do nothing */
+        }
+
+        @Override
+        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+            /* do nothing */
+        }
+
+        @Override
+        public void onActivityStarted(Activity activity) {
+            /* do nothing */
+        }
+
+        @Override
+        public void onActivityResumed(Activity activity) {
+            /* do nothing */
+        }
+
+        @Override
+        public void onActivityStopped(Activity activity) {
+            /* do nothing */
+        }
+
+        @Override
+        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
+            /* do nothing */
+        }
+
+        @Override
+        public void onActivityDestroyed(Activity activity) {
+            // We really care only if the activity is being destroyed to
+            // notify the the print spooler so it can close the print dialog.
+            // Note the the spooler has a death recipient that observes if
+            // this process gets killed so we cover the case of onDestroy not
+            // being called due to this process being killed to reclaim memory.
+            final IPrintDocumentAdapterObserver observer;
+            synchronized (mLock) {
+                if (activity == mActivity) {
+                    mDestroyed = true;
+                    observer = mObserver;
+                    clearLocked();
+                } else {
+                    observer = null;
+                    activity = null;
+                }
+            }
+            if (observer != null) {
+                activity.getApplication().unregisterActivityLifecycleCallbacks(
+                        PrintDocumentAdapterDelegate.this);
+                try {
+                    observer.onDestroy();
+                } catch (RemoteException re) {
+                    Log.e(LOG_TAG, "Error announcing destroyed state", re);
+                }
+            }
+        }
+
         private boolean isFinished() {
             return mDocumentAdapter == null;
         }
 
-        private void doFinish() {
+        private void clearLocked() {
+            mActivity = null;
             mDocumentAdapter = null;
             mHandler = null;
-            synchronized (mLock) {
-                mLayoutOrWriteCancellation = null;
+            mLayoutOrWriteCancellation = null;
+            mLastLayoutSpec = null;
+            if (mLastWriteSpec != null) {
+                IoUtils.closeQuietly(mLastWriteSpec.fd);
+                mLastWriteSpec = null;
             }
         }
 
@@ -564,63 +673,81 @@
                 }
                 switch (message.what) {
                     case MSG_START: {
-                        mDocumentAdapter.onStart();
-                    }
-                        break;
+                        final PrintDocumentAdapter adapter;
+                        synchronized (mLock) {
+                            adapter = mDocumentAdapter;
+                        }
+                        if (adapter != null) {
+                            adapter.onStart();
+                        }
+                    } break;
 
                     case MSG_LAYOUT: {
+                        final PrintDocumentAdapter adapter;
                         final CancellationSignal cancellation;
                         final LayoutSpec layoutSpec;
 
                         synchronized (mLock) {
+                            adapter = mDocumentAdapter;
                             layoutSpec = mLastLayoutSpec;
                             mLastLayoutSpec = null;
                             cancellation = new CancellationSignal();
                             mLayoutOrWriteCancellation = cancellation;
                         }
 
-                        if (layoutSpec != null) {
+                        if (layoutSpec != null && adapter != null) {
                             if (DEBUG) {
                                 Log.i(LOG_TAG, "Performing layout");
                             }
-                            mDocumentAdapter.onLayout(layoutSpec.oldAttributes,
+                            adapter.onLayout(layoutSpec.oldAttributes,
                                     layoutSpec.newAttributes, cancellation,
                                     new MyLayoutResultCallback(layoutSpec.callback,
                                             layoutSpec.sequence), layoutSpec.metadata);
                         }
-                    }
-                        break;
+                    } break;
 
                     case MSG_WRITE: {
+                        final PrintDocumentAdapter adapter;
                         final CancellationSignal cancellation;
                         final WriteSpec writeSpec;
 
                         synchronized (mLock) {
+                            adapter = mDocumentAdapter;
                             writeSpec = mLastWriteSpec;
                             mLastWriteSpec = null;
                             cancellation = new CancellationSignal();
                             mLayoutOrWriteCancellation = cancellation;
                         }
 
-                        if (writeSpec != null) {
+                        if (writeSpec != null && adapter != null) {
                             if (DEBUG) {
                                 Log.i(LOG_TAG, "Performing write");
                             }
-                            mDocumentAdapter.onWrite(writeSpec.pages, writeSpec.fd,
+                            adapter.onWrite(writeSpec.pages, writeSpec.fd,
                                     cancellation, new MyWriteResultCallback(writeSpec.callback,
                                             writeSpec.fd, writeSpec.sequence));
                         }
-                    }
-                        break;
+                    } break;
 
                     case MSG_FINISH: {
                         if (DEBUG) {
                             Log.i(LOG_TAG, "Performing finish");
                         }
-                        mDocumentAdapter.onFinish();
-                        doFinish();
-                    }
-                        break;
+                        final PrintDocumentAdapter adapter;
+                        final Activity activity;
+                        synchronized (mLock) {
+                            adapter = mDocumentAdapter;
+                            activity = mActivity;
+                            clearLocked();
+                        }
+                        if (adapter != null) {
+                            adapter.onFinish();
+                        }
+                        if (activity != null) {
+                            activity.getApplication().unregisterActivityLifecycleCallbacks(
+                                    PrintDocumentAdapterDelegate.this);
+                        }
+                    } break;
 
                     default: {
                         throw new IllegalArgumentException("Unknown message: "
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 9fc37cf..4f53c1e 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -2540,7 +2540,7 @@
 
     /**
      * Class with information if a node is a range. Use
-     * {@link RangeInfo#obtain(int, float, float, float) to get an instance.
+     * {@link RangeInfo#obtain(int, float, float, float)} to get an instance.
      */
     public static final class RangeInfo {
         private static final int MAX_POOL_SIZE = 10;
diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java
new file mode 100644
index 0000000..bbd3f2b
--- /dev/null
+++ b/core/java/android/webkit/CacheManager.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.content.Context;
+import android.net.http.Headers;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Map;
+
+
+/**
+ * Manages the HTTP cache used by an application's {@link WebView} instances.
+ * @deprecated Access to the HTTP cache will be removed in a future release.
+ * @hide Since {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
+ */
+// The class CacheManager provides the persistent cache of content that is
+// received over the network. The component handles parsing of HTTP headers and
+// utilizes the relevant cache headers to determine if the content should be
+// stored and if so, how long it is valid for. Network requests are provided to
+// this component and if they can not be resolved by the cache, the HTTP headers
+// are attached, as appropriate, to the request for revalidation of content. The
+// class also manages the cache size.
+//
+// CacheManager may only be used if your activity contains a WebView.
+@Deprecated
+public final class CacheManager {
+    /**
+     * Represents a resource stored in the HTTP cache. Instances of this class
+     * can be obtained by calling
+     * {@link CacheManager#getCacheFile CacheManager.getCacheFile(String, Map<String, String>))}.
+     *
+     * @deprecated Access to the HTTP cache will be removed in a future release.
+     */
+    @Deprecated
+    public static class CacheResult {
+        // these fields are saved to the database
+        int httpStatusCode;
+        long contentLength;
+        long expires;
+        String expiresString;
+        String localPath;
+        String lastModified;
+        String etag;
+        String mimeType;
+        String location;
+        String encoding;
+        String contentdisposition;
+        String crossDomain;
+
+        // these fields are NOT saved to the database
+        InputStream inStream;
+        OutputStream outStream;
+        File outFile;
+
+        /**
+         * Gets the status code of this cache entry.
+         *
+         * @return the status code of this cache entry
+         */
+        public int getHttpStatusCode() {
+            return httpStatusCode;
+        }
+
+        /**
+         * Gets the content length of this cache entry.
+         *
+         * @return the content length of this cache entry
+         */
+        public long getContentLength() {
+            return contentLength;
+        }
+
+        /**
+         * Gets the path of the file used to store the content of this cache
+         * entry, relative to the base directory of the cache. See
+         * {@link CacheManager#getCacheFileBaseDir CacheManager.getCacheFileBaseDir()}.
+         *
+         * @return the path of the file used to store this cache entry
+         */
+        public String getLocalPath() {
+            return localPath;
+        }
+
+        /**
+         * Gets the expiry date of this cache entry, expressed in milliseconds
+         * since midnight, January 1, 1970 UTC.
+         *
+         * @return the expiry date of this cache entry
+         */
+        public long getExpires() {
+            return expires;
+        }
+
+        /**
+         * Gets the expiry date of this cache entry, expressed as a string.
+         *
+         * @return the expiry date of this cache entry
+         *
+         */
+        public String getExpiresString() {
+            return expiresString;
+        }
+
+        /**
+         * Gets the date at which this cache entry was last modified, expressed
+         * as a string.
+         *
+         * @return the date at which this cache entry was last modified
+         */
+        public String getLastModified() {
+            return lastModified;
+        }
+
+        /**
+         * Gets the entity tag of this cache entry.
+         *
+         * @return the entity tag of this cache entry
+         */
+        public String getETag() {
+            return etag;
+        }
+
+        /**
+         * Gets the MIME type of this cache entry.
+         *
+         * @return the MIME type of this cache entry
+         */
+        public String getMimeType() {
+            return mimeType;
+        }
+
+        /**
+         * Gets the value of the HTTP 'Location' header with which this cache
+         * entry was received.
+         *
+         * @return the HTTP 'Location' header for this cache entry
+         */
+        public String getLocation() {
+            return location;
+        }
+
+        /**
+         * Gets the encoding of this cache entry.
+         *
+         * @return the encoding of this cache entry
+         */
+        public String getEncoding() {
+            return encoding;
+        }
+
+        /**
+         * Gets the value of the HTTP 'Content-Disposition' header with which
+         * this cache entry was received.
+         *
+         * @return the HTTP 'Content-Disposition' header for this cache entry
+         *
+         */
+        public String getContentDisposition() {
+            return contentdisposition;
+        }
+
+        /**
+         * Gets the input stream to the content of this cache entry, to allow
+         * content to be read. See
+         * {@link CacheManager#getCacheFile CacheManager.getCacheFile(String, Map<String, String>)}.
+         *
+         * @return an input stream to the content of this cache entry
+         */
+        public InputStream getInputStream() {
+            return inStream;
+        }
+
+        /**
+         * Gets an output stream to the content of this cache entry, to allow
+         * content to be written. See
+         * {@link CacheManager#saveCacheFile CacheManager.saveCacheFile(String, CacheResult)}.
+         *
+         * @return an output stream to the content of this cache entry
+         */
+        // Note that this is always null for objects returned by getCacheFile()!
+        public OutputStream getOutputStream() {
+            return outStream;
+        }
+
+
+        /**
+         * Sets an input stream to the content of this cache entry.
+         *
+         * @param stream an input stream to the content of this cache entry
+         */
+        public void setInputStream(InputStream stream) {
+            this.inStream = stream;
+        }
+
+        /**
+         * Sets the encoding of this cache entry.
+         *
+         * @param encoding the encoding of this cache entry
+         */
+        public void setEncoding(String encoding) {
+            this.encoding = encoding;
+        }
+
+        /**
+         * @hide
+         */
+        public void setContentLength(long contentLength) {
+            this.contentLength = contentLength;
+        }
+    }
+
+    /**
+     * Gets the base directory in which the files used to store the contents of
+     * cache entries are placed. See
+     * {@link CacheManager.CacheResult#getLocalPath CacheManager.CacheResult.getLocalPath()}.
+     *
+     * @return the base directory of the cache
+     * @deprecated This method no longer has any effect and always returns null.
+     */
+    @Deprecated
+    public static File getCacheFileBaseDir() {
+        return null;
+    }
+
+    /**
+     * Gets whether the HTTP cache is disabled.
+     *
+     * @return true if the HTTP cache is disabled
+     * @deprecated This method no longer has any effect and always returns false.
+     */
+    @Deprecated
+    public static boolean cacheDisabled() {
+        return false;
+    }
+
+    /**
+     * Starts a cache transaction. Returns true if this is the only running
+     * transaction. Otherwise, this transaction is nested inside currently
+     * running transactions and false is returned.
+     *
+     * @return true if this is the only running transaction
+     * @deprecated This method no longer has any effect and always returns false.
+     */
+    @Deprecated
+    public static boolean startCacheTransaction() {
+        return false;
+    }
+
+    /**
+     * Ends the innermost cache transaction and returns whether this was the
+     * only running transaction.
+     *
+     * @return true if this was the only running transaction
+     * @deprecated This method no longer has any effect and always returns false.
+     */
+    @Deprecated
+    public static boolean endCacheTransaction() {
+        return false;
+    }
+
+    /**
+     * Gets the cache entry for the specified URL, or null if none is found.
+     * If a non-null value is provided for the HTTP headers map, and the cache
+     * entry needs validation, appropriate headers will be added to the map.
+     * The input stream of the CacheEntry object should be closed by the caller
+     * when access to the underlying file is no longer required.
+     *
+     * @param url the URL for which a cache entry is requested
+     * @param headers a map from HTTP header name to value, to be populated
+     *                for the returned cache entry
+     * @return the cache entry for the specified URL
+     * @deprecated This method no longer has any effect and always returns null.
+     */
+    @Deprecated
+    public static CacheResult getCacheFile(String url,
+            Map<String, String> headers) {
+        return null;
+    }
+
+    /**
+     * Adds a cache entry to the HTTP cache for the specicifed URL. Also closes
+     * the cache entry's output stream.
+     *
+     * @param url the URL for which the cache entry should be added
+     * @param cacheResult the cache entry to add
+     * @deprecated Access to the HTTP cache will be removed in a future release.
+     */
+    @Deprecated
+    public static void saveCacheFile(String url, CacheResult cacheResult) {
+        saveCacheFile(url, 0, cacheResult);
+    }
+
+    static void saveCacheFile(String url, long postIdentifier,
+            CacheResult cacheRet) {
+        try {
+            cacheRet.outStream.close();
+        } catch (IOException e) {
+            return;
+        }
+
+        // This method is exposed in the public API but the API provides no
+        // way to obtain a new CacheResult object with a non-null output
+        // stream ...
+        // - CacheResult objects returned by getCacheFile() have a null
+        //   output stream.
+        // - new CacheResult objects have a null output stream and no
+        //   setter is provided.
+        // Since this method throws a null pointer exception in this case,
+        // it is effectively useless from the point of view of the public
+        // API.
+        //
+        // With the Chromium HTTP stack we continue to throw the same
+        // exception for 'backwards compatibility' with the Android HTTP
+        // stack.
+        //
+        // This method is not used from within this package, and for public API
+        // use, we should already have thrown an exception above.
+        assert false;
+    }
+}
diff --git a/core/java/android/webkit/PluginData.java b/core/java/android/webkit/PluginData.java
new file mode 100644
index 0000000..88fc9b7
--- /dev/null
+++ b/core/java/android/webkit/PluginData.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import java.io.InputStream;
+import java.util.Map;
+
+/**
+ * This class encapsulates the content generated by a plugin.  The
+ * data itself is meant to be loaded into webkit via the
+ * PluginContentLoader class, which needs to be able to construct an
+ * HTTP response. For this, it needs a stream with the response body,
+ * the length of the body, the response headers, and the response
+ * status code. The PluginData class is the container for all these
+ * parts.
+ *
+ * @hide
+ * @deprecated This class was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
+ */
+@Deprecated
+public final class PluginData {
+    /**
+     * The content stream.
+     */
+    private InputStream mStream;
+    /**
+     * The content length.
+     */
+    private long mContentLength;
+    /**
+     * The associated HTTP response headers stored as a map of
+     * lowercase header name to [ unmodified header name, header value].
+     * TODO: This design was always a hack. Remove (involves updating
+     * the Gears C++ side).
+     */
+    private Map<String, String[]> mHeaders;
+
+    /**
+     * The associated HTTP response code.
+     */
+    private int mStatusCode;
+
+    /**
+     * Creates a PluginData instance.
+     *
+     * @param stream The stream that supplies content for the plugin.
+     * @param length The length of the plugin content.
+     * @param headers The response headers. Map of
+     * lowercase header name to [ unmodified header name, header value]
+     * @param length The HTTP response status code.
+     *
+     * @hide
+     * @deprecated This class was intended to be used by Gears. Since Gears was
+     * deprecated, so is this class.
+     */
+    @Deprecated
+    public PluginData(
+            InputStream stream,
+            long length,
+            Map<String, String[]> headers,
+            int code) {
+        mStream = stream;
+        mContentLength = length;
+        mHeaders = headers;
+        mStatusCode = code;
+    }
+
+    /**
+     * Returns the input stream that contains the plugin content.
+     *
+     * @return An InputStream instance with the plugin content.
+     *
+     * @hide
+     * @deprecated This class was intended to be used by Gears. Since Gears was
+     * deprecated, so is this class.
+     */
+    @Deprecated
+    public InputStream getInputStream() {
+        return mStream;
+    }
+
+    /**
+     * Returns the length of the plugin content.
+     *
+     * @return the length of the plugin content.
+     *
+     * @hide
+     * @deprecated This class was intended to be used by Gears. Since Gears was
+     * deprecated, so is this class.
+     */
+    @Deprecated
+    public long getContentLength() {
+        return mContentLength;
+    }
+
+    /**
+     * Returns the HTTP response headers associated with the plugin
+     * content.
+     *
+     * @return A Map<String, String[]> containing all headers. The
+     * mapping is 'lowercase header name' to ['unmodified header
+     * name', header value].
+     *
+     * @hide
+     * @deprecated This class was intended to be used by Gears. Since Gears was
+     * deprecated, so is this class.
+     */
+    @Deprecated
+    public Map<String, String[]> getHeaders() {
+        return mHeaders;
+    }
+
+    /**
+     * Returns the HTTP status code for the response.
+     *
+     * @return The HTTP statue code, e.g 200.
+     *
+     * @hide
+     * @deprecated This class was intended to be used by Gears. Since Gears was
+     * deprecated, so is this class.
+     */
+    @Deprecated
+    public int getStatusCode() {
+        return mStatusCode;
+    }
+}
diff --git a/core/java/android/webkit/UrlInterceptHandler.java b/core/java/android/webkit/UrlInterceptHandler.java
new file mode 100644
index 0000000..59fc0cb
--- /dev/null
+++ b/core/java/android/webkit/UrlInterceptHandler.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.webkit.CacheManager.CacheResult;
+import android.webkit.PluginData;
+import java.util.Map;
+
+/**
+ * @hide
+ * @deprecated This interface was inteded to be used by Gears. Since Gears was
+ * deprecated, so is this class.
+ */
+@Deprecated
+public interface UrlInterceptHandler {
+
+    /**
+     * Given an URL, returns the CacheResult which contains the
+     * surrogate response for the request, or null if the handler is
+     * not interested.
+     *
+     * @param url URL string.
+     * @param headers The headers associated with the request. May be null.
+     * @return The CacheResult containing the surrogate response.
+     *
+     * @hide
+     * @deprecated Do not use, this interface is deprecated.
+     */
+    @Deprecated
+    public CacheResult service(String url, Map<String, String> headers);
+
+    /**
+     * Given an URL, returns the PluginData which contains the
+     * surrogate response for the request, or null if the handler is
+     * not interested.
+     *
+     * @param url URL string.
+     * @param headers The headers associated with the request. May be null.
+     * @return The PluginData containing the surrogate response.
+     *
+     * @hide
+     * @deprecated Do not use, this interface is deprecated.
+     */
+    @Deprecated
+    public PluginData getPluginData(String url, Map<String, String> headers);
+}
diff --git a/core/java/android/webkit/UrlInterceptRegistry.java b/core/java/android/webkit/UrlInterceptRegistry.java
new file mode 100644
index 0000000..bdf6747
--- /dev/null
+++ b/core/java/android/webkit/UrlInterceptRegistry.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.webkit.CacheManager.CacheResult;
+import android.webkit.PluginData;
+import android.webkit.UrlInterceptHandler;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+
+/**
+ * @hide
+ * @deprecated This class was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
+ */
+@Deprecated
+public final class UrlInterceptRegistry {
+
+    private final static String LOGTAG = "intercept";
+
+    private static boolean mDisabled = false;
+
+    private static LinkedList mHandlerList;
+
+    private static synchronized LinkedList getHandlers() {
+        if(mHandlerList == null)
+            mHandlerList = new LinkedList<UrlInterceptHandler>();
+        return mHandlerList;
+    }
+
+    /**
+     * set the flag to control whether url intercept is enabled or disabled
+     * 
+     * @param disabled true to disable the cache
+     *
+     * @hide
+     * @deprecated This class was intended to be used by Gears. Since Gears was
+     * deprecated, so is this class.
+     */
+    @Deprecated
+    public static synchronized void setUrlInterceptDisabled(boolean disabled) {
+        mDisabled = disabled;
+    }
+
+    /**
+     * get the state of the url intercept, enabled or disabled
+     * 
+     * @return return if it is disabled
+     *
+     * @hide
+     * @deprecated This class was intended to be used by Gears. Since Gears was
+     * deprecated, so is this class.
+     */
+    @Deprecated
+    public static synchronized boolean urlInterceptDisabled() {
+        return mDisabled;
+    }
+
+    /**
+     * Register a new UrlInterceptHandler. This handler will be called
+     * before any that were previously registered.
+     *
+     * @param handler The new UrlInterceptHandler object
+     * @return true if the handler was not previously registered.
+     *
+     * @hide
+     * @deprecated This class was intended to be used by Gears. Since Gears was
+     * deprecated, so is this class.
+     */
+    @Deprecated
+    public static synchronized boolean registerHandler(
+            UrlInterceptHandler handler) {
+        if (!getHandlers().contains(handler)) {
+            getHandlers().addFirst(handler);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Unregister a previously registered UrlInterceptHandler.
+     *
+     * @param handler A previously registered UrlInterceptHandler.
+     * @return true if the handler was found and removed from the list.
+     *
+     * @hide
+     * @deprecated This class was intended to be used by Gears. Since Gears was
+     * deprecated, so is this class.
+     */
+    @Deprecated
+    public static synchronized boolean unregisterHandler(
+            UrlInterceptHandler handler) {
+        return getHandlers().remove(handler);
+    }
+
+    /**
+     * Given an url, returns the CacheResult of the first
+     * UrlInterceptHandler interested, or null if none are.
+     *
+     * @return A CacheResult containing surrogate content.
+     *
+     * @hide
+     * @deprecated This class was intended to be used by Gears. Since Gears was
+     * deprecated, so is this class.
+     */
+    @Deprecated
+    public static synchronized CacheResult getSurrogate(
+            String url, Map<String, String> headers) {
+        if (urlInterceptDisabled()) {
+            return null;
+        }
+        Iterator iter = getHandlers().listIterator();
+        while (iter.hasNext()) {
+            UrlInterceptHandler handler = (UrlInterceptHandler) iter.next();
+            CacheResult result = handler.service(url, headers);
+            if (result != null) {
+                return result;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Given an url, returns the PluginData of the first
+     * UrlInterceptHandler interested, or null if none are or if
+     * intercepts are disabled.
+     *
+     * @return A PluginData instance containing surrogate content.
+     *
+     * @hide
+     * @deprecated This class was intended to be used by Gears. Since Gears was
+     * deprecated, so is this class.
+     */
+    @Deprecated
+    public static synchronized PluginData getPluginData(
+            String url, Map<String, String> headers) {
+        if (urlInterceptDisabled()) {
+            return null;
+        }
+        Iterator iter = getHandlers().listIterator();
+        while (iter.hasNext()) {
+            UrlInterceptHandler handler = (UrlInterceptHandler) iter.next();
+            PluginData data = handler.getPluginData(url, headers);
+            if (data != null) {
+                return data;
+            }
+        }
+        return null;
+    }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index e94cf18..fdc06a6 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -16,6 +16,10 @@
 
 package com.android.keyguard;
 
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.keyguard.KeyguardUpdateMonitor.DisplayClientState;
+
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
@@ -51,9 +55,6 @@
 import android.view.View;
 import android.view.WindowManager;
 import android.widget.RemoteViews.OnClickHandler;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.keyguard.KeyguardUpdateMonitor.DisplayClientState;
 
 import java.io.File;
 import java.lang.ref.WeakReference;
@@ -279,7 +280,7 @@
             if (newState != mTransportState) {
                 mTransportState = newState;
                 if (DEBUGXPORT) Log.v(TAG, "update widget: transport state changed");
-                KeyguardHostView.this.postShowAppropriateWidgetPage();
+                KeyguardHostView.this.post(mSwitchPageRunnable);
             }
         }
         @Override
@@ -291,7 +292,7 @@
                 if (newState != mTransportState) {
                     mTransportState = newState;
                     if (DEBUGXPORT) Log.v(TAG, "update widget: play state changed");
-                    KeyguardHostView.this.postShowAppropriateWidgetPage();
+                    KeyguardHostView.this.post(mSwitchPageRunnable);
                 }
             }
         }
@@ -495,7 +496,6 @@
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        removeCallbacks(mSwitchPageRunnable);
         mAppWidgetHost.stopListening();
         KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallbacks);
     }
@@ -1438,7 +1438,7 @@
         mAppWidgetToShow = ss.appWidgetToShow;
         setInsets(ss.insets);
         if (DEBUG) Log.d(TAG, "onRestoreInstanceState, transport=" + mTransportState);
-        postShowAppropriateWidgetPage();
+        post(mSwitchPageRunnable);
     }
 
     @Override
@@ -1471,22 +1471,13 @@
         }
     }
 
-    void showAppropriateWidgetPage() {
+    private void showAppropriateWidgetPage() {
         int state = mTransportState;
         ensureTransportPresentOrRemoved(state);
-        if (mAppWidgetContainer.isLayoutRequested()) {
-            postShowAppropriateWidgetPage();
-            return;
-        }
         int pageToShow = getAppropriateWidgetPage(state);
         mAppWidgetContainer.setCurrentPage(pageToShow);
     }
 
-    void postShowAppropriateWidgetPage() {
-        removeCallbacks(mSwitchPageRunnable);
-        post(mSwitchPageRunnable);
-    }
-
     /**
      * Examines the current state and adds the transport to the widget pager when the state changes.
      *
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java
index e07e0d0..704af6e 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java
@@ -40,6 +40,7 @@
 import android.view.animation.DecelerateInterpolator;
 import android.widget.FrameLayout;
 import android.widget.TextClock;
+
 import com.android.internal.widget.LockPatternUtils;
 
 import java.util.ArrayList;
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
index ee5ff16..787b59a 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
@@ -40,6 +40,7 @@
 import android.os.RemoteException;
 import android.print.ILayoutResultCallback;
 import android.print.IPrintDocumentAdapter;
+import android.print.IPrintDocumentAdapterObserver;
 import android.print.IWriteResultCallback;
 import android.print.PageRange;
 import android.print.PrintAttributes;
@@ -201,6 +202,14 @@
             throw new IllegalArgumentException("PrintDocumentAdapter cannot be null");
         }
 
+        try {
+            IPrintDocumentAdapter.Stub.asInterface(mIPrintDocumentAdapter)
+                    .setObserver(new PrintDocumentAdapterObserver(this));
+        } catch (RemoteException re) {
+            finish();
+            return;
+        }
+
         PrintAttributes attributes = printJob.getAttributes();
         if (attributes != null) {
             mCurrPrintAttributes.copyFrom(attributes);
@@ -249,27 +258,29 @@
         // We can safely do the work in here since at this point
         // the system is bound to our (spooler) process which
         // guarantees that this process will not be killed.
-        if (mController.hasStarted()) {
+        if (mController != null && mController.hasStarted()) {
             mController.finish();
         }
-        if (mEditor.isPrintConfirmed() && mController.isFinished()) {
-            mSpoolerProvider.getSpooler().setPrintJobState(mPrintJobId,
-                    PrintJobInfo.STATE_QUEUED, null);
+        if (mEditor != null && mEditor.isPrintConfirmed()
+                && mController != null && mController.isFinished()) {
+                mSpoolerProvider.getSpooler().setPrintJobState(mPrintJobId,
+                        PrintJobInfo.STATE_QUEUED, null);
         } else {
             mSpoolerProvider.getSpooler().setPrintJobState(mPrintJobId,
                     PrintJobInfo.STATE_CANCELED, null);
         }
-        mIPrintDocumentAdapter.unlinkToDeath(mDeathRecipient, 0);
         if (mGeneratingPrintJobDialog != null) {
             mGeneratingPrintJobDialog.dismiss();
             mGeneratingPrintJobDialog = null;
         }
+        mIPrintDocumentAdapter.unlinkToDeath(mDeathRecipient, 0);
         mSpoolerProvider.destroy();
         super.onDestroy();
     }
 
     public boolean onTouchEvent(MotionEvent event) {
-        if (!mEditor.isPrintConfirmed() && mEditor.shouldCloseOnTouch(event)) {
+        if (mController != null && mEditor != null &&
+                !mEditor.isPrintConfirmed() && mEditor.shouldCloseOnTouch(event)) {
             if (!mController.isWorking()) {
                 PrintJobConfigActivity.this.finish();
             }
@@ -287,17 +298,19 @@
     }
 
     public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_BACK) {
-            if (mEditor.isShwoingGeneratingPrintJobUi()) {
+        if (mController != null && mEditor != null) {
+            if (keyCode == KeyEvent.KEYCODE_BACK) {
+                if (mEditor.isShwoingGeneratingPrintJobUi()) {
+                    return true;
+                }
+                if (event.isTracking() && !event.isCanceled()) {
+                    if (!mController.isWorking()) {
+                        PrintJobConfigActivity.this.finish();
+                    }
+                }
+                mEditor.cancel();
                 return true;
             }
-            if (event.isTracking() && !event.isCanceled()) {
-                if (!mController.isWorking()) {
-                    PrintJobConfigActivity.this.finish();
-                }
-            }
-            mEditor.cancel();
-            return true;
         }
         return super.onKeyUp(keyCode, event);
     }
@@ -2701,4 +2714,32 @@
             /* do noting - we are in the same process */
         }
     }
+
+    private static final class PrintDocumentAdapterObserver
+            extends IPrintDocumentAdapterObserver.Stub {
+        private final WeakReference<PrintJobConfigActivity> mWeakActvity;
+
+        public PrintDocumentAdapterObserver(PrintJobConfigActivity activity) {
+            mWeakActvity = new WeakReference<PrintJobConfigActivity>(activity);
+        }
+
+        @Override
+        public void onDestroy() {
+            final PrintJobConfigActivity activity = mWeakActvity.get();
+            if (activity != null) {
+                activity.mController.mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        if (activity.mController != null) {
+                            activity.mController.cancel();
+                        }
+                        if (activity.mEditor != null) {
+                            activity.mEditor.cancel();
+                        }
+                        activity.finish();
+                    }
+                });
+            }
+        }
+    }
 }