Merge "Solve three memory leaks related to PatchCache"
diff --git a/core/java/android/net/http/CertificateChainValidator.java b/core/java/android/net/http/CertificateChainValidator.java
index 1e476fc..a28b5a7 100644
--- a/core/java/android/net/http/CertificateChainValidator.java
+++ b/core/java/android/net/http/CertificateChainValidator.java
@@ -16,22 +16,26 @@
 
 package android.net.http;
 
-import com.android.org.conscrypt.SSLParametersImpl;
-import com.android.org.conscrypt.TrustManagerImpl;
+import android.util.Slog;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.lang.reflect.Method;
 import java.security.GeneralSecurityException;
-import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
-import javax.net.ssl.DefaultHostnameVerifier;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
 import javax.net.ssl.SSLHandshakeException;
 import javax.net.ssl.SSLSession;
 import javax.net.ssl.SSLSocket;
-import javax.net.ssl.X509TrustManager;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509ExtendedTrustManager;
 
 /**
  * Class responsible for all server certificate validation functionality
@@ -39,28 +43,51 @@
  * {@hide}
  */
 public class CertificateChainValidator {
+    private static final String TAG = "CertificateChainValidator";
 
-    /**
-     * The singleton instance of the certificate chain validator
-     */
-    private static final CertificateChainValidator sInstance
-            = new CertificateChainValidator();
+    private static class NoPreloadHolder {
+        /**
+         * The singleton instance of the certificate chain validator.
+         */
+        private static final CertificateChainValidator sInstance = new CertificateChainValidator();
 
-    private static final DefaultHostnameVerifier sVerifier
-            = new DefaultHostnameVerifier();
+        /**
+         * The singleton instance of the hostname verifier.
+         */
+        private static final HostnameVerifier sVerifier = HttpsURLConnection
+                .getDefaultHostnameVerifier();
+    }
+
+    private X509ExtendedTrustManager mTrustManager;
 
     /**
      * @return The singleton instance of the certificates chain validator
      */
     public static CertificateChainValidator getInstance() {
-        return sInstance;
+        return NoPreloadHolder.sInstance;
     }
 
     /**
      * Creates a new certificate chain validator. This is a private constructor.
      * If you need a Certificate chain validator, call getInstance().
      */
-    private CertificateChainValidator() {}
+    private CertificateChainValidator() {
+        try {
+            TrustManagerFactory tmf = TrustManagerFactory.getInstance("X.509");
+            for (TrustManager tm : tmf.getTrustManagers()) {
+                if (tm instanceof X509ExtendedTrustManager) {
+                    mTrustManager = (X509ExtendedTrustManager) tm;
+                }
+            }
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("X.509 TrustManager factory must be available", e);
+        }
+
+        if (mTrustManager == null) {
+            throw new RuntimeException(
+                    "None of the X.509 TrustManagers are X509ExtendedTrustManager");
+        }
+    }
 
     /**
      * Performs the handshake and server certificates validation
@@ -136,14 +163,27 @@
      * Handles updates to credential storage.
      */
     public static void handleTrustStorageUpdate() {
-
+        TrustManagerFactory tmf;
         try {
-            X509TrustManager x509TrustManager = SSLParametersImpl.getDefaultX509TrustManager();
-            if( x509TrustManager instanceof TrustManagerImpl ) {
-                TrustManagerImpl trustManager = (TrustManagerImpl) x509TrustManager;
-                trustManager.handleTrustStorageUpdate();
+            tmf = TrustManagerFactory.getInstance("X.509");
+        } catch (NoSuchAlgorithmException e) {
+            Slog.w(TAG, "Couldn't find default X.509 TrustManagerFactory");
+            return;
+        }
+
+        TrustManager[] tms = tmf.getTrustManagers();
+        boolean sentUpdate = false;
+        for (TrustManager tm : tms) {
+            try {
+                Method updateMethod = tm.getClass().getDeclaredMethod("handleTrustStorageUpdate");
+                updateMethod.setAccessible(true);
+                updateMethod.invoke(tm);
+                sentUpdate = true;
+            } catch (Exception e) {
             }
-        } catch (KeyManagementException ignored) {
+        }
+        if (!sentUpdate) {
+            Slog.w(TAG, "Didn't find a TrustManager to handle CA list update");
         }
     }
 
@@ -166,7 +206,8 @@
 
         boolean valid = domain != null
                 && !domain.isEmpty()
-                && sVerifier.verify(domain, currCertificate);
+                && NoPreloadHolder.sVerifier.verify(domain,
+                        new DelegatingSSLSession.CertificateWrap(currCertificate));
         if (!valid) {
             if (HttpLog.LOGV) {
                 HttpLog.v("certificate not for this host: " + domain);
@@ -175,13 +216,8 @@
         }
 
         try {
-            X509TrustManager x509TrustManager = SSLParametersImpl.getDefaultX509TrustManager();
-            if (x509TrustManager instanceof TrustManagerImpl) {
-                TrustManagerImpl trustManager = (TrustManagerImpl) x509TrustManager;
-                trustManager.checkServerTrusted(chain, authType, domain);
-            } else {
-                x509TrustManager.checkServerTrusted(chain, authType);
-            }
+            getInstance().getTrustManager().checkServerTrusted(chain, authType,
+                    new DelegatingSocketWrapper(domain));
             return null;  // No errors.
         } catch (GeneralSecurityException e) {
             if (HttpLog.LOGV) {
@@ -192,6 +228,12 @@
         }
     }
 
+    /**
+     * Returns the platform default {@link X509ExtendedTrustManager}.
+     */
+    private X509ExtendedTrustManager getTrustManager() {
+        return mTrustManager;
+    }
 
     private void closeSocketThrowException(
             SSLSocket socket, String errorMessage, String defaultErrorMessage)
@@ -217,4 +259,4 @@
 
         throw new SSLHandshakeException(errorMessage);
     }
-}
+}
\ No newline at end of file
diff --git a/core/java/android/net/http/DelegatingSSLSession.java b/core/java/android/net/http/DelegatingSSLSession.java
new file mode 100644
index 0000000..ff75b24
--- /dev/null
+++ b/core/java/android/net/http/DelegatingSSLSession.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.http;
+
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.X509ExtendedTrustManager;
+
+/**
+ * This is used when only a {@code hostname} is available but usage of the new API
+ * {@link X509ExtendedTrustManager#checkServerTrusted(X509Certificate[], String, Socket)}
+ * requires a {@link SSLSocket}.
+ *
+ * @hide
+ */
+public class DelegatingSSLSession implements SSLSession {
+    protected DelegatingSSLSession() {
+    }
+
+    public static class HostnameWrap extends DelegatingSSLSession {
+        private final String mHostname;
+
+        public HostnameWrap(String hostname) {
+            mHostname = hostname;
+        }
+
+        @Override
+        public String getPeerHost() {
+            return mHostname;
+        }
+    }
+
+    public static class CertificateWrap extends DelegatingSSLSession {
+        private final Certificate mCertificate;
+
+        public CertificateWrap(Certificate certificate) {
+            mCertificate = certificate;
+        }
+
+        @Override
+        public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+            return new Certificate[] { mCertificate };
+        }
+    }
+
+
+    @Override
+    public int getApplicationBufferSize() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getCipherSuite() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public long getCreationTime() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public byte[] getId() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public long getLastAccessedTime() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Certificate[] getLocalCertificates() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Principal getLocalPrincipal() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int getPacketBufferSize() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public javax.security.cert.X509Certificate[] getPeerCertificateChain()
+            throws SSLPeerUnverifiedException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getPeerHost() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int getPeerPort() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getProtocol() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public SSLSessionContext getSessionContext() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Object getValue(String name) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String[] getValueNames() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void invalidate() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isValid() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void putValue(String name, Object value) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void removeValue(String name) {
+        throw new UnsupportedOperationException();
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/net/http/DelegatingSocketWrapper.java b/core/java/android/net/http/DelegatingSocketWrapper.java
new file mode 100644
index 0000000..230d017
--- /dev/null
+++ b/core/java/android/net/http/DelegatingSocketWrapper.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.http;
+
+import java.io.IOException;
+
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.X509ExtendedTrustManager;
+
+/**
+ * This is used when only a {@code hostname} is available for
+ * {@link X509ExtendedTrustManager#checkServerTrusted(java.security.cert.X509Certificate[], String, Socket)}
+ * but we want to use the new API that requires a {@link SSLSocket}.
+ */
+class DelegatingSocketWrapper extends SSLSocket {
+    private String hostname;
+
+    public DelegatingSocketWrapper(String hostname) {
+        this.hostname = hostname;
+    }
+
+    @Override
+    public String[] getSupportedCipherSuites() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String[] getEnabledCipherSuites() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setEnabledCipherSuites(String[] suites) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String[] getSupportedProtocols() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String[] getEnabledProtocols() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setEnabledProtocols(String[] protocols) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public SSLSession getSession() {
+        return new DelegatingSSLSession.HostnameWrap(hostname);
+    }
+
+    @Override
+    public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void startHandshake() throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setUseClientMode(boolean mode) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean getUseClientMode() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setNeedClientAuth(boolean need) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setWantClientAuth(boolean want) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean getNeedClientAuth() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean getWantClientAuth() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setEnableSessionCreation(boolean flag) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean getEnableSessionCreation() {
+        throw new UnsupportedOperationException();
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/net/http/X509TrustManagerExtensions.java b/core/java/android/net/http/X509TrustManagerExtensions.java
index cfe5f27..666832a 100644
--- a/core/java/android/net/http/X509TrustManagerExtensions.java
+++ b/core/java/android/net/http/X509TrustManagerExtensions.java
@@ -22,14 +22,25 @@
 import java.security.cert.X509Certificate;
 import java.util.List;
 
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.X509ExtendedTrustManager;
 import javax.net.ssl.X509TrustManager;
 
 /**
  * X509TrustManager wrapper exposing Android-added features.
- *
- * <p> The checkServerTrusted method allows callers to perform additional
- * verification of certificate chains after they have been successfully
- * verified by the platform.</p>
+ * <p>
+ * The checkServerTrusted method allows callers to perform additional
+ * verification of certificate chains after they have been successfully verified
+ * by the platform.
+ * </p>
+ * <p>
+ * If the returned certificate list is not needed, see also
+ * {@code X509ExtendedTrustManager#checkServerTrusted(X509Certificate[], String, java.net.Socket)}
+ * where an {@link SSLSocket} can be used to verify the given hostname during
+ * handshake using
+ * {@code SSLParameters#setEndpointIdentificationAlgorithm(String)}.
+ * </p>
  */
 public class X509TrustManagerExtensions {
 
@@ -61,6 +72,7 @@
      */
     public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, String authType,
                                                     String host) throws CertificateException {
-        return mDelegate.checkServerTrusted(chain, authType, host);
+        return mDelegate.checkServerTrusted(chain, authType,
+                new DelegatingSSLSession.HostnameWrap(host));
     }
 }
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 13c1fc8..44bf7f8 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -114,7 +114,7 @@
         }
     }
 
-    int32_t* yDivs = chunk->getXDivs();
+    int32_t* yDivs = chunk->getYDivs();
     for (int i = 0; i < chunk->numYDivs; i++) {
         yDivs[i] = int32_t(yDivs[i] * scale + 0.5f);
         if (i > 0 && yDivs[i] == yDivs[i - 1]) {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 2ab7e36..b6c6770 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -2373,6 +2373,11 @@
     }
 
     public void removeWindowLocked(Session session, WindowState win) {
+        removeWindowLocked(session, win, false);
+    }
+
+    private void removeWindowLocked(Session session, WindowState win,
+            boolean forceRemove) {
         if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
             if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "Starting window removed " + win);
             removeStartingWindowTimeout(win.mAppToken);
@@ -2423,7 +2428,7 @@
                     mDisplayMagnifier.onWindowTransitionLocked(win, transit);
                 }
             }
-            if (win.mExiting || win.mWinAnimator.isAnimating()) {
+            if (!forceRemove && (win.mExiting || win.mWinAnimator.isAnimating())) {
                 // The exit animation is running... wait for it!
                 //Slog.i(TAG, "*** Running exit animation...");
                 win.mExiting = true;
@@ -10852,7 +10857,7 @@
             WindowList windows = displayContent.getWindowList();
             while (!windows.isEmpty()) {
                 final WindowState win = windows.get(windows.size() - 1);
-                removeWindowLocked(win.mSession, win);
+                removeWindowLocked(win.mSession, win, true);
             }
         }
         mAnimator.removeDisplayLocked(displayId);