Merge "Cancel current work in PrintDocumentAdatper if printing is cancelled." into klp-dev
diff --git a/core/java/android/net/PacProxySelector.java b/core/java/android/net/PacProxySelector.java
index b674324..8a2c2b6 100644
--- a/core/java/android/net/PacProxySelector.java
+++ b/core/java/android/net/PacProxySelector.java
@@ -97,7 +97,7 @@
                 } catch (Exception e) {
                     port = 8080;
                 }
-                ret.add(new Proxy(Type.HTTP, new InetSocketAddress(host, port)));
+                ret.add(new Proxy(Type.HTTP, InetSocketAddress.createUnresolved(host, port)));
             }
         }
         if (ret.size() == 0) {
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index f76e190..da9ba5a 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -1255,7 +1255,8 @@
             Animator anim = runningAnimators.keyAt(i);
             if (anim != null) {
                 AnimationInfo oldInfo = runningAnimators.get(anim);
-                if (oldInfo != null) {
+                if (oldInfo != null && oldInfo.view != null &&
+                        oldInfo.view.getContext() == sceneRoot.getContext()) {
                     boolean cancel = false;
                     TransitionValues oldValues = oldInfo.values;
                     View oldView = oldInfo.view;
diff --git a/core/java/android/transition/TransitionInflater.java b/core/java/android/transition/TransitionInflater.java
index 4af0f51..9f77d5e 100644
--- a/core/java/android/transition/TransitionInflater.java
+++ b/core/java/android/transition/TransitionInflater.java
@@ -20,9 +20,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
-import android.util.ArrayMap;
 import android.util.AttributeSet;
-import android.util.SparseArray;
 import android.util.Xml;
 import android.view.InflateException;
 import android.view.ViewGroup;
@@ -43,15 +41,7 @@
  */
 public class TransitionInflater {
 
-    // We only need one inflater for any given context. Also, this allows us to associate
-    // ids with unique instances per-Context, used to avoid re-inflating
-    // already-inflated resources into new/different instances
-    private static final ArrayMap<Context, TransitionInflater> sInflaterMap =
-            new ArrayMap<Context, TransitionInflater>();
-
     private Context mContext;
-    // TODO: do we need id maps for transitions and transitionMgrs as well?
-    SparseArray<Scene> mScenes = new SparseArray<Scene>();
 
     private TransitionInflater(Context context) {
         mContext = context;
@@ -61,13 +51,7 @@
      * Obtains the TransitionInflater from the given context.
      */
     public static TransitionInflater from(Context context) {
-        TransitionInflater inflater = sInflaterMap.get(context);
-        if (inflater != null) {
-            return inflater;
-        }
-        inflater = new TransitionInflater(context);
-        sInflaterMap.put(context, inflater);
-        return inflater;
+        return new TransitionInflater(context);
     }
 
     /**
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 65a2d4d..5392a96 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -959,9 +959,11 @@
         if (!mInDrawing) {
             if (verifyDrawable(dr)) {
                 final Rect dirty = dr.getBounds();
+                final int scrollX = mScrollX + mPaddingLeft;
+                final int scrollY = mScrollY + mPaddingTop;
 
-                invalidate(dirty.left + mScrollX, dirty.top + mScrollY,
-                        dirty.right + mScrollX, dirty.bottom + mScrollY);
+                invalidate(dirty.left + scrollX, dirty.top + scrollY,
+                        dirty.right + scrollX, dirty.bottom + scrollY);
             } else {
                 super.invalidateDrawable(dr);
             }
diff --git a/docs/downloads/devbytes/ImmersiveMode.zip b/docs/downloads/devbytes/ImmersiveMode.zip
new file mode 100644
index 0000000..6b82245
--- /dev/null
+++ b/docs/downloads/devbytes/ImmersiveMode.zip
Binary files differ
diff --git a/docs/html/develop/index.jd b/docs/html/develop/index.jd
index 3e82cbb..61a98b7 100644
--- a/docs/html/develop/index.jd
+++ b/docs/html/develop/index.jd
@@ -128,7 +128,7 @@
               <p>Three new features that make it easier to understand what players are doing in your game and help you manage game features...</p>
             </a></li>
             <li><a href="//android-developers.blogspot.com/2013/05/new-ways-to-optimize-your-business-in.html">
-              <div class="feed-image" style="background:url('//android-developers.blogspot.com/2013/10/improved-app-insight-by-linking-google.html') no-repeat 0 0;background-size:180px"></div>
+              <div class="feed-image" style="background:url('//3.bp.blogspot.com/-_8WvpdTVGsE/UkxxxrVoNNI/AAAAAAAACj8/FrQyA-BO11c/s1600/gp-referral-ga.png') no-repeat 0 0;background-size:180px"></div>
               <h4>Linking Google Analytics with Google Play</h4>
               <p>Understanding your users easier through a new integration between Google Analytics and the Google Play Developer Console...</p>
               </a></li>
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardService.java b/packages/Keyguard/src/com/android/keyguard/KeyguardService.java
index 8ccd6fe..36b2446 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardService.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardService.java
@@ -68,8 +68,6 @@
     }
 
     private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {
-        private boolean mSetHiddenCalled;
-        private boolean mIsHidden;
         public boolean isShowing() {
             return mKeyguardViewMediator.isShowing();
         }
@@ -91,10 +89,7 @@
         }
         public void setHidden(boolean isHidden) {
             checkPermission();
-            if (mSetHiddenCalled && mIsHidden == isHidden) return;
             mKeyguardViewMediator.setHidden(isHidden);
-            mSetHiddenCalled = true;
-            mIsHidden = isHidden;
         }
         public void dismiss() {
             mKeyguardViewMediator.dismiss();
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
index d933275..0bfee38 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
@@ -98,26 +98,13 @@
     }
 
     protected void refresh() {
-        Resources res = mContext.getResources();
-        Locale locale = Locale.getDefault();
-        final String dateFormat = DateFormat.getBestDateTimePattern(locale,
-                res.getString(R.string.abbrev_wday_month_day_no_year));
+        Patterns.update(mContext);
 
-        mDateView.setFormat24Hour(dateFormat);
-        mDateView.setFormat12Hour(dateFormat);
+        mDateView.setFormat24Hour(Patterns.dateView);
+        mDateView.setFormat12Hour(Patterns.dateView);
 
-        // 12-hour clock.
-        // CLDR insists on adding an AM/PM indicator even though it wasn't in the skeleton
-        // format.  The following code removes the AM/PM indicator if we didn't want it.
-        final String clock12skel = res.getString(R.string.clock_12hr_format);
-        String clock12hr = DateFormat.getBestDateTimePattern(locale, clock12skel);
-        clock12hr = clock12skel.contains("a") ? clock12hr : clock12hr.replaceAll("a", "").trim();
-        mClockView.setFormat12Hour(clock12hr);
-
-        // 24-hour clock
-        final String clock24skel = res.getString(R.string.clock_24hr_format);
-        final String clock24hr = DateFormat.getBestDateTimePattern(locale, clock24skel);
-        mClockView.setFormat24Hour(clock24hr);
+        mClockView.setFormat12Hour(Patterns.clockView12);
+        mClockView.setFormat24Hour(Patterns.clockView24);
 
         refreshAlarmStatus();
     }
@@ -149,4 +136,35 @@
         return LockPatternUtils.ID_DEFAULT_STATUS_WIDGET;
     }
 
+    // DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often.
+    // This is an optimization to ensure we only recompute the patterns when the inputs change.
+    private static final class Patterns {
+        static String dateView;
+        static String clockView12;
+        static String clockView24;
+        static String cacheKey;
+
+        static void update(Context context) {
+            final Locale locale = Locale.getDefault();
+            final Resources res = context.getResources();
+            final String dateViewSkel = res.getString(R.string.abbrev_wday_month_day_no_year);
+            final String clockView12Skel = res.getString(R.string.clock_12hr_format);
+            final String clockView24Skel = res.getString(R.string.clock_24hr_format);
+            final String key = locale.toString() + dateViewSkel + clockView12Skel + clockView24Skel;
+            if (key.equals(cacheKey)) return;
+
+            dateView = DateFormat.getBestDateTimePattern(locale, dateViewSkel);
+
+            clockView12 = DateFormat.getBestDateTimePattern(locale, clockView12Skel);
+            // CLDR insists on adding an AM/PM indicator even though it wasn't in the skeleton
+            // format.  The following code removes the AM/PM indicator if we didn't want it.
+            if (!clockView12Skel.contains("a")) {
+                clockView12 = clockView12.replaceAll("a", "").trim();
+            }
+
+            clockView24 = DateFormat.getBestDateTimePattern(locale, clockView24Skel);
+
+            cacheKey = key;
+        }
+    }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 520cea3..a849316 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -815,7 +815,7 @@
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
-                cb.onKeyguardVisibilityChanged(isShowing);
+                cb.onKeyguardVisibilityChangedRaw(isShowing);
             }
         }
     }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 76f9637..c08880d 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -19,6 +19,7 @@
 import android.app.admin.DevicePolicyManager;
 import android.graphics.Bitmap;
 import android.media.AudioManager;
+import android.os.SystemClock;
 import android.view.WindowManagerPolicy;
 
 import com.android.internal.telephony.IccCardConstants;
@@ -27,6 +28,11 @@
  * Callback for general information relevant to lock screen.
  */
 class KeyguardUpdateMonitorCallback {
+
+    private static final long VISIBILITY_CHANGED_COLLAPSE_MS = 1000;
+    private long mVisibilityChangedCalled;
+    private boolean mShowing;
+
     /**
      * Called when the battery status changes, e.g. when plugged in or unplugged, charge
      * level, etc. changes.
@@ -70,6 +76,15 @@
      */
     void onKeyguardVisibilityChanged(boolean showing) { }
 
+    void onKeyguardVisibilityChangedRaw(boolean showing) {
+        final long now = SystemClock.elapsedRealtime();
+        if (showing == mShowing
+                && (now - mVisibilityChangedCalled) < VISIBILITY_CHANGED_COLLAPSE_MS) return;
+        onKeyguardVisibilityChanged(showing);
+        mVisibilityChangedCalled = now;
+        mShowing = showing;
+    }
+
     /**
      * Called when visibility of lockscreen clock changes, such as when
      * obscured by a widget.
diff --git a/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java b/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
index 596435a..10bcdad 100644
--- a/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
+++ b/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java
@@ -117,8 +117,8 @@
                         if (!proxy.equals(Proxy.NO_PROXY)) {
                             // Only Inets created by PacProxySelector.
                             InetSocketAddress inetSocketAddress =
-                                    (InetSocketAddress)list.get(0).address();
-                            server = new Socket(inetSocketAddress.getAddress(),
+                                    (InetSocketAddress)proxy.address();
+                            server = new Socket(inetSocketAddress.getHostName(),
                                     inetSocketAddress.getPort());
                             sendLine(server, requestLine);
                         } else {
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 7c61c44..594f683 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -77,6 +77,7 @@
 import android.net.wimax.WimaxManagerConstants;
 import android.os.AsyncTask;
 import android.os.Binder;
+import android.os.Build;
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -141,6 +142,7 @@
 import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.URL;
+import java.net.URLConnection;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -154,6 +156,10 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLSession;
+
 /**
  * @hide
  */
@@ -4066,8 +4072,28 @@
     static class CheckMp extends
             AsyncTask<CheckMp.Params, Void, Integer> {
         private static final String CHECKMP_TAG = "CheckMp";
+
+        // adb shell setprop persist.checkmp.testfailures 1 to enable testing failures
+        private static boolean mTestingFailures;
+
+        // Choosing 4 loops as half of them will use HTTPS and the other half HTTP
+        private static final int MAX_LOOPS = 4;
+
+        // Number of milli-seconds to complete all of the retires
         public static final int MAX_TIMEOUT_MS =  60000;
+
+        // The socket should retry only 5 seconds, the default is longer
         private static final int SOCKET_TIMEOUT_MS = 5000;
+
+        // Sleep time for network errors
+        private static final int NET_ERROR_SLEEP_SEC = 3;
+
+        // Sleep time for network route establishment
+        private static final int NET_ROUTE_ESTABLISHMENT_SLEEP_SEC = 3;
+
+        // Short sleep time for polling :(
+        private static final int POLLING_SLEEP_SEC = 1;
+
         private Context mContext;
         private ConnectivityService mCs;
         private TelephonyManager mTm;
@@ -4093,6 +4119,31 @@
             }
         }
 
+        // As explained to me by Brian Carlstrom and Kenny Root, Certificates can be
+        // issued by name or ip address, for Google its by name so when we construct
+        // this HostnameVerifier we'll pass the original Uri and use it to verify
+        // the host. If the host name in the original uril fails we'll test the
+        // hostname parameter just incase things change.
+        static class CheckMpHostnameVerifier implements HostnameVerifier {
+            Uri mOrgUri;
+
+            CheckMpHostnameVerifier(Uri orgUri) {
+                mOrgUri = orgUri;
+            }
+
+            @Override
+            public boolean verify(String hostname, SSLSession session) {
+                HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
+                String orgUriHost = mOrgUri.getHost();
+                boolean retVal = hv.verify(orgUriHost, session) || hv.verify(hostname, session);
+                if (DBG) {
+                    log("isMobileOk: hostnameVerify retVal=" + retVal + " hostname=" + hostname
+                        + " orgUriHost=" + orgUriHost);
+                }
+                return retVal;
+            }
+        }
+
         /**
          * The call back object passed in Params. onComplete will be called
          * on the main thread.
@@ -4103,6 +4154,13 @@
         }
 
         public CheckMp(Context context, ConnectivityService cs) {
+            if (Build.IS_DEBUGGABLE) {
+                mTestingFailures =
+                        SystemProperties.getInt("persist.checkmp.testfailures", 0) == 1;
+            } else {
+                mTestingFailures = false;
+            }
+
             mContext = context;
             mCs = cs;
 
@@ -4174,7 +4232,7 @@
                             mCs.setEnableFailFastMobileData(DctConstants.ENABLED);
                             break;
                         }
-                        sleep(1);
+                        sleep(POLLING_SLEEP_SEC);
                     }
                 }
 
@@ -4192,7 +4250,7 @@
                     }
                     if (VDBG) log("isMobileOk: hipri not started yet");
                     result = CMP_RESULT_CODE_NO_CONNECTION;
-                    sleep(1);
+                    sleep(POLLING_SLEEP_SEC);
                 }
 
                 // Continue trying to connect until time has run out
@@ -4208,7 +4266,7 @@
                                 log("isMobileOk: not connected ni=" +
                                     mCs.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI));
                             }
-                            sleep(1);
+                            sleep(POLLING_SLEEP_SEC);
                             result = CMP_RESULT_CODE_NO_CONNECTION;
                             continue;
                         }
@@ -4226,7 +4284,7 @@
 
                         // 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
+                        // the addresses and will try every address but that will bypass the
                         // route to host we setup and the connection could succeed as the default
                         // interface might be connected to the internet via wifi or other interface.
                         InetAddress[] addresses;
@@ -4263,14 +4321,14 @@
 
                         int addrTried = 0;
                         while (true) {
-                            // Loop through at most 3 valid addresses or until
+                            // Loop through at most MAX_LOOPS valid addresses or until
                             // we run out of time
-                            if (addrTried++ >= 3) {
-                                log("too many loops tried - giving up");
+                            if (addrTried++ >= MAX_LOOPS) {
+                                log("isMobileOk: too many loops tried - giving up");
                                 break;
                             }
                             if (SystemClock.elapsedRealtime() >= endTime) {
-                                log("spend too much time - giving up");
+                                log("isMobileOk: spend too much time - giving up");
                                 break;
                             }
 
@@ -4283,25 +4341,37 @@
                                 // Wait a short time to be sure the route is established ??
                                 log("isMobileOk:"
                                         + " wait to establish route to hostAddr=" + hostAddr);
-                                sleep(3);
+                                sleep(NET_ROUTE_ESTABLISHMENT_SLEEP_SEC);
                             } else {
                                 log("isMobileOk:"
                                         + " could not establish route to hostAddr=" + hostAddr);
+                                // Wait a short time before the next attempt
+                                sleep(NET_ERROR_SLEEP_SEC);
                                 continue;
                             }
 
-                            // Rewrite the url to have numeric address to use the specific route.
-                            // Add a pointless random query param to fool proxies into not caching.
-                            URL newUrl = new URL(orgUri.getScheme(),
-                                    hostAddr.getHostAddress(),
-                                    orgUri.getPath() + "?q=" + rand.nextInt(Integer.MAX_VALUE));
+                            // Rewrite the url to have numeric address to use the specific route
+                            // using http for half the attempts and https for the other half.
+                            // Doing https first and http second as on a redirected walled garden
+                            // such as t-mobile uses we get a SocketTimeoutException: "SSL
+                            // handshake timed out" which we declare as
+                            // CMP_RESULT_CODE_NO_TCP_CONNECTION. We could change this, but by
+                            // having http second we will be using logic used for some time.
+                            URL newUrl;
+                            String scheme = (addrTried <= (MAX_LOOPS/2)) ? "https" : "http";
+                            newUrl = new URL(scheme, hostAddr.getHostAddress(),
+                                        orgUri.getPath());
                             log("isMobileOk: newUrl=" + newUrl);
 
                             HttpURLConnection urlConn = null;
                             try {
-                                // Open the connection set the request header and get the response
-                                urlConn = (HttpURLConnection) newUrl.openConnection(
+                                // Open the connection set the request headers and get the response
+                                urlConn = (HttpURLConnection)newUrl.openConnection(
                                         java.net.Proxy.NO_PROXY);
+                                if (scheme.equals("https")) {
+                                    ((HttpsURLConnection)urlConn).setHostnameVerifier(
+                                            new CheckMpHostnameVerifier(orgUri));
+                                }
                                 urlConn.setInstanceFollowRedirects(false);
                                 urlConn.setConnectTimeout(SOCKET_TIMEOUT_MS);
                                 urlConn.setReadTimeout(SOCKET_TIMEOUT_MS);
@@ -4320,10 +4390,17 @@
                                 urlConn.disconnect();
                                 urlConn = null;
 
+                                if (mTestingFailures) {
+                                    // Pretend no connection, this tests using http and https
+                                    result = CMP_RESULT_CODE_NO_CONNECTION;
+                                    log("isMobileOk: TESTING_FAILURES, pretend no connction");
+                                    continue;
+                                }
+
                                 if (responseCode == 204) {
                                     // Return
                                     result = CMP_RESULT_CODE_CONNECTABLE;
-                                    log("isMobileOk: X expected responseCode=" + responseCode
+                                    log("isMobileOk: X got expected responseCode=" + responseCode
                                             + " result=" + result);
                                     return result;
                                 } else {
@@ -4337,12 +4414,14 @@
                                     result = CMP_RESULT_CODE_REDIRECTED;
                                 }
                             } catch (Exception e) {
-                                log("isMobileOk: HttpURLConnection Exception e=" + e);
+                                log("isMobileOk: HttpURLConnection Exception" + e);
                                 result = CMP_RESULT_CODE_NO_TCP_CONNECTION;
                                 if (urlConn != null) {
                                     urlConn.disconnect();
                                     urlConn = null;
                                 }
+                                sleep(NET_ERROR_SLEEP_SEC);
+                                continue;
                             }
                         }
                         log("isMobileOk: X loops|timed out result=" + result);
@@ -4370,7 +4449,7 @@
                             log("isMobileOk: connected ni=" +
                                 mCs.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI));
                         }
-                        sleep(1);
+                        sleep(POLLING_SLEEP_SEC);
                         continue;
                     }
                 }
@@ -4435,7 +4514,7 @@
             }
         }
 
-        private void log(String s) {
+        private static void log(String s) {
             Slog.d(ConnectivityService.TAG, "[" + CHECKMP_TAG + "] " + s);
         }
     }