Merge "Cleanup API typo" into klp-dev
diff --git a/api/current.txt b/api/current.txt
index b0ecb76..e4d8b76 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -25,6 +25,7 @@
     field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
     field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
     field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
+    field public static final java.lang.String BIND_PRINT_SPOOLER_SERVICE = "android.permission.BIND_PRINT_SPOOLER_SERVICE";
     field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS";
     field public static final java.lang.String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE";
     field public static final java.lang.String BIND_VPN_SERVICE = "android.permission.BIND_VPN_SERVICE";
@@ -19147,7 +19148,7 @@
     method public void onFinish();
     method public abstract void onLayout(android.print.PrintAttributes, android.print.PrintAttributes, android.os.CancellationSignal, android.print.PrintDocumentAdapter.LayoutResultCallback, android.os.Bundle);
     method public void onStart();
-    method public abstract void onWrite(android.print.PageRange[], java.io.FileDescriptor, android.os.CancellationSignal, android.print.PrintDocumentAdapter.WriteResultCallback);
+    method public abstract void onWrite(android.print.PageRange[], android.os.ParcelFileDescriptor, android.os.CancellationSignal, android.print.PrintDocumentAdapter.WriteResultCallback);
     field public static final java.lang.String METADATA_KEY_PRINT_PREVIEW = "KEY_METADATA_PRINT_PREVIEW";
   }
 
@@ -19167,6 +19168,7 @@
     method public int describeContents();
     method public int getColorMode();
     method public int getContentType();
+    method public long getDataSize();
     method public int getFittingMode();
     method public android.print.PrintAttributes.Margins getMargins();
     method public android.print.PrintAttributes.MediaSize getMediaSize();
@@ -19198,7 +19200,7 @@
   public class PrintFileDocumentAdapter extends android.print.PrintDocumentAdapter {
     ctor public PrintFileDocumentAdapter(android.content.Context, java.io.File, android.print.PrintDocumentInfo);
     method public void onLayout(android.print.PrintAttributes, android.print.PrintAttributes, android.os.CancellationSignal, android.print.PrintDocumentAdapter.LayoutResultCallback, android.os.Bundle);
-    method public void onWrite(android.print.PageRange[], java.io.FileDescriptor, android.os.CancellationSignal, android.print.PrintDocumentAdapter.WriteResultCallback);
+    method public void onWrite(android.print.PageRange[], android.os.ParcelFileDescriptor, android.os.CancellationSignal, android.print.PrintDocumentAdapter.WriteResultCallback);
   }
 
   public final class PrintJob {
@@ -19220,9 +19222,10 @@
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int PRINT_JOB_ID_UNDEFINED = -1; // 0xffffffff
-    field public static final int STATE_CANCELED = 6; // 0x6
-    field public static final int STATE_COMPLETED = 4; // 0x4
-    field public static final int STATE_FAILED = 5; // 0x5
+    field public static final int STATE_BLOCKED = 4; // 0x4
+    field public static final int STATE_CANCELED = 7; // 0x7
+    field public static final int STATE_COMPLETED = 5; // 0x5
+    field public static final int STATE_FAILED = 6; // 0x6
     field public static final int STATE_QUEUED = 2; // 0x2
     field public static final int STATE_STARTED = 3; // 0x3
   }
@@ -19340,17 +19343,19 @@
 package android.printservice {
 
   public final class PrintDocument {
-    method public java.io.FileDescriptor getData();
+    method public android.os.ParcelFileDescriptor getData();
     method public android.print.PrintDocumentInfo getInfo();
   }
 
   public final class PrintJob {
+    method public boolean block(java.lang.String);
     method public boolean cancel();
     method public boolean complete();
     method public boolean fail(java.lang.String);
     method public android.printservice.PrintDocument getDocument();
     method public int getId();
     method public android.print.PrintJobInfo getInfo();
+    method public boolean isBlocked();
     method public boolean isCancelled();
     method public boolean isCompleted();
     method public boolean isFailed();
@@ -19382,9 +19387,11 @@
     method public final boolean isDestroyed();
     method public final boolean isPrinterDiscoveryStarted();
     method public abstract void onDestroy();
-    method public abstract void onRequestPrinterUpdate(android.print.PrinterId);
     method public abstract void onStartPrinterDiscovery(java.util.List<android.print.PrinterId>);
+    method public abstract void onStartPrinterStateTracking(android.print.PrinterId);
     method public abstract void onStopPrinterDiscovery();
+    method public abstract void onStopPrinterStateTracking(android.print.PrinterId);
+    method public abstract void onValidatePrinters(java.util.List<android.print.PrinterId>);
     method public final void removePrinters(java.util.List<android.print.PrinterId>);
     method public final void updatePrinters(java.util.List<android.print.PrinterInfo>);
   }
@@ -31158,7 +31165,6 @@
     method public int getAlignmentMode();
     method public int getColumnCount();
     method public int getOrientation();
-    method public android.util.Printer getPrinter();
     method public int getRowCount();
     method public boolean getUseDefaultMargins();
     method public boolean isColumnOrderPreserved();
@@ -31168,7 +31174,6 @@
     method public void setColumnCount(int);
     method public void setColumnOrderPreserved(boolean);
     method public void setOrientation(int);
-    method public void setPrinter(android.util.Printer);
     method public void setRowCount(int);
     method public void setRowOrderPreserved(boolean);
     method public void setUseDefaultMargins(boolean);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index eba69b6..2b0c896 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -216,6 +216,12 @@
 
     void resetPreferredActivities(int userId);
 
+    ResolveInfo getLastChosenActivity(in Intent intent,
+            String resolvedType, int flags);
+
+    void setLastChosenActivity(in Intent intent, String resolvedType, int flags,
+            in IntentFilter filter, int match, in ComponentName activity);
+
     void addPreferredActivity(in IntentFilter filter, int match,
             in ComponentName[] set, in ComponentName activity, int userId);
 
@@ -226,7 +232,7 @@
 
     int getPreferredActivities(out List<IntentFilter> outFilters,
             out List<ComponentName> outActivities, String packageName);
-    
+
     /**
      * As per {@link android.content.pm.PackageManager#setComponentEnabledSetting}.
      */
diff --git a/core/java/android/net/CaptivePortalTracker.java b/core/java/android/net/CaptivePortalTracker.java
index 74c2c59..6b96ad4 100644
--- a/core/java/android/net/CaptivePortalTracker.java
+++ b/core/java/android/net/CaptivePortalTracker.java
@@ -16,22 +16,16 @@
 
 package android.net;
 
-import android.app.Activity;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.net.ConnectivityManager;
 import android.net.IConnectivityManager;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.os.Handler;
-import android.os.UserHandle;
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -46,7 +40,6 @@
 import android.telephony.CellInfoLte;
 import android.telephony.CellInfoWcdma;
 import android.telephony.TelephonyManager;
-import android.text.TextUtils;
 
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
@@ -60,8 +53,6 @@
 import java.net.UnknownHostException;
 import java.util.List;
 
-import com.android.internal.R;
-
 /**
  * This class allows captive portal detection on a network.
  * @hide
@@ -71,7 +62,6 @@
     private static final String TAG = "CaptivePortalTracker";
 
     private static final String DEFAULT_SERVER = "clients3.google.com";
-    private static final String NOTIFICATION_ID = "CaptivePortal.Notification";
 
     private static final int SOCKET_TIMEOUT_MS = 10000;
 
@@ -93,7 +83,6 @@
 
     private String mServer;
     private String mUrl;
-    private boolean mNotificationShown = false;
     private boolean mIsCaptivePortalCheckEnabled = false;
     private IConnectivityManager mConnService;
     private TelephonyManager mTelephonyManager;
@@ -192,12 +181,12 @@
     private class DefaultState extends State {
         @Override
         public void enter() {
-            if (DBG) log(getName() + "\n");
+            setNotificationOff();
         }
 
         @Override
         public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
+            if (DBG) log(getName() + message.toString());
             switch (message.what) {
                 case CMD_DETECT_PORTAL:
                     NetworkInfo info = (NetworkInfo) message.obj;
@@ -219,23 +208,24 @@
     private class NoActiveNetworkState extends State {
         @Override
         public void enter() {
-            if (DBG) log(getName() + "\n");
             mNetworkInfo = null;
-            /* Clear any previous notification */
-            setNotificationVisible(false);
         }
 
         @Override
         public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
+            if (DBG) log(getName() + message.toString());
             InetAddress server;
             NetworkInfo info;
             switch (message.what) {
                 case CMD_CONNECTIVITY_CHANGE:
                     info = (NetworkInfo) message.obj;
-                    if (info.isConnected() && isActiveNetwork(info)) {
-                        mNetworkInfo = info;
-                        transitionTo(mDelayedCaptiveCheckState);
+                    if (info.getType() == ConnectivityManager.TYPE_WIFI) {
+                        if (info.isConnected() && isActiveNetwork(info)) {
+                            mNetworkInfo = info;
+                            transitionTo(mDelayedCaptiveCheckState);
+                        }
+                    } else {
+                        log(getName() + " not a wifi connectivity change, ignore");
                     }
                     break;
                 default:
@@ -248,7 +238,7 @@
     private class ActiveNetworkState extends State {
         @Override
         public void enter() {
-            if (DBG) log(getName() + "\n");
+            setNotificationOff();
         }
 
         @Override
@@ -281,7 +271,6 @@
     private class DelayedCaptiveCheckState extends State {
         @Override
         public void enter() {
-            if (DBG) log(getName() + "\n");
             Message message = obtainMessage(CMD_DELAYED_CAPTIVE_CHECK, ++mDelayedCheckToken, 0);
             if (mDeviceProvisioned) {
                 sendMessageDelayed(message, DELAYED_CHECK_INTERVAL_MS);
@@ -292,7 +281,7 @@
 
         @Override
         public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
+            if (DBG) log(getName() + message.toString());
             switch (message.what) {
                 case CMD_DELAYED_CAPTIVE_CHECK:
                     if (message.arg1 == mDelayedCheckToken) {
@@ -308,7 +297,12 @@
                             if (captive) {
                                 // Setup Wizard will assist the user in connecting to a captive
                                 // portal, so make the notification visible unless during setup
-                                setNotificationVisible(true);
+                                try {
+                                    mConnService.setProvisioningNotificationVisible(true,
+                                        mNetworkInfo.getType(), mNetworkInfo.getExtraInfo(), mUrl);
+                                } catch(RemoteException e) {
+                                    e.printStackTrace();
+                                }
                             }
                         } else {
                             Intent intent = new Intent(
@@ -366,6 +360,15 @@
         return false;
     }
 
+    private void setNotificationOff() {
+        try {
+            mConnService.setProvisioningNotificationVisible(false, ConnectivityManager.TYPE_NONE,
+                    null, null);
+        } catch (RemoteException e) {
+            log("setNotificationOff: " + e);
+        }
+    }
+
     /**
      * Do a URL fetch on a known server to see if we get the data we expect.
      * Measure the response time and broadcast that.
@@ -435,77 +438,6 @@
         return null;
     }
 
-    private void setNotificationVisible(boolean visible) {
-        // if it should be hidden and it is already hidden, then noop
-        if (!visible && !mNotificationShown) {
-            if (DBG) log("setNotivicationVisible: false and not shown, so noop");
-            return;
-        }
-
-        Resources r = Resources.getSystem();
-        NotificationManager notificationManager = (NotificationManager) mContext
-            .getSystemService(Context.NOTIFICATION_SERVICE);
-
-        if (visible) {
-            CharSequence title;
-            CharSequence details;
-            int icon;
-            String url = null;
-            switch (mNetworkInfo.getType()) {
-                case ConnectivityManager.TYPE_WIFI:
-                    title = r.getString(R.string.wifi_available_sign_in, 0);
-                    details = r.getString(R.string.network_available_sign_in_detailed,
-                            mNetworkInfo.getExtraInfo());
-                    icon = R.drawable.stat_notify_wifi_in_range;
-                    url = mUrl;
-                    break;
-                case ConnectivityManager.TYPE_MOBILE:
-                    title = r.getString(R.string.network_available_sign_in, 0);
-                    // TODO: Change this to pull from NetworkInfo once a printable
-                    // name has been added to it
-                    details = mTelephonyManager.getNetworkOperatorName();
-                    icon = R.drawable.stat_notify_rssi_in_range;
-                    try {
-                        url = mConnService.getMobileProvisioningUrl();
-                        if (TextUtils.isEmpty(url)) {
-                            url = mConnService.getMobileRedirectedProvisioningUrl();
-                        }
-                    } catch(RemoteException e) {
-                        e.printStackTrace();
-                    }
-                    if (TextUtils.isEmpty(url)) {
-                        url = mUrl;
-                    }
-                    break;
-                default:
-                    title = r.getString(R.string.network_available_sign_in, 0);
-                    details = r.getString(R.string.network_available_sign_in_detailed,
-                            mNetworkInfo.getExtraInfo());
-                    icon = R.drawable.stat_notify_rssi_in_range;
-                    url = mUrl;
-                    break;
-            }
-
-            Notification notification = new Notification();
-            notification.when = 0;
-            notification.icon = icon;
-            notification.flags = Notification.FLAG_AUTO_CANCEL;
-            Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
-            intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
-                    Intent.FLAG_ACTIVITY_NEW_TASK);
-            notification.contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
-            notification.tickerText = title;
-            notification.setLatestEventInfo(mContext, title, details, notification.contentIntent);
-
-            if (DBG) log("setNotivicationVisible: make visible");
-            notificationManager.notify(NOTIFICATION_ID, 1, notification);
-        } else {
-            if (DBG) log("setNotivicationVisible: cancel notification");
-            notificationManager.cancel(NOTIFICATION_ID, 1);
-        }
-        mNotificationShown = visible;
-    }
-
     private void sendFailedCaptivePortalCheckBroadcast(long requestTimestampMs) {
         sendNetworkConditionsBroadcast(false /* response received */, false /* ignored */,
                 requestTimestampMs, 0 /* ignored */);
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index f6a3a4a..3874369 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -624,6 +624,29 @@
     }
 
     /**
+     * Returns details about the Provisioning or currently active default data network. When
+     * connected, this network is the default route for outgoing connections.
+     * You should always check {@link NetworkInfo#isConnected()} before initiating
+     * network traffic. This may return {@code null} when there is no default
+     * network.
+     *
+     * @return a {@link NetworkInfo} object for the current default network
+     *        or {@code null} if no network default network is currently active
+     *
+     * <p>This method requires the call to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
+     *
+     * {@hide}
+     */
+    public NetworkInfo getProvisioningOrActiveNetworkInfo() {
+        try {
+            return mService.getProvisioningOrActiveNetworkInfo();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
      * Returns the IP information for the current default network.
      *
      * @return a {@link LinkProperties} object describing the IP info
@@ -1357,63 +1380,19 @@
     }
 
     /**
-     * The ResultReceiver resultCode for checkMobileProvisioning (CMP_RESULT_CODE)
-     */
-
-    /**
-     * No connection was possible to the network.
-     * {@hide}
-     */
-    public static final int CMP_RESULT_CODE_NO_CONNECTION = 0;
-
-    /**
-     * A connection was made to the internet, all is well.
-     * {@hide}
-     */
-    public static final int CMP_RESULT_CODE_CONNECTABLE = 1;
-
-    /**
-     * A connection was made but there was a redirection, we appear to be in walled garden.
-     * This is an indication of a warm sim on a mobile network.
-     * {@hide}
-     */
-    public static final int CMP_RESULT_CODE_REDIRECTED = 2;
-
-    /**
-     * A connection was made but no dns server was available to resolve a name to address.
-     * This is an indication of a warm sim on a mobile network.
+     * Check mobile provisioning.
      *
-     * {@hide}
-     */
-    public static final int CMP_RESULT_CODE_NO_DNS = 3;
-
-    /**
-     * A connection was made but could not open a TCP connection.
-     * This is an indication of a warm sim on a mobile network.
-     * {@hide}
-     */
-    public static final int CMP_RESULT_CODE_NO_TCP_CONNECTION = 4;
-
-    /**
-     * Check mobile provisioning. The resultCode passed to
-     * onReceiveResult will be one of the CMP_RESULT_CODE_xxxx values above.
-     * This may take a minute or more to complete.
-     *
-     * @param sendNotificaiton, when true a notification will be sent to user.
      * @param suggestedTimeOutMs, timeout in milliseconds
-     * @param resultReceiver needs to  be supplied to receive the result
      *
      * @return time out that will be used, maybe less that suggestedTimeOutMs
      * -1 if an error.
      *
      * {@hide}
      */
-    public int checkMobileProvisioning(boolean sendNotification, int suggestedTimeOutMs,
-            ResultReceiver resultReceiver) {
+    public int checkMobileProvisioning(int suggestedTimeOutMs) {
         int timeOutMs = -1;
         try {
-            timeOutMs = mService.checkMobileProvisioning(sendNotification, suggestedTimeOutMs,
-                    resultReceiver);
+            timeOutMs = mService.checkMobileProvisioning(suggestedTimeOutMs);
         } catch (RemoteException e) {
         }
         return timeOutMs;
@@ -1481,4 +1460,20 @@
             return null;
         }
     }
+
+    /**
+     * Set sign in error notification to visible or in visible
+     *
+     * @param visible
+     * @param networkType
+     *
+     * {@hide}
+     */
+    public void setProvisioningNotificationVisible(boolean visible, int networkType,
+            String extraInfo, String url) {
+        try {
+            mService.setProvisioningNotificationVisible(visible, networkType, extraInfo, url);
+        } catch (RemoteException e) {
+        }
+    }
 }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index bf2dade..c07e900 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -50,6 +50,8 @@
     NetworkInfo getNetworkInfo(int networkType);
     NetworkInfo[] getAllNetworkInfo();
 
+    NetworkInfo getProvisioningOrActiveNetworkInfo();
+
     boolean isNetworkSupported(int networkType);
 
     LinkProperties getActiveLinkProperties();
@@ -141,7 +143,7 @@
 
     int findConnectionTypeForIface(in String iface);
 
-    int checkMobileProvisioning(boolean sendNotification, int suggestedTimeOutMs, in ResultReceiver resultReceiver);
+    int checkMobileProvisioning(int suggestedTimeOutMs);
 
     String getMobileProvisioningUrl();
 
@@ -153,4 +155,5 @@
 
     LinkInfo[] getAllLinkInfo();
 
+    void setProvisioningNotificationVisible(boolean visible, int networkType, in String extraInfo, in String url);
 }
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index faa13b0..125d5c1 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -61,8 +61,12 @@
     private ITelephony mPhoneService;
 
     private String mApnType;
+    private NetworkInfo mNetworkInfo;
     private boolean mTeardownRequested = false;
     private Handler mTarget;
+    private Context mContext;
+    private LinkProperties mLinkProperties;
+    private LinkCapabilities mLinkCapabilities;
     private boolean mPrivateDnsRouteSet = false;
     private boolean mDefaultRouteSet = false;
 
@@ -106,6 +110,7 @@
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
+        filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN);
         filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
 
         mContext.registerReceiver(new MobileDataStateReceiver(), filter);
@@ -184,10 +189,41 @@
     public void releaseWakeLock() {
     }
 
+    private void updateLinkProperitesAndCapatilities(Intent intent) {
+        mLinkProperties = intent.getParcelableExtra(
+                PhoneConstants.DATA_LINK_PROPERTIES_KEY);
+        if (mLinkProperties == null) {
+            loge("CONNECTED event did not supply link properties.");
+            mLinkProperties = new LinkProperties();
+        }
+        mLinkCapabilities = intent.getParcelableExtra(
+                PhoneConstants.DATA_LINK_CAPABILITIES_KEY);
+        if (mLinkCapabilities == null) {
+            loge("CONNECTED event did not supply link capabilities.");
+            mLinkCapabilities = new LinkCapabilities();
+        }
+    }
+
     private class MobileDataStateReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
             if (intent.getAction().equals(TelephonyIntents.
+                    ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN)) {
+                String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY);
+                String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY);
+                if (!TextUtils.equals(mApnType, apnType)) {
+                    return;
+                }
+                if (DBG) {
+                    log("Broadcast received: " + intent.getAction() + " apnType=" + apnType
+                            + " apnName=" + apnName);
+                }
+
+                // Make us in the connecting state until we make a new TYPE_MOBILE_PROVISIONING
+                mMobileDataState = PhoneConstants.DataState.CONNECTING;
+                updateLinkProperitesAndCapatilities(intent);
+                setDetailedState(DetailedState.CONNECTED_TO_PROVISIONING_NETWORK, "", apnName);
+            } else if (intent.getAction().equals(TelephonyIntents.
                     ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
                 String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY);
                 if (VDBG) {
@@ -249,18 +285,7 @@
                             setDetailedState(DetailedState.SUSPENDED, reason, apnName);
                             break;
                         case CONNECTED:
-                            mLinkProperties = intent.getParcelableExtra(
-                                    PhoneConstants.DATA_LINK_PROPERTIES_KEY);
-                            if (mLinkProperties == null) {
-                                loge("CONNECTED event did not supply link properties.");
-                                mLinkProperties = new LinkProperties();
-                            }
-                            mLinkCapabilities = intent.getParcelableExtra(
-                                    PhoneConstants.DATA_LINK_CAPABILITIES_KEY);
-                            if (mLinkCapabilities == null) {
-                                loge("CONNECTED event did not supply link capabilities.");
-                                mLinkCapabilities = new LinkCapabilities();
-                            }
+                            updateLinkProperitesAndCapatilities(intent);
                             setDetailedState(DetailedState.CONNECTED, reason, apnName);
                             break;
                     }
@@ -319,8 +344,8 @@
                 String reason = intent.getStringExtra(PhoneConstants.FAILURE_REASON_KEY);
                 String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY);
                 if (DBG) {
-                    log("Received " + intent.getAction() +
-                                " broadcast" + (reason == null ? "" : "(" + reason + ")"));
+                    log("Broadcast received: " + intent.getAction() +
+                                " reason=" + reason == null ? "null" : reason);
                 }
                 setDetailedState(DetailedState.FAILED, reason, apnName);
             } else {
@@ -412,6 +437,13 @@
         return (setEnableApn(mApnType, false) != PhoneConstants.APN_REQUEST_FAILED);
     }
 
+    /**
+     * @return true if this is ready to operate
+     */
+    public boolean isReady() {
+        return mDataConnectionTrackerAc != null;
+    }
+
     @Override
     public void captivePortalCheckComplete() {
         // not implemented
@@ -574,6 +606,40 @@
         }
     }
 
+    /**
+     *  Inform DCT mobile provisioning has started, it ends when provisioning completes.
+     */
+    public void enableMobileProvisioning(String url) {
+        if (DBG) log("enableMobileProvisioning(url=" + url + ")");
+        final AsyncChannel channel = mDataConnectionTrackerAc;
+        if (channel != null) {
+            Message msg = Message.obtain();
+            msg.what = DctConstants.CMD_ENABLE_MOBILE_PROVISIONING;
+            msg.setData(Bundle.forPair(DctConstants.PROVISIONING_URL_KEY, url));
+            channel.sendMessage(msg);
+        }
+    }
+
+    /**
+     * Return if this network is the provisioning network. Valid only if connected.
+     * @param met
+     */
+    public boolean isProvisioningNetwork() {
+        boolean retVal;
+        try {
+            Message msg = Message.obtain();
+            msg.what = DctConstants.CMD_IS_PROVISIONING_APN;
+            msg.setData(Bundle.forPair(DctConstants.APN_TYPE_KEY, mApnType));
+            Message result = mDataConnectionTrackerAc.sendMessageSynchronously(msg);
+            retVal = result.arg1 == DctConstants.ENABLED;
+        } catch (NullPointerException e) {
+            loge("isProvisioningNetwork: X " + e);
+            retVal = false;
+        }
+        if (DBG) log("isProvisioningNetwork: retVal=" + retVal);
+        return retVal;
+    }
+
     @Override
     public void addStackedLink(LinkProperties link) {
         mLinkProperties.addStackedLink(link);
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 689dae5..dabc73a 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -84,6 +84,12 @@
         VERIFYING_POOR_LINK,
         /** Checking if network is a captive portal */
         CAPTIVE_PORTAL_CHECK,
+        /**
+         * Network is connected to provisioning network
+         * TODO: Probably not needed when we add TYPE_PROVISIONING_NETWORK
+         * @hide
+         */
+        CONNECTED_TO_PROVISIONING_NETWORK
     }
 
     /**
@@ -108,6 +114,7 @@
         stateMap.put(DetailedState.DISCONNECTED, State.DISCONNECTED);
         stateMap.put(DetailedState.FAILED, State.DISCONNECTED);
         stateMap.put(DetailedState.BLOCKED, State.DISCONNECTED);
+        stateMap.put(DetailedState.CONNECTED_TO_PROVISIONING_NETWORK, State.CONNECTED);
     }
 
     private int mNetworkType;
diff --git a/core/java/android/print/IPrintManager.aidl b/core/java/android/print/IPrintManager.aidl
index 3bfd9a1..fb6bb2e 100644
--- a/core/java/android/print/IPrintManager.aidl
+++ b/core/java/android/print/IPrintManager.aidl
@@ -41,7 +41,9 @@
     void startPrinterDiscovery(in IPrinterDiscoveryObserver observer,
             in List<PrinterId> priorityList, int userId);
     void stopPrinterDiscovery(in IPrinterDiscoveryObserver observer, int userId);
-    void requestPrinterUpdate(in PrinterId printerId, int userId);
+    void validatePrinters(in List<PrinterId> printerIds, int userId);
+    void startPrinterStateTracking(in PrinterId printerId, int userId);
+    void stopPrinterStateTracking(in PrinterId printerId, int userId);
     void destroyPrinterDiscoverySession(in IPrinterDiscoveryObserver observer,
             int userId);
 }
diff --git a/core/java/android/print/PrintDocumentAdapter.java b/core/java/android/print/PrintDocumentAdapter.java
index 8a64e85..33b4aad 100644
--- a/core/java/android/print/PrintDocumentAdapter.java
+++ b/core/java/android/print/PrintDocumentAdapter.java
@@ -18,8 +18,8 @@
 
 import android.os.Bundle;
 import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
 
-import java.io.FileDescriptor;
 import java.util.List;
 
 /**
@@ -41,7 +41,7 @@
  * <li>
  * After every call to {@link #onLayout(PrintAttributes, PrintAttributes,
  * CancellationSignal, LayoutResultCallback, Bundle)}, you may get a call to
- * {@link #onWrite(PageRange[], FileDescriptor, CancellationSignal, WriteResultCallback)}
+ * {@link #onWrite(PageRange[], ParcelFileDescriptor, CancellationSignal, WriteResultCallback)}
  * asking you to write a PDF file with the content for specific pages.
  * </li>
  * <li>
@@ -64,7 +64,7 @@
  * PrintAttributes, CancellationSignal, LayoutResultCallback, Bundle)} on
  * the UI thread (assuming onStart initializes resources needed for layout).
  * This will ensure that the UI does not change while you are laying out the
- * printed content. Then you can handle {@link #onWrite(PageRange[], FileDescriptor,
+ * printed content. Then you can handle {@link #onWrite(PageRange[], ParcelFileDescriptor,
  * CancellationSignal, WriteResultCallback)} and {@link #onFinish()} on another
  * thread. This will ensure that the UI is frozen for the minimal amount of
  * time. Also this assumes that you will generate the printed content in
@@ -150,10 +150,10 @@
      * from of a PDF file to the given file descriptor. This method is invoked
      * on the main thread.
      *<p>
-     * After you are done writing, you should <strong>not</strong> close the
-     * file descriptor, rather you must invoke: {@link WriteResultCallback
-     * #onWriteFinished(List)}, if writing completed successfully; or {@link
-     * WriteResultCallback#onWriteFailed(CharSequence)}, if an error occurred.
+     * After you are done writing, you should close the file descriptor and
+     * invoke {@link WriteResultCallback #onWriteFinished(List)}, if writing
+     * completed successfully; or {@link WriteResultCallback#onWriteFailed(
+     * CharSequence)}, if an error occurred.
      * </p>
      * <p>
      * <strong>Note:</strong> If the printed content is large, it is a good
@@ -171,7 +171,7 @@
      * @see WriteResultCallback
      * @see CancellationSignal
      */
-    public abstract void onWrite(PageRange[] pages, FileDescriptor destination,
+    public abstract void onWrite(PageRange[] pages, ParcelFileDescriptor destination,
             CancellationSignal cancellationSignal, WriteResultCallback callback);
 
     /**
@@ -185,7 +185,7 @@
 
     /**
      * Base class for implementing a callback for the result of {@link
-     * PrintDocumentAdapter#onWrite(PageRange[], FileDescriptor, CancellationSignal,
+     * PrintDocumentAdapter#onWrite(PageRange[], ParcelFileDescriptor, CancellationSignal,
      * WriteResultCallback)}.
      */
     public static abstract class WriteResultCallback {
diff --git a/core/java/android/print/PrintDocumentInfo.java b/core/java/android/print/PrintDocumentInfo.java
index b32961b..f2b91ae 100644
--- a/core/java/android/print/PrintDocumentInfo.java
+++ b/core/java/android/print/PrintDocumentInfo.java
@@ -60,6 +60,7 @@
     private int mColorMode;
     private Margins mMargins;
     private MediaSize mMediaSize;
+    private long mDataSize;
 
     /**
      * Creates a new instance.
@@ -82,6 +83,7 @@
         mColorMode = prototype.mColorMode;
         mMargins = prototype.mMargins;
         mMediaSize = prototype.mMediaSize;
+        mDataSize = prototype.mDataSize;
     }
 
     /**
@@ -98,6 +100,7 @@
         mColorMode = parcel.readInt();
         mMargins = Margins.createFromParcel(parcel);
         mMediaSize = MediaSize.createFromParcel(parcel);
+        mDataSize = parcel.readLong();
     }
 
     /**
@@ -188,6 +191,26 @@
         return mMediaSize;
     }
 
+    /**
+     * Gets the document data size in bytes.
+     *
+     * @return The data size.
+     */
+    public long getDataSize() {
+        return mDataSize;
+    }
+
+    /**
+     * Sets the document data size in bytes.
+     *
+     * @param dataSize The data size.
+     *
+     * @hide
+     */
+    public void setDataSize(long dataSize) {
+        mDataSize = dataSize;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -203,6 +226,7 @@
         parcel.writeInt(mColorMode);
         mMargins.writeToParcel(parcel);
         mMediaSize.writeToParcel(parcel);
+        parcel.writeLong(mDataSize);
     }
 
     @Override
@@ -217,6 +241,8 @@
         result = prime * result + mColorMode;
         result = prime * result + (mMargins != null ? mMargins.hashCode() : 0);
         result = prime * result + (mMediaSize != null ? mMediaSize.hashCode() : 0);
+        result = prime * result + (int) mDataSize;
+        result = prime * result + (int) mDataSize >> 32;
         return result;
     }
 
@@ -264,6 +290,9 @@
         } else if (!mMediaSize.equals(other.mMediaSize)) {
             return false;
         }
+        if (mDataSize != other.mDataSize) {
+            return false;
+        }
         return true;
     }
 
@@ -279,6 +308,7 @@
         builder.append(", colorMode=").append(PrintAttributes.colorModeToString(mColorMode));
         builder.append(", margins=").append(mMargins);
         builder.append(", mediaSize=").append(mMediaSize);
+        builder.append(", size=").append(mDataSize);
         builder.append("}");
         return builder.toString();
     }
diff --git a/core/java/android/print/PrintFileDocumentAdapter.java b/core/java/android/print/PrintFileDocumentAdapter.java
index dbc8b6f..b9053961 100644
--- a/core/java/android/print/PrintFileDocumentAdapter.java
+++ b/core/java/android/print/PrintFileDocumentAdapter.java
@@ -21,6 +21,7 @@
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.CancellationSignal.OnCancelListener;
+import android.os.ParcelFileDescriptor;
 import android.util.Log;
 
 import com.android.internal.R;
@@ -28,7 +29,6 @@
 import libcore.io.IoUtils;
 
 import java.io.File;
-import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -81,7 +81,7 @@
     }
 
     @Override
-    public void onWrite(PageRange[] pages, FileDescriptor destination,
+    public void onWrite(PageRange[] pages, ParcelFileDescriptor destination,
             CancellationSignal cancellationSignal, WriteResultCallback callback) {
         mWriteFileAsyncTask = new WriteFileAsyncTask(destination, cancellationSignal, callback);
         mWriteFileAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
@@ -90,13 +90,13 @@
 
     private final class WriteFileAsyncTask extends AsyncTask<Void, Void, Void> {
 
-        private final FileDescriptor mDestination;
+        private final ParcelFileDescriptor mDestination;
 
         private final WriteResultCallback mResultCallback;
 
         private final CancellationSignal mCancellationSignal;
 
-        public WriteFileAsyncTask(FileDescriptor destination,
+        public WriteFileAsyncTask(ParcelFileDescriptor destination,
                 CancellationSignal cancellationSignal, WriteResultCallback callback) {
             mDestination = destination;
             mResultCallback = callback;
@@ -112,7 +112,7 @@
         @Override
         protected Void doInBackground(Void... params) {
             InputStream in = null;
-            OutputStream out = new FileOutputStream(mDestination);
+            OutputStream out = new FileOutputStream(mDestination.getFileDescriptor());
             final byte[] buffer = new byte[8192];
             try {
                 in = new FileInputStream(mFile);
diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java
index 602f3c1..b919ad6 100644
--- a/core/java/android/print/PrintJobInfo.java
+++ b/core/java/android/print/PrintJobInfo.java
@@ -44,6 +44,13 @@
     public static final int STATE_ANY_VISIBLE_TO_CLIENTS = -2;
 
     /**
+     * Constant for matching any active print job state.
+     *
+     * @hide
+     */
+    public static final int STATE_ANY_ACTIVE = -3;
+
+    /**
      * Print job state: The print job is being created but not yet
      * ready to be printed.
      * <p>
@@ -55,7 +62,7 @@
     public static final int STATE_CREATED = 1;
 
     /**
-     * Print job status: The print jobs is created, it is ready
+     * Print job state: The print jobs is created, it is ready
      * to be printed and should be processed.
      * <p>
      * Next valid states: {@link #STATE_STARTED}, {@link #STATE_FAILED},
@@ -65,40 +72,49 @@
     public static final int STATE_QUEUED = 2;
 
     /**
-     * Print job status: The print job is being printed.
+     * Print job state: The print job is being printed.
      * <p>
      * Next valid states: {@link #STATE_COMPLETED}, {@link #STATE_FAILED},
-     * {@link #STATE_CANCELED}
+     * {@link #STATE_CANCELED}, {@link #STATE_BLOCKED}
      * </p>
      */
     public static final int STATE_STARTED = 3;
 
     /**
-     * Print job status: The print job was successfully printed.
-     * This is a terminal state.
+     * Print job state: The print job is blocked.
      * <p>
-     * Next valid states: None
+     * Next valid states: {@link #STATE_FAILED}, {@link #STATE_CANCELED},
+     * {@link #STATE_STARTED}
      * </p>
      */
-    public static final int STATE_COMPLETED = 4;
+    public static final int STATE_BLOCKED = 4;
 
     /**
-     * Print job status: The print job was printing but printing failed.
+     * Print job state: The print job was successfully printed.
      * This is a terminal state.
      * <p>
      * Next valid states: None
      * </p>
      */
-    public static final int STATE_FAILED = 5;
+    public static final int STATE_COMPLETED = 5;
 
     /**
-     * Print job status: The print job was canceled.
+     * Print job state: The print job was printing but printing failed.
      * This is a terminal state.
      * <p>
      * Next valid states: None
      * </p>
      */
-    public static final int STATE_CANCELED = 6;
+    public static final int STATE_FAILED = 6;
+
+    /**
+     * Print job state: The print job was canceled.
+     * This is a terminal state.
+     * <p>
+     * Next valid states: None
+     * </p>
+     */
+    public static final int STATE_CANCELED = 7;
 
     /** The unique print job id. */
     private int mId;
@@ -127,8 +143,8 @@
     /** How many copies to print. */
     private int mCopies;
 
-    /** Failure reason if this job failed. */
-    private String mFailureReason;
+    /** Reason for the print job being in its current state. */
+    private String mStateReason;
 
     /** The pages to print */
     private PageRange[] mPageRanges;
@@ -155,7 +171,7 @@
         mUserId = other.mUserId;
         mTag = other.mTag;
         mCopies = other.mCopies;
-        mFailureReason = other.mFailureReason;
+        mStateReason = other.mStateReason;
         mPageRanges = other.mPageRanges;
         mAttributes = other.mAttributes;
         mDocumentInfo = other.mDocumentInfo;
@@ -171,7 +187,7 @@
         mUserId = parcel.readInt();
         mTag = parcel.readString();
         mCopies = parcel.readInt();
-        mFailureReason = parcel.readString();
+        mStateReason = parcel.readString();
         if (parcel.readInt() == 1) {
             Parcelable[] parcelables = parcel.readParcelableArray(null);
             mPageRanges = new PageRange[parcelables.length];
@@ -377,25 +393,27 @@
     }
 
     /**
-     * The failure reason if this print job failed.
+     * Gets the reason for the print job being in the current state.
      *
-     * @return The failure reason.
+     * @return The reason, or null if there is no reason or the
+     * reason is unknown.
      *
      * @hide
      */
-    public String getFailureReason() {
-        return mFailureReason;
+    public String getStateReason() {
+        return mStateReason;
     }
 
     /**
-     * The failure reason if this print job failed.
+     * Sets the reason for the print job being in the current state.
      *
-     * @param failureReason The failure reason.
+     * @param stateReason The reason, or null if there is no reason
+     * or the reason is unknown.
      *
      * @hide
      */
-    public void setFailureReason(String failureReason) {
-        mFailureReason = failureReason;
+    public void setStateReason(String stateReason) {
+        mStateReason = stateReason;
     }
 
     /**
@@ -476,7 +494,7 @@
         parcel.writeInt(mUserId);
         parcel.writeString(mTag);
         parcel.writeInt(mCopies);
-        parcel.writeString(mFailureReason);
+        parcel.writeString(mStateReason);
         if (mPageRanges != null) {
             parcel.writeInt(1);
             parcel.writeParcelableArray(mPageRanges, flags);
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java
index d3e35c3..6e32c05 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -36,7 +36,6 @@
 import libcore.io.IoUtils;
 
 import java.io.File;
-import java.io.FileDescriptor;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -163,7 +162,7 @@
      * @param pdfFile The PDF file to print.
      * @param documentInfo Information about the printed document.
      * @param attributes The default print job attributes.
-     * @return The created print job.
+     * @return The created print job on success or null on failure.
      *
      * @see PrintJob
      */
@@ -181,7 +180,7 @@
      * @param printJobName A name for the new print job.
      * @param documentAdapter An adapter that emits the document to print.
      * @param attributes The default print job attributes.
-     * @return The created print job.
+     * @return The created print job on success or null on failure.
      *
      * @see PrintJob
      */
@@ -279,7 +278,7 @@
             }
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = pages;
-            args.arg2 = fd.getFileDescriptor();
+            args.arg2 = fd;
             args.arg3 = callback;
             args.argi1 = sequence;
             mHandler.removeMessages(MyHandler.MSG_WRITE);
@@ -342,7 +341,7 @@
                     case MSG_WRITE: {
                         SomeArgs args = (SomeArgs) message.obj;
                         PageRange[] pages = (PageRange[]) args.arg1;
-                        FileDescriptor fd = (FileDescriptor) args.arg2;
+                        ParcelFileDescriptor fd = (ParcelFileDescriptor) args.arg2;
                         IWriteResultCallback callback = (IWriteResultCallback) args.arg3;
                         final int sequence = args.argi1;
                         args.recycle();
@@ -428,12 +427,12 @@
         }
 
         private final class MyWriteResultCallback extends WriteResultCallback {
-            private FileDescriptor mFd;
+            private ParcelFileDescriptor mFd;
             private int mSequence;
             private IWriteResultCallback mCallback;
 
             public MyWriteResultCallback(IWriteResultCallback callback,
-                    FileDescriptor fd, int sequence) {
+                    ParcelFileDescriptor fd, int sequence) {
                 mFd = fd;
                 mSequence = sequence;
                 mCallback = callback;
diff --git a/core/java/android/print/PrinterDiscoverySession.java b/core/java/android/print/PrinterDiscoverySession.java
index 8fbdd9c..46f0bef 100644
--- a/core/java/android/print/PrinterDiscoverySession.java
+++ b/core/java/android/print/PrinterDiscoverySession.java
@@ -74,6 +74,7 @@
     public final void startPrinterDisovery(List<PrinterId> priorityList) {
         if (isDestroyed()) {
             Log.w(LOG_TAG, "Ignoring start printers dsicovery - session destroyed");
+            return;
         }
         if (!mIsPrinterDiscoveryStarted) {
             mIsPrinterDiscoveryStarted = true;
@@ -88,6 +89,7 @@
     public final void stopPrinterDiscovery() {
         if (isDestroyed()) {
             Log.w(LOG_TAG, "Ignoring stop printers discovery - session destroyed");
+            return;
         }
         if (mIsPrinterDiscoveryStarted) {
             mIsPrinterDiscoveryStarted = false;
@@ -99,14 +101,39 @@
         }
     }
 
-    public final void requestPrinterUpdate(PrinterId printerId) {
+    public final void startPrinterStateTracking(PrinterId printerId) {
         if (isDestroyed()) {
-            Log.w(LOG_TAG, "Ignoring reqeust printer update - session destroyed");
+            Log.w(LOG_TAG, "Ignoring start printer state tracking - session destroyed");
+            return;
         }
         try {
-            mPrintManager.requestPrinterUpdate(printerId, mUserId);
+            mPrintManager.startPrinterStateTracking(printerId, mUserId);
         } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error requesting printer update", re);
+            Log.e(LOG_TAG, "Error starting printer state tracking", re);
+        }
+    }
+
+    public final void stopPrinterStateTracking(PrinterId printerId) {
+        if (isDestroyed()) {
+            Log.w(LOG_TAG, "Ignoring stop printer state tracking - session destroyed");
+            return;
+        }
+        try {
+            mPrintManager.stopPrinterStateTracking(printerId, mUserId);
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error stoping printer state tracking", re);
+        }
+    }
+
+    public final void validatePrinters(List<PrinterId> printerIds) {
+        if (isDestroyed()) {
+            Log.w(LOG_TAG, "Ignoring validate printers - session destroyed");
+            return;
+        }
+        try {
+            mPrintManager.validatePrinters(printerIds, mUserId);
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error validating printers", re);
         }
     }
 
diff --git a/core/java/android/print/pdf/PrintedPdfDocument.java b/core/java/android/print/pdf/PrintedPdfDocument.java
index a3be38b..bee17ef 100644
--- a/core/java/android/print/pdf/PrintedPdfDocument.java
+++ b/core/java/android/print/pdf/PrintedPdfDocument.java
@@ -111,7 +111,10 @@
      * @see #finishPage(Page)
      */
     public Page startPage(int pageNumber) {
-        PageInfo pageInfo = new PageInfo.Builder(mPageSize, 0).create();
+        PageInfo pageInfo = new PageInfo
+                .Builder(mPageSize, 0)
+                .setContentSize(mContentSize)
+                .create();
         Page page = mDocument.startPage(pageInfo);
         return page;
     }
diff --git a/core/java/android/printservice/IPrintService.aidl b/core/java/android/printservice/IPrintService.aidl
index 2cee1d8..ee36619 100644
--- a/core/java/android/printservice/IPrintService.aidl
+++ b/core/java/android/printservice/IPrintService.aidl
@@ -33,6 +33,8 @@
     void createPrinterDiscoverySession();
     void startPrinterDiscovery(in List<PrinterId> priorityList);
     void stopPrinterDiscovery();
-    void requestPrinterUpdate(in PrinterId printerId);
+    void validatePrinters(in List<PrinterId> printerIds);
+    void startPrinterStateTracking(in PrinterId printerId);
+    void stopPrinterStateTracking(in PrinterId printerId);
     void destroyPrinterDiscoverySession();
 }
diff --git a/core/java/android/printservice/PrintDocument.java b/core/java/android/printservice/PrintDocument.java
index 7437dc5..8292cfbc 100644
--- a/core/java/android/printservice/PrintDocument.java
+++ b/core/java/android/printservice/PrintDocument.java
@@ -21,12 +21,15 @@
 import android.print.PrintDocumentInfo;
 import android.util.Log;
 
-import java.io.FileDescriptor;
 import java.io.IOException;
 
 /**
  * This class represents a printed document from the perspective of a print
  * service. It exposes APIs to query the document and obtain its data.
+ * <p>
+ * <strong>Note: </strong> All methods of this class must be executed on the
+ * main application thread.
+ * </p>
  */
 public final class PrintDocument {
 
@@ -51,6 +54,7 @@
      * @return The document info.
      */
     public PrintDocumentInfo getInfo() {
+        PrintService.throwIfNotCalledOnMainThread();
         return mInfo;
     }
 
@@ -64,7 +68,8 @@
      *
      * @return A file descriptor for reading the data.
      */
-    public FileDescriptor getData() {
+    public ParcelFileDescriptor getData() {
+        PrintService.throwIfNotCalledOnMainThread();
         ParcelFileDescriptor source = null;
         ParcelFileDescriptor sink = null;
         try {
@@ -72,7 +77,7 @@
             source = fds[0];
             sink = fds[1];
             mPrintServiceClient.writePrintJobData(sink, mPrintJobId);
-            return source.getFileDescriptor();
+            return source;
         } catch (IOException ioe) {
             Log.e(LOG_TAG, "Error calling getting print job data!", ioe);
         } catch (RemoteException re) {
diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java
index d2fbef2..8bae9d6 100644
--- a/core/java/android/printservice/PrintJob.java
+++ b/core/java/android/printservice/PrintJob.java
@@ -18,6 +18,7 @@
 
 import android.os.RemoteException;
 import android.print.PrintJobInfo;
+import android.text.TextUtils;
 import android.util.Log;
 
 /**
@@ -123,6 +124,21 @@
     }
 
     /**
+     * Gets whether this print job is blocked. Such a print job is halted
+     * due to an abnormal condition and can be started or canceled or failed.
+     *
+     * @return Whether the print job is blocked.
+     *
+     * @see #start()
+     * @see #cancel()
+     * @see #fail(CharSequence)
+     */
+    public boolean isBlocked() {
+        PrintService.throwIfNotCalledOnMainThread();
+        return getInfo().getState() == PrintJobInfo.STATE_BLOCKED;
+    }
+
+    /**
      * Gets whether this print job is completed. Such a print job
      * is successfully printed. This is a final state.
      *
@@ -163,21 +179,49 @@
 
     /**
      * Starts the print job. You should call this method if {@link
-     * #isQueued()} returns true and you started printing.
+     * #isQueued()} or {@link #isBlocked()} returns true and you started
+     * resumed printing.
      *
-     * @return Whether the job as started.
+     * @return Whether the job was started.
      *
      * @see #isQueued()
+     * @see #isBlocked()
      */
     public boolean start() {
         PrintService.throwIfNotCalledOnMainThread();
-        if (isQueued()) {
+        final int state = getInfo().getState();
+        if (state == PrintJobInfo.STATE_QUEUED
+                || state == PrintJobInfo.STATE_BLOCKED) {
             return setState(PrintJobInfo.STATE_STARTED, null);
         }
         return false;
     }
 
     /**
+     * Blocks the print job. You should call this method if {@link
+     * #isStarted()} or {@link #isBlocked()} returns true and you need
+     * to block the print job. For example, the user has to add some
+     * paper to continue printing. To resume the print job call {@link
+     * #start()}.
+     *
+     * @return Whether the job was blocked.
+     *
+     * @see #isStarted()
+     * @see #isBlocked()
+     */
+    public boolean block(String reason) {
+        PrintService.throwIfNotCalledOnMainThread();
+        PrintJobInfo info = getInfo();
+        final int state = info.getState();
+        if (state == PrintJobInfo.STATE_STARTED
+                || (state == PrintJobInfo.STATE_BLOCKED
+                        && !TextUtils.equals(info.getStateReason(), reason))) {
+            return setState(PrintJobInfo.STATE_BLOCKED, reason);
+        }
+        return false;
+    }
+
+    /**
      * Completes the print job. You should call this method if {@link
      * #isStarted()} returns true and you are done printing.
      *
@@ -195,8 +239,8 @@
 
     /**
      * Fails the print job. You should call this method if {@link
-     * #isQueued()} or {@link #isStarted()} returns true you failed
-     * while printing.
+     * #isQueued()} or {@link #isStarted()} or {@link #isBlocked()}
+     * returns true you failed while printing.
      *
      * @param error The human readable, short, and translated reason
      * for the failure.
@@ -204,10 +248,11 @@
      *
      * @see #isQueued()
      * @see #isStarted()
+     * @see #isBlocked()
      */
     public boolean fail(String error) {
         PrintService.throwIfNotCalledOnMainThread();
-        if (isQueued() || isStarted()) {
+        if (!isInImmutableState()) {
             return setState(PrintJobInfo.STATE_FAILED, error);
         }
         return false;
@@ -215,18 +260,19 @@
 
     /**
      * Cancels the print job. You should call this method if {@link
-     * #isQueued()} or {@link #isStarted()} returns true and you canceled
-     * the print job as a response to a call to {@link
-     * PrintService#onRequestCancelPrintJob(PrintJob)}.
+     * #isQueued()} or {@link #isStarted() or #isBlocked()} returns
+     * true and you canceled the print job as a response to a call to
+     * {@link PrintService#onRequestCancelPrintJob(PrintJob)}.
      *
      * @return Whether the job is canceled.
      *
      * @see #isStarted()
      * @see #isQueued()
+     * @see #isBlocked()
      */
     public boolean cancel() {
         PrintService.throwIfNotCalledOnMainThread();
-        if (isQueued() || isStarted()) {
+        if (!isInImmutableState()) {
             return setState(PrintJobInfo.STATE_CANCELED, null);
         }
         return false;
@@ -277,7 +323,8 @@
     private boolean isInImmutableState() {
         final int state = mCachedInfo.getState();
         return state == PrintJobInfo.STATE_COMPLETED
-                || state == PrintJobInfo.STATE_CANCELED;
+                || state == PrintJobInfo.STATE_CANCELED
+                || state == PrintJobInfo.STATE_FAILED;
     }
 
     private boolean setState(int state, String error) {
@@ -287,7 +334,7 @@
                 // we may not be able to re-fetch it later if the job gets
                 // removed from the spooler as a result of the state change.
                 mCachedInfo.setState(state);
-                mCachedInfo.setFailureReason(error);
+                mCachedInfo.setStateReason(error);
                 return true;
             }
         } catch (RemoteException re) {
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index f6c0a9a..96552af 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -314,8 +314,20 @@
             }
 
             @Override
-            public void requestPrinterUpdate(PrinterId printerId) {
-                mHandler.obtainMessage(ServiceHandler.MSG_REQUEST_PRINTER_UPDATE,
+            public void validatePrinters(List<PrinterId> printerIds) {
+                mHandler.obtainMessage(ServiceHandler.MSG_VALIDATE_PRINTERS,
+                        printerIds).sendToTarget();
+            }
+
+            @Override
+            public void startPrinterStateTracking(PrinterId printerId) {
+                mHandler.obtainMessage(ServiceHandler.MSG_START_PRINTER_STATE_TRACKING,
+                        printerId).sendToTarget();
+            }
+
+            @Override
+            public void stopPrinterStateTracking(PrinterId printerId) {
+                mHandler.obtainMessage(ServiceHandler.MSG_STOP_PRINTER_STATE_TRACKING,
                         printerId).sendToTarget();
             }
 
@@ -344,10 +356,12 @@
         public static final int MSG_DESTROY_PRINTER_DISCOVERY_SESSION = 2;
         public static final int MSG_START_PRINTER_DISCOVERY = 3;
         public static final int MSG_STOP_PRINTER_DISCOVERY = 4;
-        public static final int MSG_REQUEST_PRINTER_UPDATE = 5;
-        public static final int MSG_ON_PRINTJOB_QUEUED = 6;
-        public static final int MSG_ON_REQUEST_CANCEL_PRINTJOB = 7;
-        public static final int MSG_SET_CLEINT = 8;
+        public static final int MSG_VALIDATE_PRINTERS = 5;
+        public static final int MSG_START_PRINTER_STATE_TRACKING = 6;
+        public static final int MSG_STOP_PRINTER_STATE_TRACKING = 7;
+        public static final int MSG_ON_PRINTJOB_QUEUED = 8;
+        public static final int MSG_ON_REQUEST_CANCEL_PRINTJOB = 9;
+        public static final int MSG_SET_CLEINT = 10;
 
         public ServiceHandler(Looper looper) {
             super(looper, null, true);
@@ -391,10 +405,24 @@
                     }
                 } break;
 
-                case MSG_REQUEST_PRINTER_UPDATE: {
+                case MSG_VALIDATE_PRINTERS: {
+                    if (mDiscoverySession != null) {
+                        List<PrinterId> printerIds = (List<PrinterId>) message.obj;
+                        mDiscoverySession.validatePrinters(printerIds);
+                    }
+                } break;
+
+                case MSG_START_PRINTER_STATE_TRACKING: {
                     if (mDiscoverySession != null) {
                         PrinterId printerId = (PrinterId) message.obj;
-                        mDiscoverySession.requestPrinterUpdate(printerId);
+                        mDiscoverySession.startPrinterStateTracking(printerId);
+                    }
+                } break;
+
+                case MSG_STOP_PRINTER_STATE_TRACKING: {
+                    if (mDiscoverySession != null) {
+                        PrinterId printerId = (PrinterId) message.obj;
+                        mDiscoverySession.stopPrinterStateTracking(printerId);
                     }
                 } break;
 
diff --git a/core/java/android/printservice/PrinterDiscoverySession.java b/core/java/android/printservice/PrinterDiscoverySession.java
index 8b959a6..1f86ecc 100644
--- a/core/java/android/printservice/PrinterDiscoverySession.java
+++ b/core/java/android/printservice/PrinterDiscoverySession.java
@@ -53,15 +53,23 @@
  * session. Printers are <strong>not</strong> persisted across sessions.
  * </p>
  * <p>
- * The system will make a call to
- * {@link PrinterDiscoverySession#onRequestPrinterUpdate(PrinterId)} if you
- * need to update a given printer. It is possible that you add a printer without
+ * The system will make a call to {@link #onValidatePrinters(List)} if you
+ * need to update some printers. It is possible that you add a printer without
  * specifying its capabilities. This enables you to avoid querying all discovered
  * printers for their capabilities, rather querying the capabilities of a printer
  * only if necessary. For example, the system will request that you update a printer
- * if it gets selected by the user. If you did not report the printer capabilities
- * when adding it, you must do so after the system requests a printer update.
- * Otherwise, the printer will be ignored.
+ * if it gets selected by the user. When validating printers you do not need to
+ * provide the printers' capabilities but may do so.
+ * </p>
+ * <p>
+ * If the system is interested in being constantly updated for the state of a
+ * printer you will receive a call to {@link #onStartPrinterStateTracking(PrinterId)}
+ * after which you will have to do a best effort to keep the system updated for
+ * changes in the printer state and capabilities. You also <strong>must</strong>
+ * update the printer capabilities if you did not provide them when adding it, or
+ * the printer will be ignored. When the system is no longer interested in getting
+ * updates for a printer you will receive a call to {@link #onStopPrinterStateTracking(
+ * PrinterId)}.
  * </p>
  * <p>
  * <strong>Note: </strong> All callbacks in this class are executed on the main
@@ -115,7 +123,7 @@
      * the printer that was added but not removed.
      * <p>
      * <strong>Note: </strong> Calls to this method after the session is
-     * destroyed, i.e. after the {@link #onDestroy()} callback, will be ignored.
+     * destroyed, that is after the {@link #onDestroy()} callback, will be ignored.
      * </p>
      *
      * @return The printers.
@@ -139,7 +147,7 @@
      * times during the life of this session. Duplicates will be ignored.
      * <p>
      * <strong>Note: </strong> Calls to this method after the session is
-     * destroyed, i.e. after the {@link #onDestroy()} callback, will be ignored.
+     * destroyed, that is after the {@link #onDestroy()} callback, will be ignored.
      * </p>
      *
      * @param printers The printers to add.
@@ -218,7 +226,7 @@
      * call this method multiple times during the lifetime of this session.
      * <p>
      * <strong>Note: </strong> Calls to this method after the session is
-     * destroyed, i.e. after the {@link #onDestroy()} callback, will be ignored.
+     * destroyed, that is after the {@link #onDestroy()} callback, will be ignored.
      * </p>
      *
      * @param printerIds The ids of the removed printers.
@@ -293,7 +301,7 @@
      * during the lifetime of this session.
      * <p>
      * <strong>Note: </strong> Calls to this method after the session is
-     * destroyed, i.e. after the {@link #onDestroy()} callback, will be ignored.
+     * destroyed, that is after the {@link #onDestroy()} callback, will be ignored.
      * </p>
      *
      * @param printers The printers to update.
@@ -441,7 +449,9 @@
      * <p>
      * <strong>Note: </strong>You are also given a list of printers whose availability
      * has to be checked first. For example, these printers could be the user's favorite
-     * ones, therefore they have to be verified first.
+     * ones, therefore they have to be verified first. You do <strong>not need</strong>
+     * to provide the capabilities of the printers, rather verify whether they exist
+     * similarly to {@link #onValidatePrinters(List)}.
      * </p>
      *
      * @param priorityList The list of printers to validate first. Never null.
@@ -463,9 +473,28 @@
     public abstract void onStopPrinterDiscovery();
 
     /**
-     * Requests that you update a printer. You are responsible for updating
-     * the printer by also reporting its capabilities via calling {@link
-     * #updatePrinters(List)}.
+     * Callback asking you to validate that the given printers are valid, that
+     * is they exist. You are responsible for checking whether these printers
+     * exist and for the ones that do exist notify the system via calling
+     * {@link #updatePrinters(List)}.
+     * <p>
+     * <strong>Note: </strong> You are <strong>not required</strong> to provide
+     * the printer capabilities when updating the printers that do exist.
+     * <p>
+     *
+     * @param printerIds The printers to validate.
+     *
+     * @see #updatePrinters(List)
+     * @see PrinterInfo.Builder#setCapabilities(PrinterCapabilitiesInfo)
+     *      PrinterInfo.Builder.setCapabilities(PrinterCapabilitiesInfo)
+     */
+    public abstract void onValidatePrinters(List<PrinterId> printerIds);
+
+    /**
+     * Callback asking you to start tracking the state of a printer. Tracking
+     * the state means that you should do a best effort to observe the state
+     * of this printer and notify the system if that state changes via calling
+     * {@link #updatePrinters(List)}.
      * <p>
      * <strong>Note: </strong> A printer can be initially added without its
      * capabilities to avoid polling printers that the user will not select.
@@ -473,18 +502,33 @@
      * printer <strong>including</strong> its capabilities. Otherwise, the
      * printer will be ignored.
      * <p>
-     * A scenario when you may be requested to update a printer is if the user
-     * selects it and the system has to present print options UI based on the
-     * printer's capabilities.
+     * <p>
+     * A scenario when you may be requested to track a printer's state is if
+     * the user selects that printer and the system has to present print
+     * options UI based on the printer's capabilities. In this case the user
+     * should be promptly informed if, for example, the printer becomes
+     * unavailable.
      * </p>
      *
-     * @param printerId The printer id.
+     * @param printerId The printer to start tracking.
      *
+     * @see #onStopPrinterStateTracking(PrinterId)
      * @see #updatePrinters(List)
      * @see PrinterInfo.Builder#setCapabilities(PrinterCapabilitiesInfo)
      *      PrinterInfo.Builder.setCapabilities(PrinterCapabilitiesInfo)
      */
-    public abstract void onRequestPrinterUpdate(PrinterId printerId);
+    public abstract void onStartPrinterStateTracking(PrinterId printerId);
+
+    /**
+     * Callback asking you to stop tracking the state of a printer. The passed
+     * in printer id is the one for which you received a call to {@link
+     * #onStartPrinterStateTracking(PrinterId)}.
+     *
+     * @param printerId The printer to stop tracking.
+     *
+     * @see #onStartPrinterStateTracking(PrinterId)
+     */
+    public abstract void onStopPrinterStateTracking(PrinterId printerId);
 
     /**
      * Notifies you that the session is destroyed. After this callback is invoked
@@ -538,9 +582,21 @@
         }
     }
 
-    void requestPrinterUpdate(PrinterId printerId) {
-        if (!mIsDestroyed) {
-            onRequestPrinterUpdate(printerId);
+    void validatePrinters(List<PrinterId> printerIds) {
+        if (!mIsDestroyed && mObserver != null) {
+            onValidatePrinters(printerIds);
+        }
+    }
+
+    void startPrinterStateTracking(PrinterId printerId) {
+        if (!mIsDestroyed && mObserver != null) {
+            onStartPrinterStateTracking(printerId);
+        }
+    }
+
+    void stopPrinterStateTracking(PrinterId printerId) {
+        if (!mIsDestroyed && mObserver != null) {
+            onStopPrinterStateTracking(printerId);
         }
     }
 
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index 109fcfe..54cc3f4 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -251,14 +251,14 @@
 
     // Instance variables
 
-    final Axis horizontalAxis = new Axis(true);
-    final Axis verticalAxis = new Axis(false);
-    int orientation = DEFAULT_ORIENTATION;
-    boolean useDefaultMargins = DEFAULT_USE_DEFAULT_MARGINS;
-    int alignmentMode = DEFAULT_ALIGNMENT_MODE;
-    int defaultGap;
-    int lastLayoutParamsHashCode = UNINITIALIZED_HASH;
-    Printer printer = LOG_PRINTER;
+    final Axis mHorizontalAxis = new Axis(true);
+    final Axis mVerticalAxis = new Axis(false);
+    int mOrientation = DEFAULT_ORIENTATION;
+    boolean mUseDefaultMargins = DEFAULT_USE_DEFAULT_MARGINS;
+    int mAlignmentMode = DEFAULT_ALIGNMENT_MODE;
+    int mDefaultGap;
+    int mLastLayoutParamsHashCode = UNINITIALIZED_HASH;
+    Printer mPrinter = LOG_PRINTER;
 
     // Constructors
 
@@ -267,7 +267,7 @@
      */
     public GridLayout(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
-        defaultGap = context.getResources().getDimensionPixelOffset(R.dimen.default_gap);
+        mDefaultGap = context.getResources().getDimensionPixelOffset(R.dimen.default_gap);
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GridLayout);
         try {
             setRowCount(a.getInt(ROW_COUNT, DEFAULT_COUNT));
@@ -309,7 +309,7 @@
      * @attr ref android.R.styleable#GridLayout_orientation
      */
     public int getOrientation() {
-        return orientation;
+        return mOrientation;
     }
 
     /**
@@ -349,8 +349,8 @@
      * @attr ref android.R.styleable#GridLayout_orientation
      */
     public void setOrientation(int orientation) {
-        if (this.orientation != orientation) {
-            this.orientation = orientation;
+        if (this.mOrientation != orientation) {
+            this.mOrientation = orientation;
             invalidateStructure();
             requestLayout();
         }
@@ -369,7 +369,7 @@
      * @attr ref android.R.styleable#GridLayout_rowCount
      */
     public int getRowCount() {
-        return verticalAxis.getCount();
+        return mVerticalAxis.getCount();
     }
 
     /**
@@ -384,7 +384,7 @@
      * @attr ref android.R.styleable#GridLayout_rowCount
      */
     public void setRowCount(int rowCount) {
-        verticalAxis.setCount(rowCount);
+        mVerticalAxis.setCount(rowCount);
         invalidateStructure();
         requestLayout();
     }
@@ -402,7 +402,7 @@
      * @attr ref android.R.styleable#GridLayout_columnCount
      */
     public int getColumnCount() {
-        return horizontalAxis.getCount();
+        return mHorizontalAxis.getCount();
     }
 
     /**
@@ -417,7 +417,7 @@
      * @attr ref android.R.styleable#GridLayout_columnCount
      */
     public void setColumnCount(int columnCount) {
-        horizontalAxis.setCount(columnCount);
+        mHorizontalAxis.setCount(columnCount);
         invalidateStructure();
         requestLayout();
     }
@@ -433,7 +433,7 @@
      * @attr ref android.R.styleable#GridLayout_useDefaultMargins
      */
     public boolean getUseDefaultMargins() {
-        return useDefaultMargins;
+        return mUseDefaultMargins;
     }
 
     /**
@@ -463,7 +463,7 @@
      * @attr ref android.R.styleable#GridLayout_useDefaultMargins
      */
     public void setUseDefaultMargins(boolean useDefaultMargins) {
-        this.useDefaultMargins = useDefaultMargins;
+        this.mUseDefaultMargins = useDefaultMargins;
         requestLayout();
     }
 
@@ -480,7 +480,7 @@
      * @attr ref android.R.styleable#GridLayout_alignmentMode
      */
     public int getAlignmentMode() {
-        return alignmentMode;
+        return mAlignmentMode;
     }
 
     /**
@@ -499,7 +499,7 @@
      * @attr ref android.R.styleable#GridLayout_alignmentMode
      */
     public void setAlignmentMode(int alignmentMode) {
-        this.alignmentMode = alignmentMode;
+        this.mAlignmentMode = alignmentMode;
         requestLayout();
     }
 
@@ -514,7 +514,7 @@
      * @attr ref android.R.styleable#GridLayout_rowOrderPreserved
      */
     public boolean isRowOrderPreserved() {
-        return verticalAxis.isOrderPreserved();
+        return mVerticalAxis.isOrderPreserved();
     }
 
     /**
@@ -534,7 +534,7 @@
      * @attr ref android.R.styleable#GridLayout_rowOrderPreserved
      */
     public void setRowOrderPreserved(boolean rowOrderPreserved) {
-        verticalAxis.setOrderPreserved(rowOrderPreserved);
+        mVerticalAxis.setOrderPreserved(rowOrderPreserved);
         invalidateStructure();
         requestLayout();
     }
@@ -550,7 +550,7 @@
      * @attr ref android.R.styleable#GridLayout_columnOrderPreserved
      */
     public boolean isColumnOrderPreserved() {
-        return horizontalAxis.isOrderPreserved();
+        return mHorizontalAxis.isOrderPreserved();
     }
 
     /**
@@ -570,7 +570,7 @@
      * @attr ref android.R.styleable#GridLayout_columnOrderPreserved
      */
     public void setColumnOrderPreserved(boolean columnOrderPreserved) {
-        horizontalAxis.setOrderPreserved(columnOrderPreserved);
+        mHorizontalAxis.setOrderPreserved(columnOrderPreserved);
         invalidateStructure();
         requestLayout();
     }
@@ -581,9 +581,11 @@
      * @see #setPrinter(android.util.Printer)
      *
      * @return the printer associated with this view
+     *
+     * @hide
      */
     public Printer getPrinter() {
-        return printer;
+        return mPrinter;
     }
 
     /**
@@ -593,9 +595,11 @@
      * @param printer the printer associated with this layout
      *
      * @see #getPrinter()
+     *
+     * @hide
      */
     public void setPrinter(Printer printer) {
-        this.printer = (printer == null) ? NO_PRINTER : printer;
+        this.mPrinter = (printer == null) ? NO_PRINTER : printer;
     }
 
     // Static utility methods
@@ -643,7 +647,7 @@
         if (c.getClass() == Space.class) {
             return 0;
         }
-        return defaultGap / 2;
+        return mDefaultGap / 2;
     }
 
     private int getDefaultMargin(View c, boolean isAtEdge, boolean horizontal, boolean leading) {
@@ -651,11 +655,11 @@
     }
 
     private int getDefaultMargin(View c, LayoutParams p, boolean horizontal, boolean leading) {
-        if (!useDefaultMargins) {
+        if (!mUseDefaultMargins) {
             return 0;
         }
         Spec spec = horizontal ? p.columnSpec : p.rowSpec;
-        Axis axis = horizontal ? horizontalAxis : verticalAxis;
+        Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
         Interval span = spec.span;
         boolean leading1 = (horizontal && isLayoutRtl()) ? !leading : leading;
         boolean isAtEdge = leading1 ? (span.min == 0) : (span.max == axis.getCount());
@@ -672,10 +676,10 @@
     }
 
     private int getMargin(View view, boolean horizontal, boolean leading) {
-        if (alignmentMode == ALIGN_MARGINS) {
+        if (mAlignmentMode == ALIGN_MARGINS) {
             return getMargin1(view, horizontal, leading);
         } else {
-            Axis axis = horizontal ? horizontalAxis : verticalAxis;
+            Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
             int[] margins = leading ? axis.getLeadingMargins() : axis.getTrailingMargins();
             LayoutParams lp = getLayoutParams(view);
             Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
@@ -722,8 +726,8 @@
 
     // install default indices for cells that don't define them
     private void validateLayoutParams() {
-        final boolean horizontal = (orientation == HORIZONTAL);
-        final Axis axis = horizontal ? horizontalAxis : verticalAxis;
+        final boolean horizontal = (mOrientation == HORIZONTAL);
+        final Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
         final int count = (axis.definedCount != UNDEFINED) ? axis.definedCount : 0;
 
         int major = 0;
@@ -779,9 +783,9 @@
     }
 
     private void invalidateStructure() {
-        lastLayoutParamsHashCode = UNINITIALIZED_HASH;
-        horizontalAxis.invalidateStructure();
-        verticalAxis.invalidateStructure();
+        mLastLayoutParamsHashCode = UNINITIALIZED_HASH;
+        mHorizontalAxis.invalidateStructure();
+        mVerticalAxis.invalidateStructure();
         // This can end up being done twice. Better twice than not at all.
         invalidateValues();
     }
@@ -789,9 +793,9 @@
     private void invalidateValues() {
         // Need null check because requestLayout() is called in View's initializer,
         // before we are set up.
-        if (horizontalAxis != null && verticalAxis != null) {
-            horizontalAxis.invalidateValues();
-            verticalAxis.invalidateValues();
+        if (mHorizontalAxis != null && mVerticalAxis != null) {
+            mHorizontalAxis.invalidateValues();
+            mVerticalAxis.invalidateValues();
         }
     }
 
@@ -822,7 +826,7 @@
         if (span.min != UNDEFINED && span.min < 0) {
             handleInvalidParams(groupName + " indices must be positive");
         }
-        Axis axis = horizontal ? horizontalAxis : verticalAxis;
+        Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
         int count = axis.definedCount;
         if (count != UNDEFINED) {
             if (span.max > count) {
@@ -908,7 +912,7 @@
         int right  = getWidth()  - getPaddingRight()  - insets.right;
         int bottom = getHeight() - getPaddingBottom() - insets.bottom;
 
-        int[] xs = horizontalAxis.locations;
+        int[] xs = mHorizontalAxis.locations;
         if (xs != null) {
             for (int i = 0, length = xs.length; i < length; i++) {
                 int x = left + xs[i];
@@ -916,7 +920,7 @@
             }
         }
 
-        int[] ys = verticalAxis.locations;
+        int[] ys = mVerticalAxis.locations;
         if (ys != null) {
             for (int i = 0, length = ys.length; i < length; i++) {
                 int y = top + ys[i];
@@ -973,11 +977,11 @@
     }
 
     private void consistencyCheck() {
-        if (lastLayoutParamsHashCode == UNINITIALIZED_HASH) {
+        if (mLastLayoutParamsHashCode == UNINITIALIZED_HASH) {
             validateLayoutParams();
-            lastLayoutParamsHashCode = computeLayoutParamsHashCode();
-        } else if (lastLayoutParamsHashCode != computeLayoutParamsHashCode()) {
-            printer.println("The fields of some layout parameters were modified in between "
+            mLastLayoutParamsHashCode = computeLayoutParamsHashCode();
+        } else if (mLastLayoutParamsHashCode != computeLayoutParamsHashCode()) {
+            mPrinter.println("The fields of some layout parameters were modified in between "
                     + "layout operations. Check the javadoc for GridLayout.LayoutParams#rowSpec.");
             invalidateStructure();
             consistencyCheck();
@@ -1005,11 +1009,11 @@
             if (firstPass) {
                 measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, lp.height);
             } else {
-                boolean horizontal = (orientation == HORIZONTAL);
+                boolean horizontal = (mOrientation == HORIZONTAL);
                 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
                 if (spec.alignment == FILL) {
                     Interval span = spec.span;
-                    Axis axis = horizontal ? horizontalAxis : verticalAxis;
+                    Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
                     int[] locations = axis.getLocations();
                     int cellSize = locations[span.max] - locations[span.min];
                     int viewSize = cellSize - getTotalMargin(c, horizontal);
@@ -1048,14 +1052,14 @@
         int heightSansPadding;
 
         // Use the orientation property to decide which axis should be laid out first.
-        if (orientation == HORIZONTAL) {
-            widthSansPadding = horizontalAxis.getMeasure(widthSpecSansPadding);
+        if (mOrientation == HORIZONTAL) {
+            widthSansPadding = mHorizontalAxis.getMeasure(widthSpecSansPadding);
             measureChildrenWithMargins(widthSpecSansPadding, heightSpecSansPadding, false);
-            heightSansPadding = verticalAxis.getMeasure(heightSpecSansPadding);
+            heightSansPadding = mVerticalAxis.getMeasure(heightSpecSansPadding);
         } else {
-            heightSansPadding = verticalAxis.getMeasure(heightSpecSansPadding);
+            heightSansPadding = mVerticalAxis.getMeasure(heightSpecSansPadding);
             measureChildrenWithMargins(widthSpecSansPadding, heightSpecSansPadding, false);
-            widthSansPadding = horizontalAxis.getMeasure(widthSpecSansPadding);
+            widthSansPadding = mHorizontalAxis.getMeasure(widthSpecSansPadding);
         }
 
         int measuredWidth  = Math.max(widthSansPadding  + hPadding, getSuggestedMinimumWidth());
@@ -1114,11 +1118,11 @@
         int paddingRight = getPaddingRight();
         int paddingBottom = getPaddingBottom();
 
-        horizontalAxis.layout(targetWidth - paddingLeft - paddingRight);
-        verticalAxis.layout(targetHeight - paddingTop - paddingBottom);
+        mHorizontalAxis.layout(targetWidth - paddingLeft - paddingRight);
+        mVerticalAxis.layout(targetHeight - paddingTop - paddingBottom);
 
-        int[] hLocations = horizontalAxis.getLocations();
-        int[] vLocations = verticalAxis.getLocations();
+        int[] hLocations = mHorizontalAxis.getLocations();
+        int[] vLocations = mVerticalAxis.getLocations();
 
         for (int i = 0, N = getChildCount(); i < N; i++) {
             View c = getChildAt(i);
@@ -1145,8 +1149,8 @@
             Alignment hAlign = getAlignment(columnSpec.alignment, true);
             Alignment vAlign = getAlignment(rowSpec.alignment, false);
 
-            Bounds boundsX = horizontalAxis.getGroupBounds().getValue(i);
-            Bounds boundsY = verticalAxis.getGroupBounds().getValue(i);
+            Bounds boundsX = mHorizontalAxis.getGroupBounds().getValue(i);
+            Bounds boundsY = mVerticalAxis.getGroupBounds().getValue(i);
 
             // Gravity offsets: the location of the alignment group relative to its cell group.
             int gravityOffsetX = hAlign.getGravityOffset(c, cellWidth - boundsX.size(true));
@@ -1571,7 +1575,7 @@
                     removed.add(arc);
                 }
             }
-            printer.println(axisName + " constraints: " + arcsToString(culprits) +
+            mPrinter.println(axisName + " constraints: " + arcsToString(culprits) +
                     " are inconsistent; permanently removing: " + arcsToString(removed) + ". ");
         }
 
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index aa94728..ab81a37 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -22,11 +22,13 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
+import android.app.AppGlobals;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
+import android.content.pm.IPackageManager;
 import android.content.pm.LabeledIntent;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -45,7 +47,6 @@
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
 import android.widget.Button;
-import android.widget.GridView;
 import android.widget.ImageView;
 import android.widget.ListView;
 import android.widget.TextView;
@@ -71,13 +72,13 @@
     private PackageManager mPm;
     private boolean mAlwaysUseOption;
     private boolean mShowExtended;
-    private GridView mGrid;
+    private ListView mListView;
     private Button mAlwaysButton;
     private Button mOnceButton;
     private int mIconDpi;
     private int mIconSize;
     private int mMaxColumns;
-    private int mLastSelected = GridView.INVALID_POSITION;
+    private int mLastSelected = ListView.INVALID_POSITION;
 
     private boolean mRegistered;
     private final PackageMonitor mPackageMonitor = new PackageMonitor() {
@@ -139,17 +140,15 @@
             finish();
             return;
         } else if (count > 1) {
-            ap.mView = getLayoutInflater().inflate(R.layout.resolver_grid, null);
-            mGrid = (GridView) ap.mView.findViewById(R.id.resolver_grid);
-            mGrid.setAdapter(mAdapter);
-            mGrid.setOnItemClickListener(this);
-            mGrid.setOnItemLongClickListener(new ItemLongClickListener());
+            ap.mView = getLayoutInflater().inflate(R.layout.resolver_list, null);
+            mListView = (ListView) ap.mView.findViewById(R.id.resolver_list);
+            mListView.setAdapter(mAdapter);
+            mListView.setOnItemClickListener(this);
+            mListView.setOnItemLongClickListener(new ItemLongClickListener());
 
             if (alwaysUseOption) {
-                mGrid.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+                mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
             }
-
-            resizeGrid();
         } else if (count == 1) {
             startActivity(mAdapter.intentForPosition(0));
             mPackageMonitor.unregister();
@@ -172,11 +171,11 @@
                 mAlwaysUseOption = false;
             }
         }
-    }
-
-    void resizeGrid() {
-        final int itemCount = mAdapter.getCount();
-        mGrid.setNumColumns(Math.min(itemCount, mMaxColumns));
+        final int initialHighlight = mAdapter.getInitialHighlight();
+        if (initialHighlight >= 0) {
+            mListView.setItemChecked(initialHighlight, true);
+            onItemClick(null, null, initialHighlight, 0); // Other entries are not used
+        }
     }
 
     Drawable getIcon(Resources res, int resId) {
@@ -247,26 +246,26 @@
     protected void onRestoreInstanceState(Bundle savedInstanceState) {
         super.onRestoreInstanceState(savedInstanceState);
         if (mAlwaysUseOption) {
-            final int checkedPos = mGrid.getCheckedItemPosition();
-            final boolean enabled = checkedPos != GridView.INVALID_POSITION;
+            final int checkedPos = mListView.getCheckedItemPosition();
+            final boolean enabled = checkedPos != ListView.INVALID_POSITION;
             mLastSelected = checkedPos;
             mAlwaysButton.setEnabled(enabled);
             mOnceButton.setEnabled(enabled);
             if (enabled) {
-                mGrid.setSelection(checkedPos);
+                mListView.setSelection(checkedPos);
             }
         }
     }
 
     @Override
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-        final int checkedPos = mGrid.getCheckedItemPosition();
-        final boolean hasValidSelection = checkedPos != GridView.INVALID_POSITION;
+        final int checkedPos = mListView.getCheckedItemPosition();
+        final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
         if (mAlwaysUseOption && (!hasValidSelection || mLastSelected != checkedPos)) {
             mAlwaysButton.setEnabled(hasValidSelection);
             mOnceButton.setEnabled(hasValidSelection);
             if (hasValidSelection) {
-                mGrid.smoothScrollToPosition(checkedPos);
+                mListView.smoothScrollToPosition(checkedPos);
             }
             mLastSelected = checkedPos;
         } else {
@@ -276,7 +275,7 @@
 
     public void onButtonClick(View v) {
         final int id = v.getId();
-        startSelected(mGrid.getCheckedItemPosition(), id == R.id.button_always);
+        startSelected(mListView.getCheckedItemPosition(), id == R.id.button_always);
         dismiss();
     }
 
@@ -288,94 +287,103 @@
     }
 
     protected void onIntentSelected(ResolveInfo ri, Intent intent, boolean alwaysCheck) {
-        if (alwaysCheck) {
-            // Build a reasonable intent filter, based on what matched.
-            IntentFilter filter = new IntentFilter();
+        // Build a reasonable intent filter, based on what matched.
+        IntentFilter filter = new IntentFilter();
 
-            if (intent.getAction() != null) {
-                filter.addAction(intent.getAction());
+        if (intent.getAction() != null) {
+            filter.addAction(intent.getAction());
+        }
+        Set<String> categories = intent.getCategories();
+        if (categories != null) {
+            for (String cat : categories) {
+                filter.addCategory(cat);
             }
-            Set<String> categories = intent.getCategories();
-            if (categories != null) {
-                for (String cat : categories) {
-                    filter.addCategory(cat);
+        }
+        filter.addCategory(Intent.CATEGORY_DEFAULT);
+
+        int cat = ri.match&IntentFilter.MATCH_CATEGORY_MASK;
+        Uri data = intent.getData();
+        if (cat == IntentFilter.MATCH_CATEGORY_TYPE) {
+            String mimeType = intent.resolveType(this);
+            if (mimeType != null) {
+                try {
+                    filter.addDataType(mimeType);
+                } catch (IntentFilter.MalformedMimeTypeException e) {
+                    Log.w("ResolverActivity", e);
+                    filter = null;
                 }
             }
-            filter.addCategory(Intent.CATEGORY_DEFAULT);
+        }
+        if (data != null && data.getScheme() != null) {
+            // We need the data specification if there was no type,
+            // OR if the scheme is not one of our magical "file:"
+            // or "content:" schemes (see IntentFilter for the reason).
+            if (cat != IntentFilter.MATCH_CATEGORY_TYPE
+                    || (!"file".equals(data.getScheme())
+                            && !"content".equals(data.getScheme()))) {
+                filter.addDataScheme(data.getScheme());
 
-            int cat = ri.match&IntentFilter.MATCH_CATEGORY_MASK;
-            Uri data = intent.getData();
-            if (cat == IntentFilter.MATCH_CATEGORY_TYPE) {
-                String mimeType = intent.resolveType(this);
-                if (mimeType != null) {
-                    try {
-                        filter.addDataType(mimeType);
-                    } catch (IntentFilter.MalformedMimeTypeException e) {
-                        Log.w("ResolverActivity", e);
-                        filter = null;
+                // Look through the resolved filter to determine which part
+                // of it matched the original Intent.
+                Iterator<PatternMatcher> pIt = ri.filter.schemeSpecificPartsIterator();
+                if (pIt != null) {
+                    String ssp = data.getSchemeSpecificPart();
+                    while (ssp != null && pIt.hasNext()) {
+                        PatternMatcher p = pIt.next();
+                        if (p.match(ssp)) {
+                            filter.addDataSchemeSpecificPart(p.getPath(), p.getType());
+                            break;
+                        }
                     }
                 }
-            }
-            if (data != null && data.getScheme() != null) {
-                // We need the data specification if there was no type,
-                // OR if the scheme is not one of our magical "file:"
-                // or "content:" schemes (see IntentFilter for the reason).
-                if (cat != IntentFilter.MATCH_CATEGORY_TYPE
-                        || (!"file".equals(data.getScheme())
-                                && !"content".equals(data.getScheme()))) {
-                    filter.addDataScheme(data.getScheme());
-
-                    // Look through the resolved filter to determine which part
-                    // of it matched the original Intent.
-                    Iterator<PatternMatcher> pIt = ri.filter.schemeSpecificPartsIterator();
-                    if (pIt != null) {
-                        String ssp = data.getSchemeSpecificPart();
-                        while (ssp != null && pIt.hasNext()) {
-                            PatternMatcher p = pIt.next();
-                            if (p.match(ssp)) {
-                                filter.addDataSchemeSpecificPart(p.getPath(), p.getType());
-                                break;
-                            }
+                Iterator<IntentFilter.AuthorityEntry> aIt = ri.filter.authoritiesIterator();
+                if (aIt != null) {
+                    while (aIt.hasNext()) {
+                        IntentFilter.AuthorityEntry a = aIt.next();
+                        if (a.match(data) >= 0) {
+                            int port = a.getPort();
+                            filter.addDataAuthority(a.getHost(),
+                                    port >= 0 ? Integer.toString(port) : null);
+                            break;
                         }
                     }
-                    Iterator<IntentFilter.AuthorityEntry> aIt = ri.filter.authoritiesIterator();
-                    if (aIt != null) {
-                        while (aIt.hasNext()) {
-                            IntentFilter.AuthorityEntry a = aIt.next();
-                            if (a.match(data) >= 0) {
-                                int port = a.getPort();
-                                filter.addDataAuthority(a.getHost(),
-                                        port >= 0 ? Integer.toString(port) : null);
-                                break;
-                            }
-                        }
-                    }
-                    pIt = ri.filter.pathsIterator();
-                    if (pIt != null) {
-                        String path = data.getPath();
-                        while (path != null && pIt.hasNext()) {
-                            PatternMatcher p = pIt.next();
-                            if (p.match(path)) {
-                                filter.addDataPath(p.getPath(), p.getType());
-                                break;
-                            }
+                }
+                pIt = ri.filter.pathsIterator();
+                if (pIt != null) {
+                    String path = data.getPath();
+                    while (path != null && pIt.hasNext()) {
+                        PatternMatcher p = pIt.next();
+                        if (p.match(path)) {
+                            filter.addDataPath(p.getPath(), p.getType());
+                            break;
                         }
                     }
                 }
             }
+        }
 
-            if (filter != null) {
-                final int N = mAdapter.mList.size();
-                ComponentName[] set = new ComponentName[N];
-                int bestMatch = 0;
-                for (int i=0; i<N; i++) {
-                    ResolveInfo r = mAdapter.mList.get(i).ri;
-                    set[i] = new ComponentName(r.activityInfo.packageName,
-                            r.activityInfo.name);
-                    if (r.match > bestMatch) bestMatch = r.match;
-                }
+        if (filter != null) {
+            final int N = mAdapter.mList.size();
+            ComponentName[] set = new ComponentName[N];
+            int bestMatch = 0;
+            for (int i=0; i<N; i++) {
+                ResolveInfo r = mAdapter.mList.get(i).ri;
+                set[i] = new ComponentName(r.activityInfo.packageName,
+                        r.activityInfo.name);
+                if (r.match > bestMatch) bestMatch = r.match;
+            }
+            if (alwaysCheck) {
                 getPackageManager().addPreferredActivity(filter, bestMatch, set,
                         intent.getComponent());
+            } else {
+                try {
+                    AppGlobals.getPackageManager().setLastChosenActivity(intent,
+                            intent.resolveTypeIfNeeded(getContentResolver()),
+                            PackageManager.MATCH_DEFAULT_ONLY,
+                            filter, bestMatch, intent.getComponent());
+                } catch (RemoteException re) {
+                    Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
+                }
             }
         }
 
@@ -410,11 +418,13 @@
     private final class ResolveListAdapter extends BaseAdapter {
         private final Intent[] mInitialIntents;
         private final List<ResolveInfo> mBaseResolveList;
+        private ResolveInfo mLastChosen;
         private final Intent mIntent;
         private final int mLaunchedFromUid;
         private final LayoutInflater mInflater;
 
         private List<DisplayResolveInfo> mList;
+        private int mInitialHighlight = -1;
 
         public ResolveListAdapter(Context context, Intent intent,
                 Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid) {
@@ -436,14 +446,24 @@
             if (newItemCount == 0) {
                 // We no longer have any items...  just finish the activity.
                 finish();
-            } else if (newItemCount != oldItemCount) {
-                resizeGrid();
             }
         }
 
+        public int getInitialHighlight() {
+            return mInitialHighlight;
+        }
+
         private void rebuildList() {
             List<ResolveInfo> currentResolveList;
 
+            try {
+                mLastChosen = AppGlobals.getPackageManager().getLastChosenActivity(
+                        mIntent, mIntent.resolveTypeIfNeeded(getContentResolver()),
+                        PackageManager.MATCH_DEFAULT_ONLY);
+            } catch (RemoteException re) {
+                Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
+            }
+
             mList.clear();
             if (mBaseResolveList != null) {
                 currentResolveList = mBaseResolveList;
@@ -556,6 +576,12 @@
             // Process labels from start to i
             int num = end - start+1;
             if (num == 1) {
+                if (mLastChosen != null
+                        && mLastChosen.activityInfo.packageName.equals(
+                                ro.activityInfo.packageName)
+                        && mLastChosen.activityInfo.name.equals(ro.activityInfo.name)) {
+                    mInitialHighlight = mList.size();
+                }
                 // No duplicate labels. Use label for entry at start
                 mList.add(new DisplayResolveInfo(ro, roLabel, null, null));
             } else {
@@ -585,6 +611,12 @@
                 }
                 for (int k = start; k <= end; k++) {
                     ResolveInfo add = rList.get(k);
+                    if (mLastChosen != null
+                            && mLastChosen.activityInfo.packageName.equals(
+                                    add.activityInfo.packageName)
+                            && mLastChosen.activityInfo.name.equals(add.activityInfo.name)) {
+                        mInitialHighlight = mList.size();
+                    }
                     if (usePkg) {
                         // Use application name for all entries from start to end-1
                         mList.add(new DisplayResolveInfo(add, roLabel,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index faf6e63..9613df3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -207,6 +207,7 @@
     <protected-broadcast android:name="android.intent.action.DREAMING_STARTED" />
     <protected-broadcast android:name="android.intent.action.DREAMING_STOPPED" />
     <protected-broadcast android:name="android.intent.action.ANY_DATA_STATE" />
+    <protected-broadcast android:name="android.intent.action.DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN" />
 
     <protected-broadcast android:name="com.android.server.WifiManager.action.START_SCAN" />
     <protected-broadcast android:name="com.android.server.WifiManager.action.DELAYED_DRIVER_STOP" />
@@ -249,6 +250,9 @@
     <protected-broadcast android:name="android.location.GPS_FIX_CHANGE" />
     <protected-broadcast android:name="android.net.proxy.PAC_REFRESH" />
 
+    <protected-broadcast
+        android:name="com.android.server.connectivityservice.CONNECTED_TO_PROVISIONING_NETWORK_ACTION" />
+
     <!-- ====================================== -->
     <!-- Permissions for things that cost money -->
     <!-- ====================================== -->
@@ -1915,13 +1919,10 @@
         android:description="@string/permdesc_bindNfcService"
         android:protectionLevel="signature" />
 
-    <!-- Allows an application to call APIs that give it access to all print jobs
-         on the device. Usually an app can access only the print jobts it created.
-         This permission is not available to third party applications.
-         @hide -->
-    <permission android:name="android.permission.ACCESS_ALL_PRINT_JOBS"
-        android:label="@string/permlab_accessAllPrintJobs"
-        android:description="@string/permdesc_accessAllPrintJobs"
+    <!-- Must be required by the PrintSpooler to ensure that only the system can bind to it. -->
+    <permission android:name="android.permission.BIND_PRINT_SPOOLER_SERVICE"
+        android:label="@string/permlab_bindPrintSpoolerService"
+        android:description="@string/permdesc_bindPrintSpoolerService"
         android:protectionLevel="signature" />
 
     <!-- Must be required by a TextService (e.g. SpellCheckerService)
diff --git a/core/res/res/layout/resolve_list_item.xml b/core/res/res/layout/resolve_list_item.xml
index 61cecae..28c5b74 100644
--- a/core/res/res/layout/resolve_list_item.xml
+++ b/core/res/res/layout/resolve_list_item.xml
@@ -18,40 +18,40 @@
 */
 -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:gravity="center"
-              android:orientation="vertical"
+              android:orientation="horizontal"
               android:layout_height="wrap_content"
               android:layout_width="match_parent"
-              android:background="@android:drawable/activity_picker_bg"
-              android:padding="16dp">
-
-    <!-- Extended activity info to distinguish between duplicate activity names -->
-    <TextView android:id="@android:id/text2"
-              android:textAppearance="?android:attr/textAppearance"
-              android:layout_width="wrap_content"
-              android:layout_height="wrap_content"
-              android:gravity="center"
-              android:minLines="2"
-              android:maxLines="2"
-              android:paddingStart="4dip"
-              android:paddingEnd="4dip" />
+              android:background="@android:drawable/activity_picker_bg">
 
     <!-- Activity icon when presenting dialog
          Size will be filled in by ResolverActivity -->
     <ImageView android:id="@+id/icon"
                android:layout_width="0dp"
                android:layout_height="0dp"
+               android:layout_marginStart="12dp"
+               android:padding="4dp"
                android:scaleType="fitCenter" />
 
-    <!-- Activity name -->
-    <TextView android:id="@android:id/text1"
-              android:textAppearance="?android:attr/textAppearanceSmall"
-              android:layout_width="wrap_content"
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:gravity="start|center_vertical"
+              android:orientation="vertical"
               android:layout_height="wrap_content"
-              android:gravity="center"
-              android:minLines="2"
-              android:maxLines="2"
-              android:paddingStart="4dip"
-              android:paddingEnd="4dip" />
+              android:layout_width="wrap_content"
+              android:layout_gravity="start|center_vertical"
+              android:layout_marginStart="12dp">
+        <!-- Activity name -->
+        <TextView android:id="@android:id/text1"
+                  android:textAppearance="?android:attr/textAppearanceMedium"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:maxLines="2" />
+        <!-- Extended activity info to distinguish between duplicate activity names -->
+        <TextView android:id="@android:id/text2"
+                  android:textAppearance="?android:attr/textAppearanceSmall"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:maxLines="2"
+                  android:paddingTop="4dip" />
+    </LinearLayout>
 </LinearLayout>
 
diff --git a/core/res/res/layout/resolver_grid.xml b/core/res/res/layout/resolver_list.xml
similarity index 88%
rename from core/res/res/layout/resolver_grid.xml
rename to core/res/res/layout/resolver_list.xml
index d271c1a..f88ced1 100644
--- a/core/res/res/layout/resolver_grid.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -23,20 +23,18 @@
               android:divider="?android:attr/dividerHorizontal"
               android:showDividers="middle"
               android:dividerPadding="0dip">
+
     <FrameLayout android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  android:layout_weight="1">
-        <GridView
-            android:layout_gravity="center"
-            android:layout_width="wrap_content"
+
+        <ListView
+            android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:id="@+id/resolver_grid"
-            android:numColumns="4"
-            android:columnWidth="128dp"
-            android:padding="16dp"
-            android:clipToPadding="false"
-            android:scrollbarStyle="outsideOverlay" />
+            android:id="@+id/resolver_list" />
+
     </FrameLayout>
+
     <LinearLayout
         android:id="@+id/button_bar"
         android:visibility="gone"
diff --git a/core/res/res/values-mcc208-mnc26/config.xml b/core/res/res/values-mcc208-mnc26/config.xml
new file mode 100644
index 0000000..31d2d0f
--- /dev/null
+++ b/core/res/res/values-mcc208-mnc26/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Don't use roaming icon for considered operators -->
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>20801</item>
+        <item>20810</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc04/config.xml b/core/res/res/values-mcc214-mnc04/config.xml
new file mode 100644
index 0000000..71301d5
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc04/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Don't use roaming icon for considered operators -->
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>21407</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc234-mnc30/config.xml b/core/res/res/values-mcc234-mnc30/config.xml
new file mode 100644
index 0000000..eabdf9a
--- /dev/null
+++ b/core/res/res/values-mcc234-mnc30/config.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Don't use roaming icon for considered operators -->
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>23430</item>
+        <item>23431</item>
+        <item>23432</item>
+        <item>23433</item>
+        <item>23434</item>
+        <item>23486</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc234-mnc31/config.xml b/core/res/res/values-mcc234-mnc31/config.xml
new file mode 100644
index 0000000..eabdf9a
--- /dev/null
+++ b/core/res/res/values-mcc234-mnc31/config.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Don't use roaming icon for considered operators -->
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>23430</item>
+        <item>23431</item>
+        <item>23432</item>
+        <item>23433</item>
+        <item>23434</item>
+        <item>23486</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc234-mnc32/config.xml b/core/res/res/values-mcc234-mnc32/config.xml
new file mode 100644
index 0000000..eabdf9a
--- /dev/null
+++ b/core/res/res/values-mcc234-mnc32/config.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Don't use roaming icon for considered operators -->
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>23430</item>
+        <item>23431</item>
+        <item>23432</item>
+        <item>23433</item>
+        <item>23434</item>
+        <item>23486</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc234-mnc33/config.xml b/core/res/res/values-mcc234-mnc33/config.xml
index d79d212..175f76e 100644
--- a/core/res/res/values-mcc234-mnc33/config.xml
+++ b/core/res/res/values-mcc234-mnc33/config.xml
@@ -35,4 +35,14 @@
          "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type"
          note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
     <string translatable="false" name="config_tether_apndata">Consumer Broadband,consumerbroadband,,,,,,,,,234,33,,DUN</string>
+
+    <!-- Don't use roaming icon for considered operators -->
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>23430</item>
+        <item>23431</item>
+        <item>23432</item>
+        <item>23433</item>
+        <item>23434</item>
+        <item>23486</item>
+    </string-array>
 </resources>
diff --git a/core/res/res/values-mcc234-mnc34/config.xml b/core/res/res/values-mcc234-mnc34/config.xml
new file mode 100644
index 0000000..eabdf9a
--- /dev/null
+++ b/core/res/res/values-mcc234-mnc34/config.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Don't use roaming icon for considered operators -->
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>23430</item>
+        <item>23431</item>
+        <item>23432</item>
+        <item>23433</item>
+        <item>23434</item>
+        <item>23486</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc234-mnc86/config.xml b/core/res/res/values-mcc234-mnc86/config.xml
new file mode 100644
index 0000000..eabdf9a
--- /dev/null
+++ b/core/res/res/values-mcc234-mnc86/config.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Don't use roaming icon for considered operators -->
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>23430</item>
+        <item>23431</item>
+        <item>23432</item>
+        <item>23433</item>
+        <item>23434</item>
+        <item>23486</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc302-mnc610/config.xml b/core/res/res/values-mcc302-mnc610/config.xml
new file mode 100644
index 0000000..706570c
--- /dev/null
+++ b/core/res/res/values-mcc302-mnc610/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Don't use roaming icon for considered operators -->
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>302</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc302-mnc640/config.xml b/core/res/res/values-mcc302-mnc640/config.xml
new file mode 100644
index 0000000..706570c
--- /dev/null
+++ b/core/res/res/values-mcc302-mnc640/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Don't use roaming icon for considered operators -->
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>302</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc302-mnc780/config.xml b/core/res/res/values-mcc302-mnc780/config.xml
index 42d4956..b03d14e 100644
--- a/core/res/res/values-mcc302-mnc780/config.xml
+++ b/core/res/res/values-mcc302-mnc780/config.xml
@@ -37,4 +37,8 @@
          note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
     <string translatable="false" name="config_tether_apndata">SaskTel Tethering,inet.stm.sk.ca,,,,,,,,,302,780,,DUN</string>
 
+    <!-- Don't use roaming icon for considered operators -->
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>302</item>
+    </string-array>
 </resources>
diff --git a/core/res/res/values-mcc425-mnc07/config.xml b/core/res/res/values-mcc425-mnc07/config.xml
index 890420e..51a9934 100644
--- a/core/res/res/values-mcc425-mnc07/config.xml
+++ b/core/res/res/values-mcc425-mnc07/config.xml
@@ -37,4 +37,8 @@
          note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
     <string translatable="false" name="config_tether_apndata">PC HOT mobile,pc.hotm,,,,,,,,,425,07,,DUN</string>
 
+    <!-- Don't use roaming icon for considered operators -->
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>42503</item>
+    </string-array>
 </resources>
diff --git a/core/res/res/values-mcc425-mnc08/config.xml b/core/res/res/values-mcc425-mnc08/config.xml
new file mode 100644
index 0000000..8470b86
--- /dev/null
+++ b/core/res/res/values-mcc425-mnc08/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Don't use roaming icon for considered operators -->
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>42502</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4c72571..d4a408d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -820,6 +820,10 @@
     <!-- IP address of the dns server to use if nobody else suggests one -->
     <string name="config_default_dns_server" translatable="false">8.8.8.8</string>
 
+    <!-- The default mobile provisioning apn. Empty by default, maybe overridden by
+         an mcc/mnc specific config.xml -->
+    <string name="mobile_provisioning_apn" translatable="false"></string>
+
     <!-- The default mobile provisioning url. Empty by default, maybe overridden by
          an mcc/mnc specific config.xml -->
     <string name="mobile_provisioning_url" translatable="false"></string>
@@ -1210,4 +1214,12 @@
     <!-- set to false if we need to show user confirmation
          when alpha identifier is not provided by the UICC -->
     <bool name="config_stkNoAlphaUsrCnf">true</bool>
+
+    <!-- Don't use roaming icon for considered operators.
+         Can use mcc or mcc+mnc as item. For example, 302 or 21407.
+         If operators, 21404 and 21407, make roaming agreements, user of 21404 should not see
+         the roaming icon as using 21407 network.
+         To do this, add 21407 item to values-mcc214-mnc04/config.xml -->
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+    </string-array>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ead46c2..4b32e2b 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -984,12 +984,13 @@
     <string name="permdesc_bindPrintService">Allows the holder to bind to the top-level
         interface of a print service. Should never be needed for normal apps.</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_accessAllPrintJobs">access all print jobs</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_accessAllPrintJobs">Allows the holder to access print jobs
-        created by another app. Should never be needed for normal apps.</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_bindPrintSpoolerService">bind to a print spooler service</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_bindPrintSpoolerService">Allows the holder to bind to the top-level
+        interface of a print spooler service. Should never be needed for normal apps.</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_bindNfcService">bind to NFC service</string>
@@ -4292,6 +4293,9 @@
     <!-- Write fail reason: couldn't write the printed content. [CHAR LIMIT=none] -->
     <string name="write_fail_reason_cannot_write">Error writing content</string>
 
+    <!-- Print fail reason: unknown. [CHAR LIMIT=25] -->
+    <string name="reason_unknown">unknown</string>
+
     <!-- PIN entry dialog label/hint for PIN [CHAR LIMIT=none] -->
     <string name="restr_pin_enter_pin">Enter PIN</string>
     <!-- PIN entry dialog label/hint for old PIN [CHAR LIMIT=none] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
old mode 100755
new mode 100644
index b0da885..f008b10
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -668,6 +668,7 @@
   <java-symbol type="string" name="preposition_for_time" />
   <java-symbol type="string" name="progress_erasing" />
   <java-symbol type="string" name="progress_unmounting" />
+  <java-symbol type="string" name="mobile_provisioning_apn" />
   <java-symbol type="string" name="mobile_provisioning_url" />
   <java-symbol type="string" name="mobile_redirected_provisioning_url" />
   <java-symbol type="string" name="reboot_safemode_confirm" />
@@ -868,6 +869,7 @@
   <java-symbol type="string" name="mediaSize_na_junior_legal" />
   <java-symbol type="string" name="mediaSize_na_ledger" />
   <java-symbol type="string" name="mediaSize_na_tabloid" />
+  <java-symbol type="string" name="reason_unknown" />
   <java-symbol type="string" name="restr_pin_enter_pin" />
   <java-symbol type="string" name="write_fail_reason_cancelled" />
   <java-symbol type="string" name="write_fail_reason_cannot_write" />
@@ -910,6 +912,7 @@
   <java-symbol type="array" name="config_masterVolumeRamp" />
   <java-symbol type="array" name="config_cdma_dun_supported_types" />
   <java-symbol type="array" name="config_disabledUntilUsedPreinstalledImes" />
+  <java-symbol type="array" name="config_operatorConsideredNonRoaming" />
 
   <java-symbol type="drawable" name="default_wallpaper" />
   <java-symbol type="drawable" name="indicator_input_error" />
@@ -1526,8 +1529,8 @@
   <java-symbol type="string" name="enable_explore_by_touch_warning_title" />
   <java-symbol type="string" name="enable_explore_by_touch_warning_message" />
 
-  <java-symbol type="layout" name="resolver_grid" />
-  <java-symbol type="id" name="resolver_grid" />
+  <java-symbol type="layout" name="resolver_list" />
+  <java-symbol type="id" name="resolver_list" />
   <java-symbol type="id" name="button_once" />
   <java-symbol type="id" name="button_always" />
   <java-symbol type="integer" name="config_maxResolverActivityColumns" />
diff --git a/docs/downloads/training/AndroidTestingFun.zip b/docs/downloads/training/AndroidTestingFun.zip
new file mode 100644
index 0000000..dca5812
--- /dev/null
+++ b/docs/downloads/training/AndroidTestingFun.zip
Binary files differ
diff --git a/docs/html/images/training/lesson2_MyFirstTestActivityTest_result.png b/docs/html/images/training/lesson2_MyFirstTestActivityTest_result.png
new file mode 100644
index 0000000..e0e869b
--- /dev/null
+++ b/docs/html/images/training/lesson2_MyFirstTestActivityTest_result.png
Binary files differ
diff --git a/docs/html/training/activity-testing/activity-basic-testing.jd b/docs/html/training/activity-testing/activity-basic-testing.jd
new file mode 100644
index 0000000..016289d
--- /dev/null
+++ b/docs/html/training/activity-testing/activity-basic-testing.jd
@@ -0,0 +1,227 @@
+page.title=Creating and Running a Test Case
+trainingnavtop=true
+
+@jd:body
+
+<!-- This is the training bar -->
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#testcase">Create a Test Case for Activity Testing</a>
+      <ol>
+      <li><a href="#fixture">Set Up Your Test Fixture</a></li>
+      <li><a href="#preconditions">Add Test Preconditions</a></li>
+      <li><a href="#test_method">Add Test Methods to Verify Your Activity</a></li>
+      </ol>
+  </li>
+  <li><a href="#build_run">Build and Run Your Test</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+<li><a href="{@docRoot}tools/testing/testing_android.html">Testing
+Fundamentals</a></li>
+</ul>
+
+</div>
+</div>
+<p>In order to verify that there are no regressions in the layout design and
+functional behavior in your application, it's important to
+create a test for each {@link android.app.Activity} in your application. For
+each test, you need to create the individual parts of a test case, including
+the test fixture, preconditions test method, and {@link android.app.Activity}
+test methods. You can then run your test to get a test report. If any test
+method fails, this might indicate a potential defect in your code.</p>
+<p class="note"><strong>Note:</strong> In the Test-Driven Development (TDD)
+approach, instead of writing most or all of your app code up-front and then
+running tests later in the development cycle, you would progressively write
+just enough production code to satisfy your test dependencies, update your
+test cases to reflect new functional requirements, and iterate repeatedly this
+way.</p>
+
+<h2 id="testcase">Create a Test Case</h2>
+<p>{@link android.app.Activity} tests are written in a structured way.
+Make sure to put your tests in a separate package, distinct from the code under
+test.</p>
+<p>By convention, your test package name should follow the same name as the
+application package, suffixed with <strong>".tests"</strong>. In the test package
+you created, add the Java class for your test case. By convention, your test case
+name should also follow the same name as the Java or Android class that you
+want to test, but suffixed with <strong>“Test”</strong>.</p>
+<p>To create a new test case in Eclipse:</p>
+<ol type="a">
+   <li>In the Package Explorer, right-click on the {@code /src} directory for
+your test project and select <strong>New &gt; Package</strong>.</li>
+   <li>Set the <strong>Name</strong> field to
+{@code &lt;your_app_package_name&gt;.tests} (for example,
+{@code com.example.android.testingfun.tests}) and click
+<strong>Finish</strong>.</li>
+   <li>Right-click on the test package you created, and select
+<strong>New &gt; Class</strong>.</li>
+    <li>Set the <strong>Name</strong> field to
+{@code &lt;your_app_activity_name&gt;Test} (for example,
+{@code MyFirstTestActivityTest}) and click <strong>Finish</strong>.</li>
+</ol>
+
+<h3 id="fixture">Set Up Your Test Fixture</h3>
+<p>A <em>test fixture</em> consists of objects that must be initialized for
+running one or more tests. To set up the test fixture, you can override the
+{@link junit.framework.TestCase#setUp()} and
+{@link junit.framework.TestCase#tearDown()} methods in your test. The
+test runner automatically runs {@link junit.framework.TestCase#setUp()} before
+running any other test methods, and {@link junit.framework.TestCase#tearDown()}
+at the end of each test method execution. You can use these methods to keep
+the code for test initialization and clean up separate from the tests methods.
+</p>
+<p>To set up your test fixture in Eclipse:</p>
+<ol>
+<li>In the Package Explorer, double-click on the test case that you created
+earlier to bring up the Eclipse Java editor, then modify your test case class
+to extend one of the sub-classes of {@link android.test.ActivityTestCase}.
+<p>For example:</p>
+<pre>
+public class MyFirstTestActivityTest
+        extends ActivityInstrumentationTestCase2&lt;MyFirstTestActivity&gt; {
+</pre>
+</li>
+<li>Next, add the constructor and {@link junit.framework.TestCase#setUp()}
+methods to your test case, and add variable declarations for the
+{@link android.app.Activity} that you want to test.</p>
+<p>For example:</p>
+<pre>
+public class MyFirstTestActivityTest
+        extends ActivityInstrumentationTestCase2&lt;MyFirstTestActivity&gt; {
+
+    private MyFirstTestActivity mFirstTestActivity;
+    private TextView mFirstTestText;
+
+    public MyFirstTestActivityTest() {
+        super(MyFirstTestActivity.class);
+    }
+
+    &#64;Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mFirstTestActivity = getActivity();
+        mFirstTestText =
+                (TextView) mFirstTestActivity
+                .findViewById(R.id.my_first_test_text_view);
+    }
+}
+</pre>
+<p>The constructor is invoked by the test runner to instantiate the test
+class, while the {@link junit.framework.TestCase#setUp()} method is invoked by
+the test runner before it runs any tests in the test class.</p>
+</li>
+</ol>
+
+<p>Typically, in the {@link junit.framework.TestCase#setUp()} method, you
+should:</p>
+<ul>
+<li>Invoke the superclass constructor for
+{@link junit.framework.TestCase#setUp()}, which is required by JUnit.</li>
+<li>Initialize your test fixture state by:
+   <ul>
+   <li>Defining the instance variables that store the state of the fixture.</li>
+   <li>Creating and storing a reference to an instance of the
+{@link android.app.Activity} under test.</li>
+   <li>Obtaining a reference to any UI components in the
+{@link android.app.Activity} that you want to test.</li>
+   </ul>
+</ul>
+
+<p>You can use the
+{@link android.test.ActivityInstrumentationTestCase2#getActivity()} method to
+get a reference to the {@link android.app.Activity} under test.</p>
+
+<h3 id="preconditions">Add Test Preconditions</h3>
+<p>As a sanity check, it is good practice to verify that the test fixture has
+been set up correctly, and the objects that you want to test have been correctly
+instantiated or initialized. That way, you won’t have to see
+tests failing because something was wrong with the setup of your test fixture.
+By convention, the method for verifying your test fixture is called
+{@code testPreconditions()}.</p>
+
+<p>For example, you might want to add a {@code testPreconditons()} method like
+this to your test case:</p>
+
+<pre>
+public void testPreconditions() {
+    assertNotNull(“mFirstTestActivity is null”, mFirstTestActivity);
+    assertNotNull(“mFirstTestText is null”, mFirstTestText);
+}
+</pre>
+
+<p>The assertion methods are from the JUnit {@link junit.framework.Assert}
+class. Generally, you can use assertions to
+verify if a specific condition that you want to test is true.
+<ul>
+<li>If the condition is false, the assertion method throws an
+{@link android.test.AssertionFailedError} exception, which is then typically
+reported by the test runner. You can provide a string in the first argument of
+your assertion method to give some contextual details if the assertion fails.</li>
+<li>If the condition is true, the test passes.</li>
+</ul>
+<p>In both cases, the test runner proceeds to run the other test methods in the
+test case.</p>
+
+<h3 id="test_method">Add Test Methods to Verify Your Activity</h3>
+<p>Next, add one or more test methods to verify the layout and functional
+behavior of your {@link android.app.Activity}.</p>
+<p>For example, if your {@link android.app.Activity} includes a
+{@link android.widget.TextView}, you can add a test method like this to check
+that it has the correct label text:</p>
+<pre>
+public void testMyFirstTestTextView_labelText() {
+    final String expected =
+            mFirstTestActivity.getString(R.string.my_first_test);
+    final String actual = mFirstTestText.getText().toString();
+    assertEquals(expected, actual);
+}
+</pre>
+
+<p>The {@code testMyFirstTestTextView_labelText()} method simply checks that the
+default text of the {@link android.widget.TextView} that is set by the layout
+is the same as the expected text defined in the {@code strings.xml} resource.</p>
+<p class="note"><strong>Note:</strong> When naming test methods, you can use
+an underscore to separate what is being tested from the specific case being
+tested. This style makes it easier to see exactly what cases are being tested.</p>
+<p>When doing this type of string value comparison, it’s good practice to read
+the expected string from your resources, instead of hardcoding the string in
+your comparison code. This prevents your test from easily breaking whenever the
+string definitions are modified in the resource file.</p>
+<p>To perform the comparison, pass both the expected and actual strings as
+arguments to the
+{@link junit.framework.Assert#assertEquals(java.lang.String, java.lang.String) assertEquals()}
+method. If the values are not the same, the assertion will throw an
+{@link junit.framework.AssertionFailedError} exception.</p>
+<p>If you added a {@code testPreconditions()} method, put your test methods
+after the {@code testPreconditions()} definition in your Java class.</p>
+<p>For a complete test case example, take a look at
+{@code MyFirstTestActivityTest.java} in the sample app.</p>
+
+<h2 id="build_run">Build and Run Your Test</h2>
+<p>You can build and run your test easily from the Package Explorer in
+Eclipse.</p>
+<p>To build and run your test:</p>
+<ol>
+<li>Connect an Android device to your machine. On the device or emulator, open
+the <strong>Settings</strong> menu, select <strong>Developer options</strong>
+and make sure that USB debugging is enabled.</li>
+<li>In the Project Explorer, right-click on the test class that you created
+earlier and select <strong>Run As &gt; Android Junit Test</strong>.</li>
+<li>In the Android Device Chooser dialog, select the device that you just
+connected, then click <strong>OK</strong>.</li>
+<li>In the JUnit view, verify that the test passes with no errors or failures.</li>
+</ol>
+<p>For example, if the test case passes with no errors, the result should look
+like this:</p>
+<img src="{@docRoot}images/training/activity-testing_lesson2_MyFirstTestActivityTest_result.png" alt="" />
+<p class="img-caption">
+  <strong>Figure 1.</strong> Result of a test with no errors.
+</p>
+
+
+
diff --git a/docs/html/training/activity-testing/activity-functional-testing.jd b/docs/html/training/activity-testing/activity-functional-testing.jd
new file mode 100644
index 0000000..7c8ff1d
--- /dev/null
+++ b/docs/html/training/activity-testing/activity-functional-testing.jd
@@ -0,0 +1,166 @@
+page.title=Creating Functional Tests
+trainingnavtop=true
+@jd:body
+
+<!-- This is the training bar -->
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+   <li><a href="#test_methods">Add Test Method to Validate Functional Behavior</a>
+   <ol>
+      <li><a href="#activitymonitor">Set Up an ActivityMonitor</a></li>
+      <li><a href="#keyinput">Send Keyboard Input Using Instrumentation</a></li>
+   </ol>
+   </li>
+</ol>
+
+<h2>Try it out</h2>
+<div class="download-box">
+ <a href="http://developer.android.com/shareables/training/AndroidTestingFun.zip"
+class="button">Download the demo</a>
+ <p class="filename">AndroidTestingFun.zip</p>
+</div>
+
+</div>
+</div>
+<p>Functional testing involves verifying that individual application
+components work together as expected by the user. For example, you can create a
+functional test to verify that an {@link android.app.Activity} correctly
+launches a target {@link android.app.Activity} when the user performs a UI
+interaction.</p>
+
+<p>To create a functional test for your {@link android.app.Activity}, your test
+class should extend {@link android.test.ActivityInstrumentationTestCase2}.
+Unlike {@link android.test.ActivityUnitTestCase},
+tests in {@link android.test.ActivityInstrumentationTestCase2} can
+communicate with the Android system and send keyboard input and click events to
+the UI.</p>
+
+<p>For a complete test case example, take a look at
+{@code SenderActivityTest.java} in the sample app.</p>
+
+<h2 id="test_methods">Add Test Method to Validate Functional Behavior</h2>
+<p id="test_goals">Your functional testing goals might include:</p>
+<ul>
+<li>Verifying that a target {@link android.app.Activity} is started when a
+UI control is pushed in the sender {@link android.app.Activity}.</li>
+<li>Verifying that the target {@link android.app.Activity} displays the
+correct data based on the user's input in the sender
+{@link android.app.Activity}.</li>
+</ul>
+<p>You might implement your test method like this:</p>
+
+<pre>
+&#64;MediumTest
+public void testSendMessageToReceiverActivity() {
+    final Button sendToReceiverButton = (Button) 
+            mSenderActivity.findViewById(R.id.send_message_button);
+
+    final EditText senderMessageEditText = (EditText) 
+            mSenderActivity.findViewById(R.id.message_input_edit_text);
+
+    // Set up an ActivityMonitor
+    ...
+
+    // Send string input value
+    ...
+
+    // Validate that ReceiverActivity is started
+    ...
+
+    // Validate that ReceiverActivity has the correct data
+    ...
+
+    // Remove the ActivityMonitor
+    ...
+}
+</pre>
+<p>The test waits for an {@link android.app.Activity} that matches this monitor,
+otherwise returns null after a timeout elapses. If {@code ReceiverActivity} was
+started, the {@link android.app.Instrumentation.ActivityMonitor ActivityMonitor}
+that you set
+up earlier receives a hit. You can use the assertion methods to verify that
+the {@code ReceiverActivity} is indeed started, and that the hit count on the
+{@link android.app.Instrumentation.ActivityMonitor ActivityMonitor} incremented
+as expected.</p>
+
+<h2 id="activitymonitor">Set up an ActivityMonitor</h2>
+<p>To monitor a single {@link android.app.Activity} in your application, you
+can register an {@link android.app.Instrumentation.ActivityMonitor ActivityMonitor}.
+The {@link android.app.Instrumentation.ActivityMonitor ActivityMonitor} is
+notified by the system whenever an {@link android.app.Activity} that matches your criteria is started.
+If a match is found, the monitor’s hit count is updated.</p>
+<p>Generally, to use an
+{@link android.app.Instrumentation.ActivityMonitor ActivityMonitor}, you should:</p>
+<ol>
+<li>Retrieve the {@link android.app.Instrumentation} instance for your test
+case by using the
+{@link android.test.InstrumentationTestCase#getInstrumentation()} method.</li>
+<li>Add an instance of {@link android.app.Instrumentation.ActivityMonitor} to
+the current instrumentation using one of the {@link android.app.Instrumentation}
+{@code addMonitor()} methods. The match criteria can be specified as an
+{@link android.content.IntentFilter} or a class name string.</li>
+<li>Wait for the {@link android.app.Activity} to start.</li>
+<li>Verify that the monitor hits were incremented.</li>
+<li>Remove the monitor.</li>
+</ol>
+<p>For example:</p>
+<pre>
+// Set up an ActivityMonitor
+ActivityMonitor receiverActivityMonitor =
+        getInstrumentation().addMonitor(ReceiverActivity.class.getName(),
+        null, false);
+
+// Validate that ReceiverActivity is started
+TouchUtils.clickView(this, sendToReceiverButton);
+ReceiverActivity receiverActivity = (ReceiverActivity) 
+        receiverActivityMonitor.waitForActivityWithTimeout(TIMEOUT_IN_MS);
+assertNotNull("ReceiverActivity is null", receiverActivity);
+assertEquals("Monitor for ReceiverActivity has not been called",
+        1, receiverActivityMonitor.getHits());
+assertEquals("Activity is of wrong type",
+        ReceiverActivity.class, receiverActivity.getClass());
+
+// Remove the ActivityMonitor
+getInstrumentation().removeMonitor(receiverActivityMonitor);
+</pre>
+
+<h2 id="keyinput">Send Keyboard Input Using Instrumentation</h2>
+<p>If your {@link android.app.Activity} has an {@link android.widget.EditText}
+field, you might want to test that users can enter values into the
+{@link android.widget.EditText} object.</p>
+<p>Generally, to send a string input value to an {@link android.widget.EditText}
+object in {@link android.test.ActivityInstrumentationTestCase2}, you should:</p>
+<ol>
+<li>Use the {@link android.app.Instrumentation#runOnMainSync(java.lang.Runnable) runOnMainSync()}
+method to run the {@link android.view.View#requestFocus()} call synchronously
+in a loop. This way, the UI thread is blocked until focus is received.</li>
+<li>Call {@link android.app.Instrumentation#waitForIdleSync()} method to wait
+for the main thread to become idle (that is, have no more events to process).</li>
+<li>Send a text string to the {@link android.widget.EditText} by calling
+{@link android.app.Instrumentation#sendStringSync(java.lang.String)
+sendStringSync()} and pass your input string as the parameter.</p>
+</ol>
+<p>For example:</p>
+<pre>
+// Send string input value
+getInstrumentation().runOnMainSync(new Runnable() {
+    &#64;Override
+    public void run() {
+        senderMessageEditText.requestFocus();
+    }
+});
+getInstrumentation().waitForIdleSync();
+getInstrumentation().sendStringSync("Hello Android!");
+getInstrumentation().waitForIdleSync();
+</pre>
+
+
+
+
+
+
+
+
diff --git a/docs/html/training/activity-testing/activity-ui-testing.jd b/docs/html/training/activity-testing/activity-ui-testing.jd
new file mode 100644
index 0000000..644f3ca
--- /dev/null
+++ b/docs/html/training/activity-testing/activity-ui-testing.jd
@@ -0,0 +1,216 @@
+page.title=Testing UI Components
+trainingnavtop=true
+
+@jd:body
+
+<!-- This is the training bar -->
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#testcase">Create a Test Case for UI Testing with Instrumentation</a>
+  <li><a href="#test_method">Add Test Methods to Verify UI Behavior</a>
+     <ol>
+     <li><a href="#verify_button_display">Verify Button Layout Parameters</a></li>
+     <li><a href="#verify_TextView">Verify TextView Layout Parameters</a></li>
+     <li><a href="#verify_button_behavior">Verify Button Behavior</a></li>
+     </ol>
+  </li>
+  <li><a href="#annotations">Apply Test Annotations</a></li>
+</ol>
+
+<h2>Try it out</h2>
+<div class="download-box">
+ <a href="http://developer.android.com/shareables/training/AndroidTestingFun.zip"
+class="button">Download the demo</a>
+ <p class="filename">AndroidTestingFun.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>Typically, your {@link android.app.Activity} includes user interface
+components (such as buttons, editable text fields, checkboxes, and pickers) to
+allow users to interact with your Android application. This lesson shows how
+you can test an {@link android.app.Activity} with a simple push-button UI. You
+can use the same general steps to test other, more sophisticated types of UI
+components.</p>
+
+<p class="note"><strong>Note:</strong> The type of UI testing in this lesson is
+called <em>white-box testing</em> because you have the
+source code for the application that you want to test. The Android
+<a href="{@docRoot}tools/testing/testing_android.html#Instrumentation">Instrumentation</a>
+framework is suitable for creating white-box tests for UI components within an
+application. An alternative type of UI testing is <em>black-box testing</em>,
+where you may not have access to the application source. This type of testing
+is useful when you want to test how your app interacts with other apps or with
+the system. Black-box testing is not covered in this training. To learn more
+about how to perform black-box testing on your Android apps, see the
+<a href="{@docRoot}tools/testing/testing_ui.html">UI Testing guide</a>.
+<p>For a complete test case example, take a look at
+{@code ClickFunActivityTest.java} in the sample app.</p>
+
+<h2 id="testcase">Create a Test Case for UI Testing with Instrumentation</h2>
+<p>When testing an {@link android.app.Activity} that has a user interface (UI),
+the {@link android.app.Activity} under test runs in the UI thread. However, the
+test application itself runs in a separate thread in the same process as the
+application under test. This means that your test app can reference objects
+from the UI thread, but if it attempts to change properties on those objects or
+send events to the UI thread, you will usually get a {@code WrongThreadException}
+error.</p>
+<p>To safely inject {@link android.content.Intent} objects into your
+{@link android.app.Activity} or run test methods on the UI thread, you can
+extend your test class to use {@link android.test.ActivityInstrumentationTestCase2}.
+To learn more about how to run test methods on the UI thread, see
+<a href="{@docRoot}tools/testing/activity_testing.html#RunOnUIThread">Testing
+on the UI thread</a>.</p>
+
+<h3 id="fixture">Set Up Your Test Fixture</h3>
+<p>When setting up the test fixture for UI testing, you should specify the
+<a href="{@docRoot}guide/topics/ui/ui-events.html#TouchMode">touch mode</a>
+in your {@link junit.framework.TestCase#setUp()} method. Setting the touch mode
+to {@code true} prevents the UI control from taking focus when you click it
+programmatically in the test method later (for example, a button UI will just
+fire its on-click listener). Make sure that you call
+{@link android.test.ActivityInstrumentationTestCase2#setActivityInitialTouchMode(boolean) setActivityInitialTouchMode()}
+before calling {@link android.test.ActivityInstrumentationTestCase2#getActivity()}.
+</p>
+<p>For example:</ap>
+<pre>
+public class ClickFunActivityTest
+        extends ActivityInstrumentationTestCase2<ClickFunActivity> {
+    ...
+    &#64;Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        setActivityInitialTouchMode(true);
+
+        mClickFunActivity = getActivity();
+        mClickMeButton = (Button) 
+                mClickFunActivity
+                .findViewById(R.id.launch_next_activity_button);
+        mInfoTextView = (TextView) 
+                mClickFunActivity.findViewById(R.id.info_text_view);
+    }
+}
+</pre>
+
+<h2 id="test_methods">Add Test Methods to Validate UI Behavior</h2>
+<p id="test_goals">Your UI testing goals might include:</p>
+<ul>
+<li>Verifying that a button is displayed with the correct layout when the
+{@link android.app.Activity} is launched.</li>
+<li>Verifying that a {@link android.widget.TextView} is initially hidden.</li>
+<li>Verifying that a {@link android.widget.TextView} displays the expected string
+when a button is pushed.</li>
+</ul>
+<p>The following section demonstrates how you can implement test methods
+to perform these verifications.</p>
+
+<h3 id="verify_button_display">Verify Button Layout Parameters</h3>
+<p>You might add a test method like this to verify that a button is displayed
+correctly in your {@link android.app.Activity}:</p>
+<pre>
+&#64;MediumTest
+public void testClickMeButton_layout() {
+    final View decorView = mClickFunActivity.getWindow().getDecorView();
+
+    ViewAsserts.assertOnScreen(decorView, mClickMeButton);
+
+    final ViewGroup.LayoutParams layoutParams =
+            mClickMeButton.getLayoutParams();
+    assertNotNull(layoutParams);
+    assertEquals(layoutParams.width, WindowManager.LayoutParams.MATCH_PARENT);
+    assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT);
+}
+</pre>
+
+<p>In the {@link android.test.ViewAsserts#assertOnScreen(android.view.View,android.view.View) assertOnScreen()}
+method call, you should pass in the root view and the view that you are
+expecting to be present on the screen. If the expected view is not found in the
+root view, the assertion method throws an {@link junit.framework.AssertionFailedError}
+exception, otherwise the test passes.</p>
+<p>You can also verify that the layout of a {@link android.widget.Button} is
+correct by getting a reference to its {@link android.view.ViewGroup.LayoutParams}
+object, then call assertion methods to verify that the
+{@link android.widget.Button} object's width and height attributes match the
+expected values.</p>
+<p>The {@code &#64;MediumTest} annotation specifies how the test is categorized,
+relative to its absolute execution time. To learn more about using test size
+annotations, see <a href="#annotations">Apply Test Annotations</a>.</p>
+
+<h3 id="verify_TextView">Verify TextView Layout Parameters</h3>
+<p>You might add a test method like this to verify that a
+{@link android.widget.TextView} initially appears hidden in
+your {@link android.app.Activity}:</p>
+<pre>
+&#64;MediumTest
+public void testInfoTextView_layout() {
+    final View decorView = mClickFunActivity.getWindow().getDecorView();
+    ViewAsserts.assertOnScreen(decorView, mInfoTextView);
+    assertTrue(View.GONE == mInfoTextView.getVisibility());
+}
+</pre>
+<p>You can call {@link android.view.Window#getDecorView()} to get a reference
+to the decor view for the {@link android.app.Activity}. The decor view is the
+top-level ViewGroup ({@link android.widget.FrameLayout}) view in the layout
+hierarchy.</p>
+
+<h3 id="verify_button_behavior">Verify Button Behavior</h3>
+<p>You can use a test method like this to verify that a
+{@link android.widget.TextView} becomes visible when a
+{@link android.widget.Button} is pushed:</p>
+
+<pre>
+&#64;MediumTest
+public void testClickMeButton_clickButtonAndExpectInfoText() {
+    String expectedInfoText = mClickFunActivity.getString(R.string.info_text);
+    TouchUtils.clickView(this, mClickMeButton);
+    assertTrue(View.VISIBLE == mInfoTextView.getVisibility());
+    assertEquals(expectedInfoText, mInfoTextView.getText());
+}
+</pre>
+
+<p>To programmatically click a {@link android.widget.Button} in your
+test, call {@link android.test.TouchUtils#clickView(android.test.InstrumentationTestCase,android.view.View) clickView()}.
+You must pass in a reference to the test case that is being run and a reference
+to the {@link android.widget.Button} to manipulate.</p>
+
+<p class="note"><strong>Note: </strong>The {@link android.test.TouchUtils}
+helper class provides convenience methods for simulating touch interactions
+with your application. You can use these methods to simulate clicking, tapping,
+and dragging of Views or the application screen.</p>
+<p class="caution"><strong>Caution: </strong>The {@link android.test.TouchUtils}
+methods are designed to send events to the UI thread safely from the test thread.
+You should not run {@link android.test.TouchUtils} directly in the UI thread or
+any test method annotated with {@code &#64;UIThread}. Doing so might
+raise the {@code WrongThreadException}.</p>
+
+<h2 id="annotations">Apply Test Annotations</h2>
+<p>The following annotations can be applied to indicate the size of a test
+method:</p>
+<dl>
+<dt>{@link
+android.test.suitebuilder.annotation.SmallTest &#64;SmallTest}</dt>
+<dd>Marks a test that should run as part of the small tests.</dd>
+<dt>{@link
+android.test.suitebuilder.annotation.MediumTest &#64;MediumTest}</dt>
+<dd>Marks a test that should run as part of the medium tests.</dd>
+<dt>{@link android.test.suitebuilder.annotation.LargeTest &#64;LargeTest}</dt>
+<dd>Marks a test that should run as part of the large tests.</dd>
+</dl>
+<p>Typically, a short running test that take only a few milliseconds should be
+marked as a {@code &#64;SmallTest}. Longer running tests (100 milliseconds or
+more) are usually marked as {@code &#64;MediumTest}s or {@code &#64;LargeTest}s,
+depending on whether the test accesses resources on the local system only or
+remote resources over a network. For guidance on using test size annotations,
+see this <a href="https://plus.sandbox.google.com/+AndroidDevelopers/posts/TPy1EeSaSg8">Android Tools Protip</a>.</p>
+<p>You can mark up your test methods with other test annotations to control
+how the tests are organized and run. For more information on other annotations,
+see the {@link java.lang.annotation.Annotation} class reference.</p>
+
+
+
+
diff --git a/docs/html/training/activity-testing/activity-unit-testing.jd b/docs/html/training/activity-testing/activity-unit-testing.jd
new file mode 100644
index 0000000..74dcda9
--- /dev/null
+++ b/docs/html/training/activity-testing/activity-unit-testing.jd
@@ -0,0 +1,134 @@
+page.title=Creating Unit Tests
+trainingnavtop=true
+@jd:body
+
+<!-- This is the training bar -->
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#testcase">Create a Test Case for Activity Unit Testing</a>
+  <li><a href="#test_method">Validate Launch of Another Activity</a>
+</ol>
+
+<h2>Try it out</h2>
+<div class="download-box">
+ <a href="http://developer.android.com/shareables/training/AndroidTestingFun.zip"
+class="button">Download the demo</a>
+ <p class="filename">AndroidTestingFun.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>An {@link android.app.Activity} unit test is an excellent way to quickly
+verify the state of an {@link android.app.Activity} and its interactions with
+other components in isolation (that is, disconnected from the rest of the
+system). A unit test generally tests the smallest possible unit of code
+(which could be a method, class, or component), without dependencies on system
+or network resources. For example, you can write a unit test to check
+that an {@link android.app.Activity} has the correct layout or that it
+triggers an {@link android.content.Intent} object correctly.</p>
+<p>Unit tests are generally not suitable for testing complex UI interaction
+events with the system. Instead, you should use
+the {@link android.test.ActivityInstrumentationTestCase2} class, as described
+in <a href="activity-ui-testing.html">Testing UI Components</a>.</p>
+<p>This lesson shows how you can write a unit test to verify that an
+{@link android.content.Intent} is triggered to launch another
+{@link android.app.Activity}.
+Since the test runs in an isolated environment, the
+{@link android.content.Intent}
+is not actually sent to the Android system, but you can inspect that the
+{@link android.content.Intent} object's payload data is accurate.</p>
+<p>For a complete test case example, take a look at
+{@code LaunchActivityTest.java} in the sample app.</p>
+
+<p class="note"><strong>Note: </strong>To test against system or external
+dependencies, you can use mock objects from a mocking
+framework and inject them into your unit tests. To learn more about the mocking
+framework provided by Android, see
+<a href="{@docRoot}tools/testing/testing_android.html#MockObjectClasses}">Mock
+Object Classes</a>.</p>
+
+<h2 id="testcase">Create a Test Case for Activity Unit Testing</h2>
+<p>The {@link android.test.ActivityUnitTestCase} class provides support for
+isolated testing of a single {@link android.app.Activity}. To create a unit
+test for your {@link android.app.Activity}, your test class should extend
+{@link android.test.ActivityUnitTestCase}.</p>
+
+<p>The {@link android.app.Activity} in an {@link android.test.ActivityUnitTestCase}
+is not automatically started by Android Instrumentation. To start the
+{@link android.app.Activity} in isolation, you need to explicitly call the
+{@link android.test.ActivityUnitTestCase#startActivity(android.content.Intent, android.os.Bundle, java.lang.Object) startActivity()}
+method, and pass in the {@link android.content.Intent} to
+launch your target {@link android.app.Activity}.</p>
+
+<p>For example:</p>
+<pre>
+public class LaunchActivityTest
+        extends ActivityUnitTestCase&lt;LaunchActivity&gt; {
+    ...
+
+    &#64;Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mLaunchIntent = new Intent(getInstrumentation()
+                .getTargetContext(), LaunchActivity.class);
+        startActivity(mLaunchIntent, null, null);
+        final Button launchNextButton =
+                (Button) getActivity()
+                .findViewById(R.id.launch_next_activity_button);
+    }
+}
+</pre>
+
+<h2 id="test_method">Validate Launch of Another Activity</h2>
+<p id="test_goals">Your unit testing goals might include:</p>
+<ul>
+<li>Verifying that {@code LaunchActivity} fires an
+{@link android.content.Intent} when a button is pushed clicked.</li>
+<li>Verifying that the launched {@link android.content.Intent} contains the
+correct payload data.</li>
+</ul>
+
+<p>To verify if an {@link android.content.Intent} was triggered
+following the {@link android.widget.Button} click, you can use the
+{@link android.test.ActivityUnitTestCase#getStartedActivityIntent()} method.
+By using assertion methods, you can verify that the returned
+{@link android.content.Intent} is not null, and that it contains the expected
+string value to launch the next {@link android.app.Activity}. If both assertions
+evaluate to {@code true}, you've successfully verified that the
+{@link android.content.Intent} was correctly sent by your
+{@link android.app.Activity}.</p>
+
+<p>You might implement your test method like this:</p>
+<pre>
+&#64;MediumTest
+public void testNextActivityWasLaunchedWithIntent() {
+    startActivity(mLaunchIntent, null, null);
+    final Button launchNextButton =
+            (Button) getActivity()
+            .findViewById(R.id.launch_next_activity_button);
+    launchNextButton.performClick();
+
+    final Intent launchIntent = getStartedActivityIntent();
+    assertNotNull("Intent was null", launchIntent);
+    assertTrue(isFinishCalled());
+
+    final String payload =
+            launchIntent.getStringExtra(NextActivity.EXTRAS_PAYLOAD_KEY);
+    assertEquals("Payload is empty", LaunchActivity.STRING_PAYLOAD, payload);
+}
+</pre>
+<p>Because {@code LaunchActivity} runs in isolation, you cannot use the
+{@link android.test.TouchUtils} library to manipulate UI controls. To directly
+click a {@link android.widget.Button}, you can call the
+{@link android.view.View#performClick()} method instead.</p>
+
+
+
+
+
+
+
diff --git a/docs/html/training/activity-testing/index.jd b/docs/html/training/activity-testing/index.jd
new file mode 100644
index 0000000..ddede71
--- /dev/null
+++ b/docs/html/training/activity-testing/index.jd
@@ -0,0 +1,68 @@
+page.title=Testing Your Android Activity
+page.tags="testing"
+
+trainingnavtop=true
+startpage=true
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- Required platform, tools, add-ons, devices, knowledge, etc. -->
+<h2>Dependencies and prerequisites</h2>
+<ul>
+  <li>Android 2.2 (API Level 8) or higher.</li>
+</ul>
+
+<h2>You Should Also Read</h2>
+<ul>
+<li><a href="{@docRoot}tools/testing/index.html">Testing
+(Developer's Guide)</a></li>
+</ul>
+
+</div>
+</div>
+
+<p>You should be writing and running tests as part of your Android application
+development cycle. Well-written tests can help you to catch bugs early in
+development and give you confidence in your code.</p>
+
+<p>A <em>test case</em> defines a set of objects and methods to run multiple
+tests independently from each other. Test cases can be organized into
+<em>test suites</em> and run programmatically, in a repeatable manner, with
+a <em>test runner</em> provided by a testing framework.</p>
+
+<p>The lessons in this class teaches you how to use the Android's custom
+testing framework that is based on the popular JUnit framework. You can
+write test cases to verify specific behavior in your application, and check for
+consistency across different Android devices. Your test cases also serve as a
+form of internal code documentation by describing the expected behavior of
+app components.</p>
+
+<h2>Lessons</h2>
+
+<!-- Create a list of the lessons in this class along with a short description
+of each lesson. These should be short and to the point. It should be clear from
+reading the summary whether someone will want to jump to a lesson or not.-->
+
+<dl>
+  <dt><b><a href="preparing-activity-testing.html">Setting Up Your Test
+Environment</a></b></dt>
+    <dd>Learn how to create your test project.</dd>
+  <dt><b><a href="activity-basic-testing.html">Creating and Running a Test 
+Case</a></b></dt>
+    <dd>Learn how to write test cases to verify the
+expected properties of your {@link android.app.Activity}, and run the test
+cases with the {@code Instrumentation} test runner provided by the Android
+framework.</dd>
+  <dt><b><a href="activity-ui-testing.html">Testing UI Components</a></b></dt>
+    <dd>Learn how to test the behavior of specific UI
+components in your {@link android.app.Activity}.</dd>
+  <dt><b><a href="activity-unit-testing.html">Creating Unit Tests</a></b></dt>
+    <dd>Learn how to how to perform unit testing to
+verify the behavior of an Activity in isolation.</dd>
+  <dt><b><a href="activity-functional-testing.html">Creating Functional Tests</a></b></dt>
+    <dd>Learn how to perform functional testing to
+verify the interaction of multiple Activities.</dd>
+
diff --git a/docs/html/training/activity-testing/preparing-activity-testing.jd b/docs/html/training/activity-testing/preparing-activity-testing.jd
new file mode 100644
index 0000000..c43c9ed
--- /dev/null
+++ b/docs/html/training/activity-testing/preparing-activity-testing.jd
@@ -0,0 +1,95 @@
+page.title=Setting Up Your Test Environment
+trainingnavtop=true
+
+@jd:body
+
+<!-- This is the training bar -->
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#eclipse">Set Up Eclipse for Testing</a></li>
+  <li><a href="#cmdline">Set Up the Command Line Interface for Testing</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+<li><a href="{@docRoot}sdk/index.html">Getting the SDK Bundle</a></li>
+<li><a href="{@docRoot}tools/testing/testing_eclipse.html">Testing from Eclipse
+with ADT</a></li>
+<li><a href="{@docRoot}tools/testing/testing_otheride.html">Testing from Other
+IDEs</a></li>
+</ul>
+
+<h2>Try it out</h2>
+<div class="download-box">
+ <a href="http://developer.android.com/shareables/training/AndroidTestingFun.zip"
+class="button">Download the demo</a>
+ <p class="filename">AndroidTestingFun.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>Before you start writing and running your tests, you should set up your test
+development environment. This lesson teaches you how to set up the Eclipse
+IDE to build and run tests, and how to
+build and run tests with the Gradle framework by using the command line
+interface.</p>
+
+<p class="note"><strong>Note:</strong> To help you get started, the lessons are
+based on Eclipse with the ADT plugin. However, for your own test development, you
+are free to use the IDE of your choice or the command-line.</p>
+
+<h2 id="eclipse">Set Up Eclipse for Testing</h2>
+<p>Eclipse with the Android Developer Tools (ADT) plugin provides an integrated
+development environment for you to create, build, and run Android application
+test cases from a graphical user interface (GUI). A convenient feature that
+Eclipse provides is the ability to auto-generate a new test project that
+corresponds with your Android application project</a>.
+
+<p>To set up your test environment in Eclipse:</p>
+
+<ol>
+<li><a href="{@docRoot}sdk/installing/bundle.html">Download and install the
+Eclipse ADT plugin</a>, if you haven’t installed it yet.</li>
+<li>Import or create the Android application project that you want to test
+against.</li>
+<li>Generate a test project that corresponds to the application project under
+test. To generate a test project for the app project that you imported:</p>
+   <ol type="a">
+   <li>In the Package Explorer, right-click on your app project, then
+select <strong>Android Tools</strong> &gt; <strong>New Test Project</strong>.</li>
+   <li>In the New Android Test Project wizard, set the property
+values for your test project then click <strong>Finish</strong>.</li>
+   </ol>
+</li>
+</ol>
+<p>You should now be able to create, build, and run test
+cases from your Eclipse environment. To learn how to perform these tasks in
+Eclipse, proceed to <a href="activity-basic-testing.html">Creating and Running 
+a Test Case</a>.</p>
+
+<h2 id="cmdline">Set Up the Command Line Interface for Testing</h2>
+<p>If you are using Gradle version 1.6 or higher as your build environment, you
+can build and run your Android application tests from the command line by using
+the Gradle Wrapper. Make sure that in your {@code gradle.build} file, the
+<a href={@docRoot}guide/topics/manifest/uses-sdk-element.html#min>minSdkVersion</a>
+attribute in the {@code defaultConfig} section is set to 8 or higher. You can
+refer to the sample {@code gradle.build} file that is
+included in the download bundle for this training class.</p>
+<p>To run your tests with the Gradle Wrapper:</p>
+<ol>
+   <li>Connect a physical Android device to your machine or launch the Android
+Emulator.</li>
+   <li>Run the following command from your project directory:
+      <pre>./gradlew build connectedCheck</pre>
+   </li>
+</ol>
+<p>To learn more about using Gradle for Android testing, see the
+<a href="//tools.android.com/tech-docs/new-build-system/user-guide#TOC-Testing">Gradle Plugin User Guide</a>.</p>
+<p>To learn more about using command line tools other than Gradle for test
+development, see
+<a href="{@docRoot}tools/testing/testing_otheride.html">Testing from Other IDEs</a>.</p>
+
diff --git a/docs/html/training/testing.jd b/docs/html/training/testing.jd
new file mode 100644
index 0000000..c55370d
--- /dev/null
+++ b/docs/html/training/testing.jd
@@ -0,0 +1,7 @@
+page.title=Best Practices for Testing
+page.trainingcourse=true
+
+@jd:body
+
+<p>These classes and articles provide information about how to
+test your Android application.</p>
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index 40c170e..b884620 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -1205,6 +1205,45 @@
   </li>
   <!-- End security and user info -->
 
+  <li class="nav-section">
+    <div class="nav-section-header">
+      <a href="<?cs var:toroot ?>training/testing.html">
+      <span class="small">Best Practices for</span><br/>
+              Testing
+      </a>
+    </div>
+    <ul>
+      <li class="nav-section">
+      <div class="nav-section-header"><a href="<?cs var:toroot ?>training/activity-testing/index.html"
+         description="How to test Activities in your Android applications.">
+            Testing Your Activity
+          </a></div>
+        <ul>
+          <li><a href="<?cs var:toroot ?>training/activity-testing/preparing-activity-testing.html">
+            <span class="en">Setting Up Your Test Environment</span>
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/activity-testing/activity-basic-testing.html">
+            <span class="en">Creating and Running a Test Case</span>
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/activity-testing/activity-ui-testing.html">
+            <span class="en">Testing UI Components</span>
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/activity-testing/activity-unit-testing.html">
+            <span class="en">Creating Unit Tests</span>
+          </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/activity-testing/activity-functional-testing.html">
+            <span class="en">Creating Functional Tests</span>
+          </a>
+          </li>
+        </ul>
+      </li>
+    </ul>
+  </li>
+  <!-- End best Testing -->
 
   <li class="nav-section">
     <div class="nav-section-header">
diff --git a/packages/PrintSpooler/Android.mk b/packages/PrintSpooler/Android.mk
index 8ae0302..f65fe4b 100644
--- a/packages/PrintSpooler/Android.mk
+++ b/packages/PrintSpooler/Android.mk
@@ -24,8 +24,6 @@
 
 LOCAL_JAVA_LIBRARIES := framework-base
 
-LOCAL_CERTIFICATE := platform
-
 LOCAL_PROGUARD_ENABLED := disabled
 
 include $(BUILD_PACKAGE)
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index ab7ea09..6fa3ed4 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -20,18 +20,22 @@
         package="com.android.printspooler"
         android:sharedUserId="android.uid.printspooler"
         android:versionName="1"
-        android:versionCode="1"
-        coreApp="true">
+        android:versionCode="1">
 
-    <uses-sdk android:minSdkVersion="18" android:targetSdkVersion="18"/>
+    <!-- Allows an application to call APIs that give it access to all print jobs
+         on the device. Usually an app can access only the print jobs it created.
+    -->
+    <permission
+        android:name="com.android.printspooler.permission.ACCESS_ALL_PRINT_JOBS"
+        android:label="@string/permlab_accessAllPrintJobs"
+        android:description="@string/permdesc_accessAllPrintJobs"
+        android:protectionLevel="signature" />
 
+    <uses-permission android:name="com.android.printspooler.permission.ACCESS_ALL_PRINT_JOBS"/>
     <uses-permission android:name="android.permission.ACCESS_ALL_PRINT_JOBS"/>
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
 
-    <permission android:name="android.permission.BIND_PRINT_SPOOLER_SERVICE"
-        android:label="@string/permlab_bindPrintSpoolerService"
-        android:description="@string/permdesc_bindPrintSpoolerService"
-        android:protectionLevel="signature" />
+    <uses-sdk android:minSdkVersion="18" android:targetSdkVersion="18"/>
 
     <application
             android:allowClearUserData="false"
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index ee3cf84..235a7a1 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -94,6 +94,9 @@
     <!-- Template for the notificaiton label for a failed print job. [CHAR LIMIT=25] -->
     <string name="failed_notification_title_template">Printer error <xliff:g id="print_job_name" example="foo.jpg">%1$s</xliff:g></string>
 
+    <!-- Template for the notificaiton label for a blocked print job. [CHAR LIMIT=25] -->
+    <string name="blocked_notification_title_template">Printer blocked <xliff:g id="print_job_name" example="foo.jpg">%1$s</xliff:g></string>
+
     <!-- Label for the notification button for cancelling a print job. [CHAR LIMIT=25] -->
     <string name="cancel">Cancel</string>
 
@@ -103,6 +106,9 @@
     <!-- Message that there is no connection to a printer. [CHAR LIMIT=40] -->
     <string name="no_connection_to_printer">No connection to printer</string>
 
+    <!-- Label for an unknown reason for failed or blocked print job. [CHAR LIMIT=25] -->
+    <string name="reason_unknown">unknown</string>
+
     <!-- Arrays -->
 
     <!-- Color mode labels. -->
@@ -129,12 +135,14 @@
         <item>Range</item>
     </string-array>
 
-    <!-- Title of an application permission, listed so the user can choose
-         whether they want to allow the application to do this. -->
-    <string name="permlab_bindPrintSpoolerService">bind to a print spooler service</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_bindPrintSpoolerService">Allows the holder to bind to the top-level
-        interface of a print spooler service. Should never be needed for normal apps.</string>
+    <!-- Permissions -->
+
+    <!-- Title of an application permission, listed so the user can choose whether they want
+         to allow the application to do this. -->
+    <string name="permlab_accessAllPrintJobs">access all print jobs</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_accessAllPrintJobs">Allows the holder to access print jobs
+        created by another app. Should never be needed for normal apps.</string>
 
 </resources>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
index 28fd0e0..ad8d95a 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
@@ -75,6 +75,8 @@
 
     private List<PrinterInfo> mFavoritePrinters;
 
+    private PrinterId mTrackedPrinter;
+
     public FusedPrintersProvider(Context context) {
         super(context);
         mPersistenceManager = new PersistenceManager(context);
@@ -166,6 +168,10 @@
     private boolean cancelInternal() {
         if (mDiscoverySession != null
                 && mDiscoverySession.isPrinterDiscoveryStarted()) {
+            if (mTrackedPrinter != null) {
+                mDiscoverySession.stopPrinterStateTracking(mTrackedPrinter);
+                mTrackedPrinter = null;
+            }
             mDiscoverySession.stopPrinterDiscovery();
             return true;
         } else if (mPersistenceManager.isReadHistoryInProgress()) {
@@ -195,10 +201,14 @@
         onStopLoading();
     }
 
-    public void refreshPrinter(PrinterId printerId) {
+    public void setTrackedPrinter(PrinterId printerId) {
         if (isStarted() && mDiscoverySession != null
                 && mDiscoverySession.isPrinterDiscoveryStarted()) {
-            mDiscoverySession.requestPrinterUpdate(printerId);
+            if (mTrackedPrinter != null) {
+                mDiscoverySession.stopPrinterStateTracking(mTrackedPrinter);
+            }
+            mTrackedPrinter = printerId;
+            mDiscoverySession.startPrinterStateTracking(printerId);
         }
     }
 
diff --git a/packages/PrintSpooler/src/com/android/printspooler/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/NotificationController.java
index c116d37..43a751c 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/NotificationController.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/NotificationController.java
@@ -32,6 +32,7 @@
 import android.print.IPrintManager;
 import android.print.PrintJobInfo;
 import android.print.PrintManager;
+import android.text.TextUtils;
 import android.util.Log;
 
 /**
@@ -64,22 +65,27 @@
                     + " state:" + PrintJobInfo.stateToString(printJob.getState()));
         }
         switch (printJob.getState()) {
-            case PrintJobInfo.STATE_QUEUED: {
-                createPrintingNotificaiton(printJob);
+            case PrintJobInfo.STATE_QUEUED:
+            case PrintJobInfo.STATE_STARTED: {
+                createPrintingNotification(printJob);
             } break;
 
             case PrintJobInfo.STATE_FAILED: {
-                createFailedNotificaiton(printJob);
+                createFailedNotification(printJob);
             } break;
 
             case PrintJobInfo.STATE_COMPLETED:
             case PrintJobInfo.STATE_CANCELED: {
                 removeNotification(printJob.getId());
             } break;
+
+            case PrintJobInfo.STATE_BLOCKED: {
+                createBlockedNotification(printJob);
+            } break;
         }
     }
 
-    private void createPrintingNotificaiton(PrintJobInfo printJob) {
+    private void createPrintingNotification(PrintJobInfo printJob) {
         Notification.Builder builder = new Notification.Builder(mContext)
                 .setSmallIcon(R.drawable.stat_notify_print)
                 .setContentTitle(mContext.getString(R.string.printing_notification_title_template,
@@ -93,17 +99,36 @@
         mNotificationManager.notify(printJob.getId(), builder.build());
     }
 
-    private void createFailedNotificaiton(PrintJobInfo printJob) {
+    private void createFailedNotification(PrintJobInfo printJob) {
+        String reason = !TextUtils.isEmpty(printJob.getStateReason())
+                ? printJob.getStateReason() : mContext.getString(R.string.reason_unknown);
+
         Notification.Builder builder = new Notification.Builder(mContext)
                 .setSmallIcon(R.drawable.stat_notify_error)
                 .setContentTitle(mContext.getString(R.string.failed_notification_title_template,
                         printJob.getLabel()))
                 .addAction(R.drawable.stat_notify_cancelling, mContext.getString(R.string.cancel),
                         createCancelIntent(printJob))
-                // TODO: Use appropriate icon when assets are ready
                 .addAction(android.R.drawable.ic_secure, mContext.getString(R.string.restart),
                         createRestartIntent(printJob.getId()))
-                .setContentText(printJob.getFailureReason())
+                .setContentText(reason)
+                .setWhen(System.currentTimeMillis())
+                .setOngoing(true)
+                .setShowWhen(true);
+        mNotificationManager.notify(printJob.getId(), builder.build());
+    }
+
+    private void createBlockedNotification(PrintJobInfo printJob) {
+        String reason = !TextUtils.isEmpty(printJob.getStateReason())
+                ? printJob.getStateReason() : mContext.getString(R.string.reason_unknown);
+
+        Notification.Builder builder = new Notification.Builder(mContext)
+                .setSmallIcon(R.drawable.stat_notify_error)
+                .setContentTitle(mContext.getString(R.string.blocked_notification_title_template,
+                        printJob.getLabel()))
+                .addAction(R.drawable.stat_notify_cancelling, mContext.getString(R.string.cancel),
+                        createCancelIntent(printJob))
+                .setContentText(reason)
                 .setWhen(System.currentTimeMillis())
                 .setOngoing(true)
                 .setShowWhen(true);
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
index 607be90..520331cb 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
@@ -473,6 +473,11 @@
 
             mControllerState = CONTROLLER_STATE_WRITE_COMPLETED;
 
+            // Update the document size.
+            File file = PrintSpoolerService.peekInstance()
+                    .generateFileForPrintJob(mPrintJobId);
+            mDocument.info.setDataSize(file.length());
+
             // Update which pages we have fetched.
             mDocument.pages = PageRangeUtils.normalize(pages);
 
@@ -1117,7 +1122,7 @@
                         (Loader<?>) getLoaderManager().getLoader(
                                 LOADER_ID_PRINTERS_LOADER);
                 if (printersLoader != null) {
-                    printersLoader.refreshPrinter(printer.getId());
+                    printersLoader.setTrackedPrinter(printer.getId());
                 }
             }
         }
@@ -1351,10 +1356,6 @@
             return mEditorState == EDITOR_STATE_CONFIRMED_PRINT;
         }
 
-//        public void confirmPreview() {
-//            mEditorState = EDITOR_STATE_CONFIRMED_PREVIEW;
-//        }
-
         public PageRange[] getRequestedPages() {
             if (hasErrors()) {
                 return null;
@@ -1374,7 +1375,7 @@
                         toIndex = Integer.parseInt(range.substring(
                                 dashIndex + 1, range.length())) - 1;
                     } else {
-                        fromIndex = toIndex = Integer.parseInt(range);
+                        fromIndex = toIndex = Integer.parseInt(range) - 1;
                     }
 
                     PageRange pageRange = new PageRange(fromIndex, toIndex);
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
index c1f4180..dd2598c 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
@@ -335,7 +335,9 @@
                 final boolean sameState = (state == printJob.getState())
                         || (state == PrintJobInfo.STATE_ANY)
                         || (state == PrintJobInfo.STATE_ANY_VISIBLE_TO_CLIENTS
-                        && printJob.getState() > PrintJobInfo.STATE_CREATED);
+                            && isStateVisibleToUser(printJob.getState()))
+                        || (state == PrintJobInfo.STATE_ANY_ACTIVE
+                            && isActiveState(printJob.getState()));
                 if (sameComponent && sameAppId && sameState) {
                     if (foundPrintJobs == null) {
                         foundPrintJobs = new ArrayList<PrintJobInfo>();
@@ -347,6 +349,11 @@
         return foundPrintJobs;
     }
 
+    private boolean isStateVisibleToUser(int state) {
+        return (isActiveState(state) && (state == PrintJobInfo.STATE_FAILED
+                || state == PrintJobInfo.STATE_COMPLETED|| state == PrintJobInfo.STATE_CANCELED));
+    }
+
     public PrintJobInfo getPrintJobInfo(int printJobId, int appId) {
         synchronized (mLock) {
             final int printJobCount = mPrintJobs.size();
@@ -389,14 +396,12 @@
 
             switch (printJob.getState()) {
                 case PrintJobInfo.STATE_QUEUED:
-                case PrintJobInfo.STATE_STARTED: {
-                    // We have a print job that was queued or started in the
-                    // past
-                    // but the device battery died or a crash occurred. In this
-                    // case
-                    // we assume the print job failed and let the user decide
-                    // whether
-                    // to restart the job or just
+                case PrintJobInfo.STATE_STARTED:
+                case PrintJobInfo.STATE_BLOCKED: {
+                    // We have a print job that was queued or started or blocked in
+                    // the past but the device battery died or a crash occurred. In
+                    // this case we assume the print job failed and let the user
+                    // decide whether to restart the job or just cancel it.
                     setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED,
                             getString(R.string.no_connection_to_printer));
                 }
@@ -501,7 +506,7 @@
                 success = true;
 
                 printJob.setState(state);
-                printJob.setFailureReason(error);
+                printJob.setStateReason(error);
                 mNotificationController.onPrintJobStateChanged(printJob);
 
                 if (DEBUG_PRINT_JOB_LIFECYCLE) {
@@ -568,7 +573,8 @@
     private boolean isActiveState(int printJobState) {
         return printJobState == PrintJobInfo.STATE_CREATED
                 || printJobState == PrintJobInfo.STATE_QUEUED
-                || printJobState == PrintJobInfo.STATE_STARTED;
+                || printJobState == PrintJobInfo.STATE_STARTED
+                || printJobState == PrintJobInfo.STATE_BLOCKED;
     }
 
     public boolean setPrintJobTag(int printJobId, String tag) {
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 136a85e..9c14654 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -36,6 +36,7 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.bluetooth.BluetoothTetheringDataTracker;
+import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -152,6 +153,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Random;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -424,9 +426,6 @@
 
     TelephonyManager mTelephonyManager;
 
-    // We only want one checkMobileProvisioning after booting.
-    volatile boolean mFirstProvisioningCheckStarted = false;
-
     public ConnectivityService(Context context, INetworkManagementService netd,
             INetworkStatsService statsService, INetworkPolicyManager policyManager) {
         // Currently, omitting a NetworkFactory will create one internally
@@ -656,9 +655,6 @@
         mSettingsObserver = new SettingsObserver(mHandler, EVENT_APPLY_GLOBAL_HTTP_PROXY);
         mSettingsObserver.observe(mContext);
 
-        mCaptivePortalTracker = CaptivePortalTracker.makeCaptivePortalTracker(mContext, this);
-        loadGlobalProxy();
-
         mDataConnectionStats = new DataConnectionStats(mContext);
         mDataConnectionStats.startMonitoring();
 
@@ -686,6 +682,10 @@
                 new IntentFilter(filter));
 
         mPacManager = new PacManager(mContext);
+
+        filter = new IntentFilter();
+        filter.addAction(CONNECTED_TO_PROVISIONING_NETWORK_ACTION);
+        mContext.registerReceiver(mProvisioningReceiver, filter);
     }
 
     /**
@@ -954,6 +954,46 @@
         return getNetworkInfo(mActiveDefaultNetwork, uid);
     }
 
+    /**
+     * Find the first Provisioning network.
+     *
+     * @return NetworkInfo or null if none.
+     */
+    private NetworkInfo getProvisioningNetworkInfo() {
+        enforceAccessPermission();
+
+        // Find the first Provisioning Network
+        NetworkInfo provNi = null;
+        for (NetworkInfo ni : getAllNetworkInfo()) {
+            if (ni.getDetailedState()
+                    == NetworkInfo.DetailedState.CONNECTED_TO_PROVISIONING_NETWORK) {
+                provNi = ni;
+                break;
+            }
+        }
+        if (DBG) log("getProvisioningNetworkInfo: X provNi=" + provNi);
+        return provNi;
+    }
+
+    /**
+     * Find the first Provisioning network or the ActiveDefaultNetwork
+     * if there is no Provisioning network
+     *
+     * @return NetworkInfo or null if none.
+     */
+    @Override
+    public NetworkInfo getProvisioningOrActiveNetworkInfo() {
+        enforceAccessPermission();
+
+        NetworkInfo provNi = getProvisioningNetworkInfo();
+        if (provNi == null) {
+            final int uid = Binder.getCallingUid();
+            provNi = getNetworkInfo(mActiveDefaultNetwork, uid);
+        }
+        if (DBG) log("getProvisioningOrActiveNetworkInfo: X provNi=" + provNi);
+        return provNi;
+    }
+
     public NetworkInfo getActiveNetworkInfoUnfiltered() {
         enforceAccessPermission();
         if (isNetworkTypeValid(mActiveDefaultNetwork)) {
@@ -1316,8 +1356,10 @@
                                 feature);
                     }
                     if (network.reconnect()) {
+                        if (DBG) log("startUsingNetworkFeature X: return APN_REQUEST_STARTED");
                         return PhoneConstants.APN_REQUEST_STARTED;
                     } else {
+                        if (DBG) log("startUsingNetworkFeature X: return APN_REQUEST_FAILED");
                         return PhoneConstants.APN_REQUEST_FAILED;
                     }
                 } else {
@@ -1329,9 +1371,11 @@
                             mNetRequestersPids[usedNetworkType].add(currentPid);
                         }
                     }
+                    if (DBG) log("startUsingNetworkFeature X: return -1 unsupported feature.");
                     return -1;
                 }
             }
+            if (DBG) log("startUsingNetworkFeature X: return APN_TYPE_NOT_AVAILABLE");
             return PhoneConstants.APN_TYPE_NOT_AVAILABLE;
          } finally {
             if (DBG) {
@@ -1365,11 +1409,12 @@
             }
         }
         if (found && u != null) {
+            if (VDBG) log("stopUsingNetworkFeature: X");
             // stop regardless of how many other time this proc had called start
             return stopUsingNetworkFeature(u, true);
         } else {
             // none found!
-            if (VDBG) log("stopUsingNetworkFeature - not a live request, ignoring");
+            if (VDBG) log("stopUsingNetworkFeature: X not a live request, ignoring");
             return 1;
         }
     }
@@ -1952,6 +1997,9 @@
          */
         if (mNetConfigs[prevNetType].isDefault()) {
             if (mActiveDefaultNetwork == prevNetType) {
+                if (DBG) {
+                    log("tryFailover: set mActiveDefaultNetwork=-1, prevNetType=" + prevNetType);
+                }
                 mActiveDefaultNetwork = -1;
             }
 
@@ -2146,6 +2194,9 @@
     }
 
     void systemReady() {
+        mCaptivePortalTracker = CaptivePortalTracker.makeCaptivePortalTracker(mContext, this);
+        loadGlobalProxy();
+
         synchronized(this) {
             mSystemReady = true;
             if (mInitialBroadcast != null) {
@@ -2176,10 +2227,11 @@
     };
 
     private boolean isNewNetTypePreferredOverCurrentNetType(int type) {
-        if ((type != mNetworkPreference &&
-                    mNetConfigs[mActiveDefaultNetwork].priority >
-                    mNetConfigs[type].priority) ||
-                mNetworkPreference == mActiveDefaultNetwork) return false;
+        if (((type != mNetworkPreference)
+                      && (mNetConfigs[mActiveDefaultNetwork].priority > mNetConfigs[type].priority))
+                   || (mNetworkPreference == mActiveDefaultNetwork)) {
+            return false;
+        }
         return true;
     }
 
@@ -2193,6 +2245,11 @@
         final NetworkStateTracker thisNet = mNetTrackers[newNetType];
         final String thisIface = thisNet.getLinkProperties().getInterfaceName();
 
+        if (VDBG) {
+            log("handleConnect: E newNetType=" + newNetType + " thisIface=" + thisIface
+                    + " isFailover" + isFailover);
+        }
+
         // if this is a default net and other default is running
         // kill the one not preferred
         if (mNetConfigs[newNetType].isDefault()) {
@@ -2355,6 +2412,10 @@
     private void handleConnectivityChange(int netType, boolean doReset) {
         int resetMask = doReset ? NetworkUtils.RESET_ALL_ADDRESSES : 0;
         boolean exempt = ConnectivityManager.isNetworkTypeExempt(netType);
+        if (VDBG) {
+            log("handleConnectivityChange: netType=" + netType + " doReset=" + doReset
+                    + " resetMask=" + resetMask);
+        }
 
         /*
          * If a non-default network is enabled, add the host routes that
@@ -2422,7 +2483,9 @@
         boolean resetDns = updateRoutes(newLp, curLp, mNetConfigs[netType].isDefault(), exempt);
 
         if (resetMask != 0 || resetDns) {
+            if (VDBG) log("handleConnectivityChange: resetting");
             if (curLp != null) {
+                if (VDBG) log("handleConnectivityChange: resetting curLp=" + curLp);
                 for (String iface : curLp.getAllInterfaceNames()) {
                     if (TextUtils.isEmpty(iface) == false) {
                         if (resetMask != 0) {
@@ -2459,6 +2522,7 @@
         // Update 464xlat state.
         NetworkStateTracker tracker = mNetTrackers[netType];
         if (mClat.requiresClat(netType, tracker)) {
+
             // If the connection was previously using clat, but is not using it now, stop the clat
             // daemon. Normally, this happens automatically when the connection disconnects, but if
             // the disconnect is not reported, or if the connection's LinkProperties changed for
@@ -2512,6 +2576,7 @@
 
         for (RouteInfo r : routeDiff.removed) {
             if (isLinkDefault || ! r.isDefaultRoute()) {
+                if (VDBG) log("updateRoutes: default remove route r=" + r);
                 removeRoute(curLp, r, TO_DEFAULT_TABLE);
             }
             if (isLinkDefault == false) {
@@ -2849,9 +2914,8 @@
         public void handleMessage(Message msg) {
             NetworkInfo info;
             switch (msg.what) {
-                case NetworkStateTracker.EVENT_STATE_CHANGED:
+                case NetworkStateTracker.EVENT_STATE_CHANGED: {
                     info = (NetworkInfo) msg.obj;
-                    int type = info.getType();
                     NetworkInfo.State state = info.getState();
 
                     if (VDBG || (state == NetworkInfo.State.CONNECTED) ||
@@ -2861,15 +2925,17 @@
                             state + "/" + info.getDetailedState());
                     }
 
-                    // After booting we'll check once for mobile provisioning
-                    // if we've provisioned by and connected.
-                    if (!mFirstProvisioningCheckStarted
+                    // Since mobile has the notion of a network/apn that can be used for
+                    // provisioning we need to check every time we're connected as
+                    // CaptiveProtalTracker won't detected it because DCT doesn't report it
+                    // as connected as ACTION_ANY_DATA_CONNECTION_STATE_CHANGED instead its
+                    // reported as ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN. Which
+                    // is received by MDST and sent here as EVENT_STATE_CHANGED.
+                    if (ConnectivityManager.isNetworkTypeMobile(info.getType())
                             && (0 != Settings.Global.getInt(mContext.getContentResolver(),
                                         Settings.Global.DEVICE_PROVISIONED, 0))
                             && (state == NetworkInfo.State.CONNECTED)) {
-                        log("check provisioning after booting");
-                        mFirstProvisioningCheckStarted = true;
-                        checkMobileProvisioning(true, CheckMp.MAX_TIMEOUT_MS, null);
+                        checkMobileProvisioning(CheckMp.MAX_TIMEOUT_MS);
                     }
 
                     EventLogTags.writeConnectivityStateChanged(
@@ -2881,6 +2947,30 @@
                     } else if (info.getDetailedState() ==
                             DetailedState.CAPTIVE_PORTAL_CHECK) {
                         handleCaptivePortalTrackerCheck(info);
+                    } else if (info.getDetailedState() ==
+                            DetailedState.CONNECTED_TO_PROVISIONING_NETWORK) {
+                        /**
+                         * TODO: Create ConnectivityManager.TYPE_MOBILE_PROVISIONING
+                         * for now its an in between network, its a network that
+                         * is actually a default network but we don't want it to be
+                         * announced as such to keep background applications from
+                         * trying to use it. It turns out that some still try so we
+                         * take the additional step of clearing any default routes
+                         * to the link that may have incorrectly setup by the lower
+                         * levels.
+                         */
+                        LinkProperties lp = getLinkProperties(info.getType());
+                        if (DBG) {
+                            log("EVENT_STATE_CHANGED: connected to provisioning network, lp=" + lp);
+                        }
+
+                        // Clear any default routes setup by the radio so
+                        // any activity by applications trying to use this
+                        // connection will fail until the provisioning network
+                        // is enabled.
+                        for (RouteInfo r : lp.getRoutes()) {
+                            removeRoute(lp, r, TO_DEFAULT_TABLE);
+                        }
                     } else if (state == NetworkInfo.State.DISCONNECTED) {
                         handleDisconnect(info);
                     } else if (state == NetworkInfo.State.SUSPENDED) {
@@ -2899,18 +2989,21 @@
                         mLockdownTracker.onNetworkInfoChanged(info);
                     }
                     break;
-                case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
+                }
+                case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED: {
                     info = (NetworkInfo) msg.obj;
                     // TODO: Temporary allowing network configuration
                     //       change not resetting sockets.
                     //       @see bug/4455071
                     handleConnectivityChange(info.getType(), false);
                     break;
-                case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED:
+                }
+                case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED: {
                     info = (NetworkInfo) msg.obj;
-                    type = info.getType();
+                    int type = info.getType();
                     updateNetworkSettings(mNetTrackers[type]);
                     break;
+                }
             }
         }
     }
@@ -3806,76 +3899,153 @@
                          enabled));
     }
 
+    private boolean isMobileDataStateTrackerReady() {
+        MobileDataStateTracker mdst =
+                (MobileDataStateTracker) mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI];
+        return (mdst != null) && (mdst.isReady());
+    }
+
+    /**
+     * The ResultReceiver resultCode for checkMobileProvisioning (CMP_RESULT_CODE)
+     */
+
+    /**
+     * No connection was possible to the network.
+     */
+    public static final int CMP_RESULT_CODE_NO_CONNECTION = 0;
+
+    /**
+     * A connection was made to the internet, all is well.
+     */
+    public static final int CMP_RESULT_CODE_CONNECTABLE = 1;
+
+    /**
+     * A connection was made but there was a redirection, we appear to be in walled garden.
+     * This is an indication of a warm sim on a mobile network.
+     */
+    public static final int CMP_RESULT_CODE_REDIRECTED = 2;
+
+    /**
+     * A connection was made but no dns server was available to resolve a name to address.
+     * This is an indication of a warm sim on a mobile network.
+     */
+    public static final int CMP_RESULT_CODE_NO_DNS = 3;
+
+    /**
+     * A connection was made but could not open a TCP connection.
+     * This is an indication of a warm sim on a mobile network.
+     */
+    public static final int CMP_RESULT_CODE_NO_TCP_CONNECTION = 4;
+
+    /**
+     * The mobile network is a provisioning network.
+     * This is an indication of a warm sim on a mobile network.
+     */
+    public static final int CMP_RESULT_CODE_PROVISIONING_NETWORK = 5;
+
+    AtomicBoolean mIsCheckingMobileProvisioning = new AtomicBoolean(false);
+
     @Override
-    public int checkMobileProvisioning(final boolean sendNotification, int suggestedTimeOutMs,
-            final ResultReceiver resultReceiver) {
-        log("checkMobileProvisioning: E sendNotification=" + sendNotification
-                + " suggestedTimeOutMs=" + suggestedTimeOutMs
-                + " resultReceiver=" + resultReceiver);
-        enforceChangePermission();
-
-        mFirstProvisioningCheckStarted = true;
-
-        int timeOutMs = suggestedTimeOutMs;
-        if (suggestedTimeOutMs > CheckMp.MAX_TIMEOUT_MS) {
-            timeOutMs = CheckMp.MAX_TIMEOUT_MS;
-        }
-
-        // Check that mobile networks are supported
-        if (!isNetworkSupported(ConnectivityManager.TYPE_MOBILE)
-                || !isNetworkSupported(ConnectivityManager.TYPE_MOBILE_HIPRI)) {
-            log("checkMobileProvisioning: X no mobile network");
-            if (resultReceiver != null) {
-                resultReceiver.send(ConnectivityManager.CMP_RESULT_CODE_NO_CONNECTION, null);
-            }
-            return timeOutMs;
-        }
+    public int checkMobileProvisioning(int suggestedTimeOutMs) {
+        int timeOutMs = -1;
+        if (DBG) log("checkMobileProvisioning: E suggestedTimeOutMs=" + suggestedTimeOutMs);
+        enforceConnectivityInternalPermission();
 
         final long token = Binder.clearCallingIdentity();
         try {
+            timeOutMs = suggestedTimeOutMs;
+            if (suggestedTimeOutMs > CheckMp.MAX_TIMEOUT_MS) {
+                timeOutMs = CheckMp.MAX_TIMEOUT_MS;
+            }
+
+            // Check that mobile networks are supported
+            if (!isNetworkSupported(ConnectivityManager.TYPE_MOBILE)
+                    || !isNetworkSupported(ConnectivityManager.TYPE_MOBILE_HIPRI)) {
+                if (DBG) log("checkMobileProvisioning: X no mobile network");
+                return timeOutMs;
+            }
+
+            // If we're already checking don't do it again
+            // TODO: Add a queue of results...
+            if (mIsCheckingMobileProvisioning.getAndSet(true)) {
+                if (DBG) log("checkMobileProvisioning: X already checking ignore for the moment");
+                return timeOutMs;
+            }
+
+            // Start off with notification off
+            setProvNotificationVisible(false, ConnectivityManager.TYPE_NONE, null, null);
+
+            // See if we've alreadying determined if we've got a provsioning connection
+            // if so we don't need to do anything active
+            MobileDataStateTracker mdstDefault = (MobileDataStateTracker)
+                    mNetTrackers[ConnectivityManager.TYPE_MOBILE];
+            boolean isDefaultProvisioning = mdstDefault.isProvisioningNetwork();
+
+            MobileDataStateTracker mdstHipri = (MobileDataStateTracker)
+                    mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI];
+            boolean isHipriProvisioning = mdstHipri.isProvisioningNetwork();
+
+            if (isDefaultProvisioning || isHipriProvisioning) {
+                if (mIsNotificationVisible) {
+                    if (DBG) {
+                        log("checkMobileProvisioning: provisioning-ignore notification is visible");
+                    }
+                } else {
+                    NetworkInfo ni = null;
+                    if (isDefaultProvisioning) {
+                        ni = mdstDefault.getNetworkInfo();
+                    }
+                    if (isHipriProvisioning) {
+                        ni = mdstHipri.getNetworkInfo();
+                    }
+                    String url = getMobileProvisioningUrl();
+                    if ((ni != null) && (!TextUtils.isEmpty(url))) {
+                        setProvNotificationVisible(true, ni.getType(), ni.getExtraInfo(), url);
+                    } else {
+                        if (DBG) log("checkMobileProvisioning: provisioning but no url, ignore");
+                    }
+                }
+                mIsCheckingMobileProvisioning.set(false);
+                return timeOutMs;
+            }
+
             CheckMp checkMp = new CheckMp(mContext, this);
             CheckMp.CallBack cb = new CheckMp.CallBack() {
                 @Override
                 void onComplete(Integer result) {
-                    log("CheckMp.onComplete: result=" + result);
-                    if (resultReceiver != null) {
-                        log("CheckMp.onComplete: send result");
-                        resultReceiver.send(result, null);
-                    }
-                    if (!sendNotification) {
-                        log("CheckMp.onComplete: done, not sending notification");
-                        return;
-                    }
+                    if (DBG) log("CheckMp.onComplete: result=" + result);
                     NetworkInfo ni =
                             mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI].getNetworkInfo();
                     switch(result) {
-                        case ConnectivityManager.CMP_RESULT_CODE_CONNECTABLE:
-                        case ConnectivityManager.CMP_RESULT_CODE_NO_CONNECTION: {
-                            log("CheckMp.onComplete: ignore, connected or no connection");
+                        case CMP_RESULT_CODE_CONNECTABLE:
+                        case CMP_RESULT_CODE_NO_CONNECTION: {
+                            if (DBG) log("CheckMp.onComplete: ignore, connected or no connection");
                             break;
                         }
-                        case ConnectivityManager.CMP_RESULT_CODE_REDIRECTED: {
-                            log("CheckMp.onComplete: warm sim");
+                        case CMP_RESULT_CODE_REDIRECTED: {
+                            if (DBG) log("CheckMp.onComplete: warm sim");
                             String url = getMobileProvisioningUrl();
                             if (TextUtils.isEmpty(url)) {
                                 url = getMobileRedirectedProvisioningUrl();
                             }
                             if (TextUtils.isEmpty(url) == false) {
-                                log("CheckMp.onComplete: warm sim (redirected), url=" + url);
-                                setNotificationVisible(true, ni, url);
+                                if (DBG) log("CheckMp.onComplete: warm (redirected), url=" + url);
+                                setProvNotificationVisible(true, ni.getType(), ni.getExtraInfo(),
+                                        url);
                             } else {
-                                log("CheckMp.onComplete: warm sim (redirected), no url");
+                                if (DBG) log("CheckMp.onComplete: warm (redirected), no url");
                             }
                             break;
                         }
-                        case ConnectivityManager.CMP_RESULT_CODE_NO_DNS:
-                        case ConnectivityManager.CMP_RESULT_CODE_NO_TCP_CONNECTION: {
+                        case CMP_RESULT_CODE_NO_DNS:
+                        case CMP_RESULT_CODE_NO_TCP_CONNECTION: {
                             String url = getMobileProvisioningUrl();
                             if (TextUtils.isEmpty(url) == false) {
-                                log("CheckMp.onComplete: warm sim (no dns/tcp), url=" + url);
-                                setNotificationVisible(true, ni, url);
+                                if (DBG) log("CheckMp.onComplete: warm (no dns/tcp), url=" + url);
+                                setProvNotificationVisible(true, ni.getType(), ni.getExtraInfo(),
+                                        url);
                             } else {
-                                log("CheckMp.onComplete: warm sim (no dns/tcp), no url");
+                                if (DBG) log("CheckMp.onComplete: warm (no dns/tcp), no url");
                             }
                             break;
                         }
@@ -3884,16 +4054,16 @@
                             break;
                         }
                     }
+                    mIsCheckingMobileProvisioning.set(false);
                 }
             };
             CheckMp.Params params =
                     new CheckMp.Params(checkMp.getDefaultUrl(), timeOutMs, cb);
-            log("checkMobileProvisioning: params=" + params);
-            setNotificationVisible(false, null, null);
+            if (DBG) log("checkMobileProvisioning: params=" + params);
             checkMp.execute(params);
         } finally {
             Binder.restoreCallingIdentity(token);
-            log("checkMobileProvisioning: X");
+            if (DBG) log("checkMobileProvisioning: X");
         }
         return timeOutMs;
     }
@@ -3965,26 +4135,38 @@
          * a known address that fetches the data we expect.
          */
         private synchronized Integer isMobileOk(Params params) {
-            Integer result = ConnectivityManager.CMP_RESULT_CODE_NO_CONNECTION;
+            Integer result = CMP_RESULT_CODE_NO_CONNECTION;
             Uri orgUri = Uri.parse(params.mUrl);
             Random rand = new Random();
             mParams = params;
 
             if (mCs.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false) {
                 log("isMobileOk: not mobile capable");
-                result = ConnectivityManager.CMP_RESULT_CODE_NO_CONNECTION;
+                result = CMP_RESULT_CODE_NO_CONNECTION;
                 return result;
             }
 
             try {
-                // Enable fail fast as we'll do retries here and use a
-                // hipri connection so the default connection stays active.
-                log("isMobileOk: start hipri url=" + params.mUrl);
-                mCs.setEnableFailFastMobileData(DctConstants.ENABLED);
-
                 // Continue trying to connect until time has run out
                 long endTime = SystemClock.elapsedRealtime() + params.mTimeOutMs;
 
+                if (!mCs.isMobileDataStateTrackerReady()) {
+                    // Wait for MobileDataStateTracker to be ready.
+                    if (DBG) log("isMobileOk: mdst is not ready");
+                    while(SystemClock.elapsedRealtime() < endTime) {
+                        if (mCs.isMobileDataStateTrackerReady()) {
+                            // Enable fail fast as we'll do retries here and use a
+                            // hipri connection so the default connection stays active.
+                            if (DBG) log("isMobileOk: mdst ready, enable fail fast of mobile data");
+                            mCs.setEnableFailFastMobileData(DctConstants.ENABLED);
+                            break;
+                        }
+                        sleep(1);
+                    }
+                }
+
+                log("isMobileOk: start hipri url=" + params.mUrl);
+
                 // First wait until we can start using hipri
                 Binder binder = new Binder();
                 while(SystemClock.elapsedRealtime() < endTime) {
@@ -3996,7 +4178,7 @@
                             break;
                     }
                     if (VDBG) log("isMobileOk: hipri not started yet");
-                    result = ConnectivityManager.CMP_RESULT_CODE_NO_CONNECTION;
+                    result = CMP_RESULT_CODE_NO_CONNECTION;
                     sleep(1);
                 }
 
@@ -4009,15 +4191,26 @@
                         NetworkInfo.State state = mCs
                                 .getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI).getState();
                         if (state != NetworkInfo.State.CONNECTED) {
-                            if (VDBG) {
+                            if (true/*VDBG*/) {
                                 log("isMobileOk: not connected ni=" +
                                     mCs.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI));
                             }
                             sleep(1);
-                            result = ConnectivityManager.CMP_RESULT_CODE_NO_CONNECTION;
+                            result = CMP_RESULT_CODE_NO_CONNECTION;
                             continue;
                         }
 
+                        // Hipri has started check if this is a provisioning url
+                        MobileDataStateTracker mdst = (MobileDataStateTracker)
+                                mCs.mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI];
+                        if (mdst.isProvisioningNetwork()) {
+                            if (DBG) log("isMobileOk: isProvisioningNetwork is true, no TCP conn");
+                            result = CMP_RESULT_CODE_NO_TCP_CONNECTION;
+                            return result;
+                        } else {
+                            if (DBG) log("isMobileOk: isProvisioningNetwork is false, continue");
+                        }
+
                         // Get of the addresses associated with the url host. We need to use the
                         // address otherwise HttpURLConnection object will use the name to get
                         // the addresses and is will try every address but that will bypass the
@@ -4028,7 +4221,7 @@
                             addresses = InetAddress.getAllByName(orgUri.getHost());
                         } catch (UnknownHostException e) {
                             log("isMobileOk: UnknownHostException");
-                            result = ConnectivityManager.CMP_RESULT_CODE_NO_DNS;
+                            result = CMP_RESULT_CODE_NO_DNS;
                             return result;
                         }
                         log("isMobileOk: addresses=" + inetAddressesToString(addresses));
@@ -4093,9 +4286,9 @@
                                 urlConn.setRequestProperty("Connection", "close");
                                 int responseCode = urlConn.getResponseCode();
                                 if (responseCode == 204) {
-                                    result = ConnectivityManager.CMP_RESULT_CODE_CONNECTABLE;
+                                    result = CMP_RESULT_CODE_CONNECTABLE;
                                 } else {
-                                    result = ConnectivityManager.CMP_RESULT_CODE_REDIRECTED;
+                                    result = CMP_RESULT_CODE_REDIRECTED;
                                 }
                                 log("isMobileOk: connected responseCode=" + responseCode);
                                 urlConn.disconnect();
@@ -4109,7 +4302,7 @@
                                 }
                             }
                         }
-                        result = ConnectivityManager.CMP_RESULT_CODE_NO_TCP_CONNECTION;
+                        result = CMP_RESULT_CODE_NO_TCP_CONNECTION;
                         log("isMobileOk: loops|timed out");
                         return result;
                     } catch (Exception e) {
@@ -4123,6 +4316,23 @@
                 mCs.setEnableFailFastMobileData(DctConstants.DISABLED);
                 mCs.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
                         Phone.FEATURE_ENABLE_HIPRI);
+
+                // Wait for hipri to disconnect.
+                long endTime = SystemClock.elapsedRealtime() + 5000;
+
+                while(SystemClock.elapsedRealtime() < endTime) {
+                    NetworkInfo.State state = mCs
+                            .getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI).getState();
+                    if (state != NetworkInfo.State.DISCONNECTED) {
+                        if (VDBG) {
+                            log("isMobileOk: connected ni=" +
+                                mCs.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI));
+                        }
+                        sleep(1);
+                        continue;
+                    }
+                }
+
                 log("isMobileOk: X result=" + result);
             }
             return result;
@@ -4188,10 +4398,55 @@
         }
     }
 
-    private static final String NOTIFICATION_ID = "CaptivePortal.Notification";
+    // TODO: Move to ConnectivityManager and make public?
+    private static final String CONNECTED_TO_PROVISIONING_NETWORK_ACTION =
+            "com.android.server.connectivityservice.CONNECTED_TO_PROVISIONING_NETWORK_ACTION";
 
-    private void setNotificationVisible(boolean visible, NetworkInfo networkInfo, String url) {
-        log("setNotificationVisible: E visible=" + visible + " ni=" + networkInfo + " url=" + url);
+    private BroadcastReceiver mProvisioningReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction().equals(CONNECTED_TO_PROVISIONING_NETWORK_ACTION)) {
+                handleMobileProvisioningAction(intent.getStringExtra("EXTRA_URL"));
+            }
+        }
+    };
+
+    private void handleMobileProvisioningAction(String url) {
+        // Notication mark notification as not visible
+        setProvNotificationVisible(false, ConnectivityManager.TYPE_NONE, null, null);
+
+        // If provisioning network handle as a special case,
+        // otherwise launch browser with the intent directly.
+        NetworkInfo ni = getProvisioningNetworkInfo();
+        if ((ni != null) && ni.getDetailedState() ==
+                    NetworkInfo.DetailedState.CONNECTED_TO_PROVISIONING_NETWORK) {
+            if (DBG) log("handleMobileProvisioningAction: on provisioning network");
+            MobileDataStateTracker mdst = (MobileDataStateTracker)
+                    mNetTrackers[ConnectivityManager.TYPE_MOBILE];
+            mdst.enableMobileProvisioning(url);
+        } else {
+            if (DBG) log("handleMobileProvisioningAction: on default network");
+            Intent newIntent =
+                    new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+            newIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
+                    Intent.FLAG_ACTIVITY_NEW_TASK);
+            try {
+                mContext.startActivity(newIntent);
+            } catch (ActivityNotFoundException e) {
+                loge("handleMobileProvisioningAction: startActivity failed" + e);
+            }
+        }
+    }
+
+    private static final String NOTIFICATION_ID = "CaptivePortal.Notification";
+    private volatile boolean mIsNotificationVisible = false;
+
+    private void setProvNotificationVisible(boolean visible, int networkType, String extraInfo,
+            String url) {
+        if (DBG) {
+            log("setProvNotificationVisible: E visible=" + visible + " networkType=" + networkType
+                + " extraInfo=" + extraInfo + " url=" + url);
+        }
 
         Resources r = Resources.getSystem();
         NotificationManager notificationManager = (NotificationManager) mContext
@@ -4201,50 +4456,64 @@
             CharSequence title;
             CharSequence details;
             int icon;
-            switch (networkInfo.getType()) {
+            Intent intent;
+            Notification notification = new Notification();
+            switch (networkType) {
                 case ConnectivityManager.TYPE_WIFI:
-                    log("setNotificationVisible: TYPE_WIFI");
                     title = r.getString(R.string.wifi_available_sign_in, 0);
                     details = r.getString(R.string.network_available_sign_in_detailed,
-                            networkInfo.getExtraInfo());
+                            extraInfo);
                     icon = R.drawable.stat_notify_wifi_in_range;
+                    intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+                    intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
+                            Intent.FLAG_ACTIVITY_NEW_TASK);
+                    notification.contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
                     break;
                 case ConnectivityManager.TYPE_MOBILE:
                 case ConnectivityManager.TYPE_MOBILE_HIPRI:
-                    log("setNotificationVisible: TYPE_MOBILE|HIPRI");
                     title = r.getString(R.string.network_available_sign_in, 0);
                     // TODO: Change this to pull from NetworkInfo once a printable
                     // name has been added to it
                     details = mTelephonyManager.getNetworkOperatorName();
                     icon = R.drawable.stat_notify_rssi_in_range;
+                    intent = new Intent(CONNECTED_TO_PROVISIONING_NETWORK_ACTION);
+                    intent.putExtra("EXTRA_URL", url);
+                    intent.setFlags(0);
+                    notification.contentIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
                     break;
                 default:
-                    log("setNotificationVisible: other type=" + networkInfo.getType());
                     title = r.getString(R.string.network_available_sign_in, 0);
                     details = r.getString(R.string.network_available_sign_in_detailed,
-                            networkInfo.getExtraInfo());
+                            extraInfo);
                     icon = R.drawable.stat_notify_rssi_in_range;
+                    intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+                    intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
+                            Intent.FLAG_ACTIVITY_NEW_TASK);
+                    notification.contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
                     break;
             }
 
-            Notification notification = new Notification();
             notification.when = 0;
             notification.icon = icon;
             notification.flags = Notification.FLAG_AUTO_CANCEL;
-            Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
-            intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
-                    Intent.FLAG_ACTIVITY_NEW_TASK);
-            notification.contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
             notification.tickerText = title;
             notification.setLatestEventInfo(mContext, title, details, notification.contentIntent);
 
-            log("setNotificaitionVisible: notify notificaiton=" + notification);
-            notificationManager.notify(NOTIFICATION_ID, 1, notification);
+            try {
+                notificationManager.notify(NOTIFICATION_ID, 1, notification);
+            } catch (NullPointerException npe) {
+                loge("setNotificaitionVisible: visible notificationManager npe=" + npe);
+                npe.printStackTrace();
+            }
         } else {
-            log("setNotificaitionVisible: cancel");
-            notificationManager.cancel(NOTIFICATION_ID, 1);
+            try {
+                notificationManager.cancel(NOTIFICATION_ID, 1);
+            } catch (NullPointerException npe) {
+                loge("setNotificaitionVisible: cancel notificationManager npe=" + npe);
+                npe.printStackTrace();
+            }
         }
-        log("setNotificationVisible: X visible=" + visible + " ni=" + networkInfo + " url=" + url);
+        mIsNotificationVisible = visible;
     }
 
     /** Location to an updatable file listing carrier provisioning urls.
@@ -4373,6 +4642,13 @@
         return url;
     }
 
+    @Override
+    public void setProvisioningNotificationVisible(boolean visible, int networkType,
+            String extraInfo, String url) {
+        enforceConnectivityInternalPermission();
+        setProvNotificationVisible(visible, networkType, extraInfo, url);
+    }
+
     private void onUserStart(int userId) {
         synchronized(mVpns) {
             Vpn userVpn = mVpns.get(userId);
diff --git a/services/java/com/android/server/PreferredComponent.java b/services/java/com/android/server/PreferredComponent.java
index bb22545..134b198 100644
--- a/services/java/com/android/server/PreferredComponent.java
+++ b/services/java/com/android/server/PreferredComponent.java
@@ -33,8 +33,16 @@
 import java.util.List;
 
 public class PreferredComponent {
+    private static final String TAG_SET = "set";
+    private static final String ATTR_ALWAYS = "always"; // boolean
+    private static final String ATTR_MATCH = "match"; // number
+    private static final String ATTR_NAME = "name"; // component name
+    private static final String ATTR_SET = "set"; // number
+
     public final int mMatch;
     public final ComponentName mComponent;
+    // Whether this is to be the one that's always chosen. If false, it's the most recently chosen.
+    public boolean mAlways;
 
     private final String[] mSetPackages;
     private final String[] mSetClasses;
@@ -50,10 +58,11 @@
     }
 
     public PreferredComponent(Callbacks callbacks, int match, ComponentName[] set,
-            ComponentName component) {
+            ComponentName component, boolean always) {
         mCallbacks = callbacks;
         mMatch = match&IntentFilter.MATCH_CATEGORY_MASK;
         mComponent = component;
+        mAlways = always;
         mShortComponent = component.flattenToShortString();
         mParseError = null;
         if (set != null) {
@@ -86,15 +95,17 @@
     public PreferredComponent(Callbacks callbacks, XmlPullParser parser)
             throws XmlPullParserException, IOException {
         mCallbacks = callbacks;
-        mShortComponent = parser.getAttributeValue(null, "name");
+        mShortComponent = parser.getAttributeValue(null, ATTR_NAME);
         mComponent = ComponentName.unflattenFromString(mShortComponent);
         if (mComponent == null) {
             mParseError = "Bad activity name " + mShortComponent;
         }
-        String matchStr = parser.getAttributeValue(null, "match");
+        String matchStr = parser.getAttributeValue(null, ATTR_MATCH);
         mMatch = matchStr != null ? Integer.parseInt(matchStr, 16) : 0;
-        String setCountStr = parser.getAttributeValue(null, "set");
+        String setCountStr = parser.getAttributeValue(null, ATTR_SET);
         int setCount = setCountStr != null ? Integer.parseInt(setCountStr) : 0;
+        String alwaysStr = parser.getAttributeValue(null, ATTR_ALWAYS);
+        mAlways = alwaysStr != null ? Boolean.parseBoolean(alwaysStr) : true;
 
         String[] myPackages = setCount > 0 ? new String[setCount] : null;
         String[] myClasses = setCount > 0 ? new String[setCount] : null;
@@ -115,8 +126,8 @@
             String tagName = parser.getName();
             //Log.i(TAG, "Parse outerDepth=" + outerDepth + " depth="
             //        + parser.getDepth() + " tag=" + tagName);
-            if (tagName.equals("set")) {
-                String name = parser.getAttributeValue(null, "name");
+            if (tagName.equals(TAG_SET)) {
+                String name = parser.getAttributeValue(null, ATTR_NAME);
                 if (name == null) {
                     if (mParseError == null) {
                         mParseError = "No name in set tag in preferred activity "
@@ -166,16 +177,17 @@
 
     public void writeToXml(XmlSerializer serializer, boolean full) throws IOException {
         final int NS = mSetClasses != null ? mSetClasses.length : 0;
-        serializer.attribute(null, "name", mShortComponent);
+        serializer.attribute(null, ATTR_NAME, mShortComponent);
         if (full) {
             if (mMatch != 0) {
-                serializer.attribute(null, "match", Integer.toHexString(mMatch));
+                serializer.attribute(null, ATTR_MATCH, Integer.toHexString(mMatch));
             }
-            serializer.attribute(null, "set", Integer.toString(NS));
+            serializer.attribute(null, ATTR_ALWAYS, Boolean.toString(mAlways));
+            serializer.attribute(null, ATTR_SET, Integer.toString(NS));
             for (int s=0; s<NS; s++) {
-                serializer.startTag(null, "set");
-                serializer.attribute(null, "name", mSetComponents[s]);
-                serializer.endTag(null, "set");
+                serializer.startTag(null, TAG_SET);
+                serializer.attribute(null, ATTR_NAME, mSetComponents[s]);
+                serializer.endTag(null, TAG_SET);
             }
         }
     }
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 4942141..686b64e 100755
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -122,6 +122,7 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.LogPrinter;
+import android.util.PrintStreamPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.Xml;
@@ -2595,6 +2596,37 @@
         return chooseBestActivity(intent, resolvedType, flags, query, userId);
     }
 
+    @Override
+    public void setLastChosenActivity(Intent intent, String resolvedType, int flags,
+            IntentFilter filter, int match, ComponentName activity) {
+        final int userId = UserHandle.getCallingUserId();
+        if (DEBUG_PREFERRED) {
+            Log.v(TAG, "setLastChosenActivity intent=" + intent
+                + " resolvedType=" + resolvedType
+                + " flags=" + flags
+                + " filter=" + filter
+                + " match=" + match
+                + " activity=" + activity);
+            filter.dump(new PrintStreamPrinter(System.out), "    ");
+        }
+        intent.setComponent(null);
+        List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags, userId);
+        // Find any earlier preferred or last chosen entries and nuke them
+        findPreferredActivity(intent, resolvedType,
+                flags, query, 0, false, true, userId);
+        // Add the new activity as the last chosen for this filter
+        addPreferredActivityInternal(filter, match, null, activity, false, userId);
+    }
+
+    @Override
+    public ResolveInfo getLastChosenActivity(Intent intent, String resolvedType, int flags) {
+        final int userId = UserHandle.getCallingUserId();
+        if (DEBUG_PREFERRED) Log.v(TAG, "Querying last chosen activity for " + intent);
+        List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags, userId);
+        return findPreferredActivity(intent, resolvedType, flags, query, 0,
+                false, false, userId);
+    }
+
     private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,
             int flags, List<ResolveInfo> query, int userId) {
         if (query != null) {
@@ -2620,7 +2652,7 @@
                 // If we have saved a preference for a preferred activity for
                 // this Intent, use that.
                 ResolveInfo ri = findPreferredActivity(intent, resolvedType,
-                        flags, query, r0.priority, userId);
+                        flags, query, r0.priority, true, false, userId);
                 if (ri != null) {
                     return ri;
                 }
@@ -2639,16 +2671,18 @@
         return null;
     }
 
-    ResolveInfo findPreferredActivity(Intent intent, String resolvedType,
-            int flags, List<ResolveInfo> query, int priority, int userId) {
+    ResolveInfo findPreferredActivity(Intent intent, String resolvedType, int flags,
+            List<ResolveInfo> query, int priority, boolean always,
+            boolean removeMatches, int userId) {
         if (!sUserManager.exists(userId)) return null;
         // writer
         synchronized (mPackages) {
             if (intent.getSelector() != null) {
-                intent = intent.getSelector(); 
+                intent = intent.getSelector();
             }
             if (DEBUG_PREFERRED) intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
             PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId);
+            // Get the list of preferred activities that handle the intent
             List<PreferredActivity> prefs = pir != null
                     ? pir.queryIntent(intent, resolvedType,
                             (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId)
@@ -2683,7 +2717,25 @@
                 final int M = prefs.size();
                 for (int i=0; i<M; i++) {
                     final PreferredActivity pa = prefs.get(i);
+                    if (DEBUG_PREFERRED) {
+                        Log.v(TAG, "Checking PreferredActivity ds="
+                                + (pa.countDataSchemes() > 0 ? pa.getDataScheme(0) : "<none>")
+                                + "\n  component=" + pa.mPref.mComponent);
+                        pa.dump(new PrintStreamPrinter(System.out), "  ");
+                    }
                     if (pa.mPref.mMatch != match) {
+                        if (DEBUG_PREFERRED) {
+                            Log.v(TAG, "Skipping bad match "
+                                    + Integer.toHexString(pa.mPref.mMatch));
+                        }
+                        continue;
+                    }
+                    // If it's not an "always" type preferred activity and that's what we're
+                    // looking for, skip it.
+                    if (always && !pa.mPref.mAlways) {
+                        if (DEBUG_PREFERRED) {
+                            Log.v(TAG, "Skipping lastChosen entry");
+                        }
                         continue;
                     }
                     final ActivityInfo ai = getActivityInfo(pa.mPref.mComponent,
@@ -2717,22 +2769,41 @@
                             continue;
                         }
 
-                        // Okay we found a previously set preferred app.
+                        if (removeMatches) {
+                            pir.removeFilter(pa);
+                            if (DEBUG_PREFERRED) {
+                                Log.v(TAG, "Removing match " + pa.mPref.mComponent);
+                            }
+                            break;
+                        }
+
+                        // Okay we found a previously set preferred or last chosen app.
                         // If the result set is different from when this
                         // was created, we need to clear it and re-ask the
-                        // user their preference.
-                        if (!pa.mPref.sameSet(query, priority)) {
+                        // user their preference, if we're looking for an "always" type entry.
+                        if (always && !pa.mPref.sameSet(query, priority)) {
                             Slog.i(TAG, "Result set changed, dropping preferred activity for "
                                     + intent + " type " + resolvedType);
+                            if (DEBUG_PREFERRED) {
+                                Log.v(TAG, "Removing preferred activity since set changed "
+                                        + pa.mPref.mComponent);
+                            }
                             pir.removeFilter(pa);
+                            // Re-add the filter as a "last chosen" entry (!always)
+                            PreferredActivity lastChosen = new PreferredActivity(
+                                    pa, pa.mPref.mMatch, null, pa.mPref.mComponent, false);
+                            pir.addFilter(lastChosen);
+                            mSettings.writePackageRestrictionsLPr(userId);
                             return null;
                         }
 
-                        // Yay!
+                        // Yay! Either the set matched or we're looking for the last chosen
+                        mSettings.writePackageRestrictionsLPr(userId);
                         return ri;
                     }
                 }
             }
+            mSettings.writePackageRestrictionsLPr(userId);
         }
         return null;
     }
@@ -9606,9 +9677,14 @@
         }
         return Build.VERSION_CODES.CUR_DEVELOPMENT;
     }
-    
+
     public void addPreferredActivity(IntentFilter filter, int match,
             ComponentName[] set, ComponentName activity, int userId) {
+        addPreferredActivityInternal(filter, match, set, activity, true, userId);
+    }
+
+    private void addPreferredActivityInternal(IntentFilter filter, int match,
+            ComponentName[] set, ComponentName activity, boolean always, int userId) {
         // writer
         int callingUid = Binder.getCallingUid();
         enforceCrossUserPermission(callingUid, userId, true, "add preferred activity");
@@ -9629,7 +9705,7 @@
             Slog.i(TAG, "Adding preferred activity " + activity + " for user " + userId + " :");
             filter.dump(new LogPrinter(Log.INFO, TAG), "  ");
             mSettings.editPreferredActivitiesLPw(userId).addFilter(
-                    new PreferredActivity(filter, match, set, activity));
+                    new PreferredActivity(filter, match, set, activity, always));
             mSettings.writePackageRestrictionsLPr(userId);
         }
     }
@@ -9691,7 +9767,7 @@
                     }
                 }
             }
-            addPreferredActivity(filter, match, set, activity, callingUserId);
+            addPreferredActivityInternal(filter, match, set, activity, true, callingUserId);
         }
     }
 
@@ -9736,8 +9812,11 @@
             Iterator<PreferredActivity> it = pir.filterIterator();
             while (it.hasNext()) {
                 PreferredActivity pa = it.next();
+                // Mark entry for removal only if it matches the package name
+                // and the entry is of type "always".
                 if (packageName == null ||
-                        pa.mPref.mComponent.getPackageName().equals(packageName)) {
+                        (pa.mPref.mComponent.getPackageName().equals(packageName)
+                                && pa.mPref.mAlways)) {
                     if (removed == null) {
                         removed = new ArrayList<PreferredActivity>();
                     }
@@ -9781,7 +9860,8 @@
                 while (it.hasNext()) {
                     final PreferredActivity pa = it.next();
                     if (packageName == null
-                            || pa.mPref.mComponent.getPackageName().equals(packageName)) {
+                            || (pa.mPref.mComponent.getPackageName().equals(packageName)
+                                    && pa.mPref.mAlways)) {
                         if (outFilters != null) {
                             outFilters.add(new IntentFilter(pa));
                         }
diff --git a/services/java/com/android/server/pm/PreferredActivity.java b/services/java/com/android/server/pm/PreferredActivity.java
index c655bb1..963cbe4 100644
--- a/services/java/com/android/server/pm/PreferredActivity.java
+++ b/services/java/com/android/server/pm/PreferredActivity.java
@@ -33,13 +33,13 @@
     private static final String TAG = "PreferredActivity";
 
     private static final boolean DEBUG_FILTERS = false;
-    static final String ATTR_USER_ID = "userId";
 
     final PreferredComponent mPref;
 
-    PreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity) {
+    PreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity,
+            boolean always) {
         super(filter);
-        mPref = new PreferredComponent(this, match, set, activity);
+        mPref = new PreferredComponent(this, match, set, activity, always);
     }
 
     PreferredActivity(XmlPullParser parser) throws XmlPullParserException, IOException {
diff --git a/services/java/com/android/server/pm/PreferredIntentResolver.java b/services/java/com/android/server/pm/PreferredIntentResolver.java
index 7fe6a05..bce24d7 100644
--- a/services/java/com/android/server/pm/PreferredIntentResolver.java
+++ b/services/java/com/android/server/pm/PreferredIntentResolver.java
@@ -26,10 +26,12 @@
     protected PreferredActivity[] newArray(int size) {
         return new PreferredActivity[size];
     }
+
     @Override
     protected boolean isPackageForFilter(String packageName, PreferredActivity filter) {
         return packageName.equals(filter.mPref.mComponent.getPackageName());
     }
+
     @Override
     protected void dumpFilter(PrintWriter out, String prefix,
             PreferredActivity filter) {
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index e18202b..ff1128d 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -2071,7 +2071,7 @@
                 if (path != null) {
                     filter.addDataPath(path);
                 }
-                PreferredActivity pa = new PreferredActivity(filter, match, set, cn);
+                PreferredActivity pa = new PreferredActivity(filter, match, set, cn, true);
                 editPreferredActivitiesLPw(userId).addFilter(pa);
             } else if (!haveNonSys) {
                 Slog.w(TAG, "No component found for default preferred activity " + cn);
diff --git a/services/java/com/android/server/print/PrintManagerService.java b/services/java/com/android/server/print/PrintManagerService.java
index 671a5dc..2563b58 100644
--- a/services/java/com/android/server/print/PrintManagerService.java
+++ b/services/java/com/android/server/print/PrintManagerService.java
@@ -254,7 +254,7 @@
     }
 
     @Override
-    public void requestPrinterUpdate(PrinterId printerId, int userId) {
+    public void validatePrinters(List<PrinterId> printerIds, int userId) {
         final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
         final UserState userState;
         synchronized (mLock) {
@@ -262,7 +262,37 @@
         }
         final long identity = Binder.clearCallingIdentity();
         try {
-            userState.requestPrinterUpdate(printerId);
+            userState.validatePrinters(printerIds);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public void startPrinterStateTracking(PrinterId printerId, int userId) {
+        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+        final UserState userState;
+        synchronized (mLock) {
+            userState = getOrCreateUserStateLocked(resolvedUserId);
+        }
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            userState.startPrinterStateTracking(printerId);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public void stopPrinterStateTracking(PrinterId printerId, int userId) {
+        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+        final UserState userState;
+        synchronized (mLock) {
+            userState = getOrCreateUserStateLocked(resolvedUserId);
+        }
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            userState.stopPrinterStateTracking(printerId);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -432,10 +462,12 @@
         if (appId == callingAppId) {
             return appId;
         }
-        if (mContext.checkCallingPermission(Manifest.permission.ACCESS_ALL_PRINT_JOBS)
+        if (mContext.checkCallingPermission(
+                "com.android.printspooler.permission.ACCESS_ALL_PRINT_JOBS")
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Call from app " + callingAppId + " as app "
-                    + appId + " without permission ACCESS_ALL_PRINT_JOBS");
+                    + appId + " without com.android.printspooler.permission"
+                    + ".ACCESS_ALL_PRINT_JOBS");
         }
         return appId;
     }
diff --git a/services/java/com/android/server/print/RemotePrintService.java b/services/java/com/android/server/print/RemotePrintService.java
index 7f4b343..14af9d8 100644
--- a/services/java/com/android/server/print/RemotePrintService.java
+++ b/services/java/com/android/server/print/RemotePrintService.java
@@ -25,6 +25,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
+import android.os.AsyncTask;
 import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
@@ -38,6 +39,8 @@
 import android.printservice.IPrintServiceClient;
 import android.util.Slog;
 
+import com.android.internal.R;
+
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
@@ -121,6 +124,36 @@
         mHasPrinterDiscoverySession = false;
         mPendingCommands.clear();
         ensureUnbound();
+
+        // Makes sure all active print jobs are failed since the service
+        // just died. Do this off the main thread since we do to allow
+        // calls into the spooler on the main thread.
+        new AsyncTask<Void, Void, Void>() {
+            @Override
+            protected Void doInBackground(Void... params) {
+                failAllActivePrintJobs();
+                return null;
+            }
+        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
+    }
+
+    private void failAllActivePrintJobs() {
+        List<PrintJobInfo> printJobs = mSpooler.getPrintJobInfos(mComponentName,
+                PrintJobInfo.STATE_ANY_ACTIVE, PrintManager.APP_ID_ANY);
+        if (printJobs == null) {
+            return;
+        }
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            final int printJobCount = printJobs.size();
+            for (int i = 0; i < printJobCount; i++) {
+                PrintJobInfo printJob = printJobs.get(i);
+                mSpooler.setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED,
+                        mContext.getString(R.string.reason_unknown));
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     private void handleOnAllPrintJobsHandled() {
@@ -308,29 +341,83 @@
         }
     }
 
-    public void requestPrinterUpdate(PrinterId printerId) {
-        mHandler.obtainMessage(MyHandler.MSG_REQUEST_PRINTER_UPDATE,
-                printerId).sendToTarget();
+    public void validatePrinters(List<PrinterId> printerIds) {
+        mHandler.obtainMessage(MyHandler.MSG_VALIDATE_PRINTERS,
+                printerIds).sendToTarget();
     }
 
-    private void handleRequestPrinterUpdate(final PrinterId printerId) {
+    private void handleValidatePrinters(final List<PrinterId> printerIds) {
         throwIfDestroyed();
         if (!isBound()) {
             ensureBound();
             mPendingCommands.add(new Runnable() {
                 @Override
                 public void run() {
-                    handleRequestPrinterUpdate(printerId);
+                    handleValidatePrinters(printerIds);
                 }
             });
         } else {
             if (DEBUG) {
-                Slog.i(LOG_TAG, "[user: " + mUserId + "] requestPrinterUpdate()");
+                Slog.i(LOG_TAG, "[user: " + mUserId + "] handleValidatePrinters()");
             }
             try {
-                mPrintService.requestPrinterUpdate(printerId);
+                mPrintService.validatePrinters(printerIds);
             } catch (RemoteException re) {
-                Slog.e(LOG_TAG, "Error requesting a printer update.", re);
+                Slog.e(LOG_TAG, "Error requesting printers validation.", re);
+            }
+        }
+    }
+
+    public void startPrinterStateTracking(PrinterId printerId) {
+        mHandler.obtainMessage(MyHandler.MSG_START_PRINTER_STATE_TRACKING,
+                printerId).sendToTarget();
+    }
+
+    private void handleStartPrinterStateTracking(final PrinterId printerId) {
+        throwIfDestroyed();
+        if (!isBound()) {
+            ensureBound();
+            mPendingCommands.add(new Runnable() {
+                @Override
+                public void run() {
+                    handleStartPrinterStateTracking(printerId);
+                }
+            });
+        } else {
+            if (DEBUG) {
+                Slog.i(LOG_TAG, "[user: " + mUserId + "] handleStartPrinterTracking()");
+            }
+            try {
+                mPrintService.startPrinterStateTracking(printerId);
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Error requesting start printer tracking.", re);
+            }
+        }
+    }
+
+    public void stopPrinterStateTracking(PrinterId printerId) {
+        mHandler.obtainMessage(MyHandler.MSG_STOP_PRINTER_STATE_TRACKING,
+                printerId).sendToTarget();
+    }
+
+    private void handleStopPrinterStateTracking(final PrinterId printerId) {
+        throwIfDestroyed();
+        if (!isBound()) {
+            ensureBound();
+            mPendingCommands.add(new Runnable() {
+                @Override
+                public void run() {
+                    handleStopPrinterStateTracking(printerId);
+                }
+            });
+        } else {
+            if (DEBUG) {
+                Slog.i(LOG_TAG, "[user: " + mUserId + "] handleStopPrinterTracking()");
+            }
+            try {
+                mPrintService.stopPrinterStateTracking(printerId);
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Error requesting stop printer tracking.", re);
             }
         }
     }
@@ -417,12 +504,14 @@
         public static final int MSG_DESTROY_PRINTER_DISCOVERY_SESSION = 2;
         public static final int MSG_START_PRINTER_DISCOVERY = 3;
         public static final int MSG_STOP_PRINTER_DISCOVERY = 4;
-        public static final int MSG_REQUEST_PRINTER_UPDATE = 5;
-        public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 6;
-        public static final int MSG_ON_REQUEST_CANCEL_PRINT_JOB = 7;
-        public static final int MSG_ON_PRINT_JOB_QUEUED = 8;
-        public static final int MSG_DESTROY = 9;
-        public static final int MSG_BINDER_DIED = 10;
+        public static final int MSG_VALIDATE_PRINTERS = 5;
+        public static final int MSG_START_PRINTER_STATE_TRACKING = 6;
+        public static final int MSG_STOP_PRINTER_STATE_TRACKING = 7;
+        public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 8;
+        public static final int MSG_ON_REQUEST_CANCEL_PRINT_JOB = 9;
+        public static final int MSG_ON_PRINT_JOB_QUEUED = 10;
+        public static final int MSG_DESTROY = 11;
+        public static final int MSG_BINDER_DIED = 12;
 
         public MyHandler(Looper looper) {
             super(looper, null, false);
@@ -449,9 +538,19 @@
                     handleStopPrinterDiscovery();
                 } break;
 
-                case MSG_REQUEST_PRINTER_UPDATE: {
+                case MSG_VALIDATE_PRINTERS: {
+                    List<PrinterId> printerIds = (List<PrinterId>) message.obj;
+                    handleValidatePrinters(printerIds);
+                } break;
+
+                case MSG_START_PRINTER_STATE_TRACKING: {
                     PrinterId printerId = (PrinterId) message.obj;
-                    handleRequestPrinterUpdate(printerId);
+                    handleStartPrinterStateTracking(printerId);
+                } break;
+
+                case MSG_STOP_PRINTER_STATE_TRACKING: {
+                    PrinterId printerId = (PrinterId) message.obj;
+                    handleStopPrinterStateTracking(printerId);
                 } break;
 
                 case MSG_ON_ALL_PRINT_JOBS_HANDLED: {
diff --git a/services/java/com/android/server/print/UserState.java b/services/java/com/android/server/print/UserState.java
index c979a11..4a1b96b 100644
--- a/services/java/com/android/server/print/UserState.java
+++ b/services/java/com/android/server/print/UserState.java
@@ -19,8 +19,12 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
@@ -46,6 +50,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -63,6 +68,11 @@
 
     private static final char COMPONENT_NAME_SEPARATOR = ':';
 
+    private static final String SHARED_PREFERENCES_FILE = "shared_prefs";
+
+    private static final String KEY_SYSTEM_PRINT_SERVICES_ENABLED =
+            "KEY_SYSTEM_PRINT_SERVICES_ENABLED";
+
     private final SimpleStringSplitter mStringColonSplitter =
             new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
 
@@ -95,6 +105,7 @@
         mUserId = userId;
         mLock = lock;
         mSpooler = new RemotePrintSpooler(context, userId, this);
+        enableSystemPrintServicesOnce();
     }
 
     @Override
@@ -190,7 +201,7 @@
         }
     }
 
-    public void requestPrinterUpdate(PrinterId printerId) {
+    public void validatePrinters(List<PrinterId> printerIds) {
         synchronized (mLock) {
             throwIfDestroyedLocked();
             // No services - nothing to do.
@@ -202,7 +213,39 @@
                 return;
             }
             // Request an updated.
-            mPrinterDiscoverySession.requestPrinterUpdateLocked(printerId);
+            mPrinterDiscoverySession.validatePrintersLocked(printerIds);
+        }
+    }
+
+    public void startPrinterStateTracking(PrinterId printerId) {
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+            // No services - nothing to do.
+            if (mActiveServices.isEmpty()) {
+                return;
+            }
+            // No session - nothing to do.
+            if (mPrinterDiscoverySession == null) {
+                return;
+            }
+            // Request start tracking the printer.
+            mPrinterDiscoverySession.startPrinterStateTrackingLocked(printerId);
+        }
+    }
+
+    public void stopPrinterStateTracking(PrinterId printerId) {
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+            // No services - nothing to do.
+            if (mActiveServices.isEmpty()) {
+                return;
+            }
+            // No session - nothing to do.
+            if (mPrinterDiscoverySession == null) {
+                return;
+            }
+            // Request stop tracking the printer.
+            mPrinterDiscoverySession.stopPrinterStateTrackingLocked(printerId);
         }
     }
 
@@ -365,6 +408,36 @@
         return false;
     }
 
+    private void enableSystemPrintServicesOnce() {
+        SharedPreferences preferences = mContext.getSharedPreferences(
+                SHARED_PREFERENCES_FILE, Context.MODE_PRIVATE);
+        if (preferences.getInt(KEY_SYSTEM_PRINT_SERVICES_ENABLED, 0) == 0) {
+            Editor editor = preferences.edit();
+            editor.putInt(KEY_SYSTEM_PRINT_SERVICES_ENABLED, 1);
+            editor.commit();
+
+            readInstalledPrintServicesLocked();
+
+            StringBuilder builder = new StringBuilder();
+
+            final int serviceCount = mInstalledServices.size();
+            for (int i = 0; i < serviceCount; i++) {
+                ServiceInfo serviceInfo = mInstalledServices.get(i).getResolveInfo().serviceInfo;
+                if ((serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                    ComponentName serviceName = new ComponentName(
+                            serviceInfo.packageName, serviceInfo.name);
+                    if (builder.length() > 0) {
+                        builder.append(":");
+                    }
+                    builder.append(serviceName.flattenToString());
+                }
+            }
+
+            Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                    Settings.Secure.ENABLED_PRINT_SERVICES, builder.toString(), mUserId);
+        }
+    }
+
     private void onConfigurationChangedLocked() {
         final int installedCount = mInstalledServices.size();
         for (int i = 0; i < installedCount; i++) {
@@ -415,6 +488,8 @@
 
         private final List<IBinder> mStartedPrinterDiscoveryTokens = new ArrayList<IBinder>();
 
+        private final List<PrinterId> mStateTrackedPrinters = new ArrayList<PrinterId>();
+
         private final Handler mHandler;
 
         private boolean mIsDestroyed;
@@ -461,14 +536,10 @@
             }
 
             // If printer discovery is ongoing and the start request has a list
-            // of printer to be checked, then we just request refreshing each of
-            // them rather making another start discovery request.
+            // of printer to be checked, then we just request validating them.
             if (!mStartedPrinterDiscoveryTokens.isEmpty()
                     && priorityList != null && !priorityList.isEmpty()) {
-                final int priorityIdCount = priorityList.size();
-                for (int i = 0; i < priorityIdCount; i++) {
-                    requestPrinterUpdate(priorityList.get(i));
-                }
+                validatePrinters(priorityList);
                 return;
             }
 
@@ -508,22 +579,99 @@
                     .sendToTarget();
         }
 
-        public void requestPrinterUpdateLocked(PrinterId printerId) {
+        public void validatePrintersLocked(List<PrinterId> printerIds) {
             if (mIsDestroyed) {
-                Log.w(LOG_TAG, "Not updating pritner - session destroyed");
+                Log.w(LOG_TAG, "Not validating pritners - session destroyed");
                 return;
             }
-            RemotePrintService service = mActiveServices.get(printerId.getServiceName());
-            if (service != null) {
-                SomeArgs args = SomeArgs.obtain();
-                args.arg1 = service;
-                args.arg2 = printerId;
-                mHandler.obtainMessage(SessionHandler
-                        .MSG_REQUEST_PRINTER_UPDATE, args)
-                        .sendToTarget();
+
+            List<PrinterId> remainingList = new ArrayList<PrinterId>(printerIds);
+            while (!remainingList.isEmpty()) {
+                Iterator<PrinterId> iterator = remainingList.iterator();
+                // Gather the printers per service and request a validation.
+                List<PrinterId> updateList = new ArrayList<PrinterId>();
+                ComponentName serviceName = null;
+                while (iterator.hasNext()) {
+                    PrinterId printerId = iterator.next();
+                    if (updateList.isEmpty()) {
+                        updateList.add(printerId);
+                        serviceName = printerId.getServiceName();
+                        iterator.remove();
+                    } else if (printerId.getServiceName().equals(serviceName)) {
+                        updateList.add(printerId);
+                        iterator.remove();
+                    }
+                }
+                // Schedule a notification of the service.
+                RemotePrintService service = mActiveServices.get(serviceName);
+                if (service != null) {
+                    SomeArgs args = SomeArgs.obtain();
+                    args.arg1 = service;
+                    args.arg2 = updateList;
+                    mHandler.obtainMessage(SessionHandler
+                            .MSG_VALIDATE_PRINTERS, args)
+                            .sendToTarget();
+                }
             }
         }
 
+        public final void startPrinterStateTrackingLocked(PrinterId printerId) {
+            if (mIsDestroyed) {
+                Log.w(LOG_TAG, "Not starting printer state tracking - session destroyed");
+                return;
+            }
+            // If printer discovery is not started - nothing to do.
+            if (mStartedPrinterDiscoveryTokens.isEmpty()) {
+                return;
+            }
+            final boolean containedPrinterId = mStateTrackedPrinters.contains(printerId);
+            // Keep track of the number of requests to track this one.
+            mStateTrackedPrinters.add(printerId);
+            // If we were tracking this printer - nothing to do.
+            if (containedPrinterId) {
+                return;
+            }
+            // No service - nothing to do.
+            RemotePrintService service = mActiveServices.get(printerId.getServiceName());
+            if (service == null) {
+                return;
+            }
+            // Ask the service to start tracking.
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = service;
+            args.arg2 = printerId;
+            mHandler.obtainMessage(SessionHandler
+                    .MSG_START_PRINTER_STATE_TRACKING, args)
+                    .sendToTarget();
+        }
+
+        public final void stopPrinterStateTrackingLocked(PrinterId printerId) {
+            if (mIsDestroyed) {
+                Log.w(LOG_TAG, "Not stopping printer state tracking - session destroyed");
+                return;
+            }
+            // If printer discovery is not started - nothing to do.
+            if (mStartedPrinterDiscoveryTokens.isEmpty()) {
+                return;
+            }
+            // If we did not track this printer - nothing to do.
+            if (!mStateTrackedPrinters.remove(printerId)) {
+                return;
+            }
+            // No service - nothing to do.
+            RemotePrintService service = mActiveServices.get(printerId.getServiceName());
+            if (service == null) {
+                return;
+            }
+            // Ask the service to start tracking.
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = service;
+            args.arg2 = printerId;
+            mHandler.obtainMessage(SessionHandler
+                    .MSG_STOP_PRINTER_STATE_TRACKING, args)
+                    .sendToTarget();
+        }
+
         public void onDestroyed() {
             /* do nothing */
         }
@@ -533,6 +681,12 @@
                 Log.w(LOG_TAG, "Not destroying - session destroyed");
                 return;
             }
+            // Make sure printer tracking is stopped.
+            final int printerCount = mStateTrackedPrinters.size();
+            for (int i = 0; i < printerCount; i++) {
+                PrinterId printerId = mStateTrackedPrinters.get(i);
+                stopPrinterStateTracking(printerId);
+            }
             // Make sure discovery is stopped.
             final int observerCount = mStartedPrinterDiscoveryTokens.size();
             for (int i = 0; i < observerCount; i++) {
@@ -744,9 +898,19 @@
             }
         }
 
-        private void handleRequestPrinterUpdate(RemotePrintService service,
+        private void handleValidatePrinters(RemotePrintService service,
+                List<PrinterId> printerIds) {
+            service.validatePrinters(printerIds);
+        }
+
+        private void handleStartPrinterStateTracking(RemotePrintService service,
                 PrinterId printerId) {
-            service.requestPrinterUpdate(printerId);
+            service.startPrinterStateTracking(printerId);
+        }
+
+        private void handleStopPrinterStateTracking(RemotePrintService service,
+                PrinterId printerId) {
+            service.stopPrinterStateTracking(printerId);
         }
 
         private void handlePrintersAdded(IPrinterDiscoveryObserver observer,
@@ -804,7 +968,9 @@
             public static final int MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION = 9;
             public static final int MSG_DISPATCH_START_PRINTER_DISCOVERY = 10;
             public static final int MSG_DISPATCH_STOP_PRINTER_DISCOVERY = 11;
-            public static final int MSG_REQUEST_PRINTER_UPDATE = 12;
+            public static final int MSG_VALIDATE_PRINTERS = 12;
+            public static final int MSG_START_PRINTER_STATE_TRACKING = 13;
+            public static final int MSG_STOP_PRINTER_STATE_TRACKING = 14;
 
             SessionHandler(Looper looper) {
                 super(looper, null, false);
@@ -878,13 +1044,29 @@
                         handleDispatchStopPrinterDiscovery(services);
                     } break;
 
-                    case MSG_REQUEST_PRINTER_UPDATE: {
+                    case MSG_VALIDATE_PRINTERS: {
+                        SomeArgs args = (SomeArgs) message.obj;
+                        RemotePrintService service = (RemotePrintService) args.arg1;
+                        List<PrinterId> printerIds = (List<PrinterId>) args.arg2;
+                        args.recycle();
+                        handleValidatePrinters(service, printerIds);
+                    } break;
+
+                    case MSG_START_PRINTER_STATE_TRACKING: {
                         SomeArgs args = (SomeArgs) message.obj;
                         RemotePrintService service = (RemotePrintService) args.arg1;
                         PrinterId printerId = (PrinterId) args.arg2;
                         args.recycle();
-                        handleRequestPrinterUpdate(service, printerId);
+                        handleStartPrinterStateTracking(service, printerId);
                     } break;
+
+                    case MSG_STOP_PRINTER_STATE_TRACKING: {
+                        SomeArgs args = (SomeArgs) message.obj;
+                        RemotePrintService service = (RemotePrintService) args.arg1;
+                        PrinterId printerId = (PrinterId) args.arg2;
+                        args.recycle();
+                        handleStopPrinterStateTracking(service, printerId);
+                    }
                 }
             }
         }
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 4f1ae11..c661b00 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -94,6 +94,8 @@
     public static final int EVENT_DISCONNECT_DC_RETRYING = BASE + 34;
     public static final int EVENT_DATA_SETUP_COMPLETE_ERROR = BASE + 35;
     public static final int CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA = BASE + 36;
+    public static final int CMD_ENABLE_MOBILE_PROVISIONING = BASE + 37;
+    public static final int CMD_IS_PROVISIONING_APN = BASE + 38;
 
     /***** Constants *****/
 
@@ -113,4 +115,5 @@
     public static final int ENABLED = 1;
 
     public static final String APN_TYPE_KEY = "apnType";
+    public static final String PROVISIONING_URL_KEY = "provisioningUrl";
 }
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index 65bdacf..a7baf1c 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -145,6 +145,29 @@
     public static final String ACTION_ANY_DATA_CONNECTION_STATE_CHANGED
             = "android.intent.action.ANY_DATA_STATE";
 
+    /**
+     * Broadcast Action: Occurs when a data connection connects to a provisioning apn
+     * and is broadcast by the low level data connection code.
+     * The intent will have the following extra values:</p>
+     * <ul>
+     *   <li><em>apn</em> - A string that is the APN associated with this
+     *      connection.</li>
+     *   <li><em>apnType</em> - A string array of APN types associated with
+     *      this connection.  The APN type <code>"*"</code> is a special
+     *      type that means this APN services all types.</li>
+     *   <li><em>linkProperties</em> - The <code>LinkProperties</code> for this APN</li>
+     *   <li><em>linkCapabilities</em> - The <code>linkCapabilities</code> for this APN</li>
+     *   <li><em>iface</em> - A string that is the name of the interface</li>
+     * </ul>
+     *
+     * <p class="note">
+     * Requires the READ_PHONE_STATE permission.
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     */
+    public static final String ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN
+            = "android.intent.action.DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN";
 
     /**
      * Broadcast Action: An attempt to establish a data connection has failed.