Most callers of toLowerCase/toUpperCase should pass Locale.US to avoid problems in Turkey.

Some callers should be replaced with equalsIgnoreCase instead.

The one exception is StreamTokenizer, where the RI uses the default
locale, which is arguably the right thing to do. No-one cares because
that's legacy API, but I've added a test anyway.

I've left HttpCookie and GeneralName for my co-conspirators because the
appropriate resolutions aren't as obvious there...

Bug: 3325637
Change-Id: Ia37a1caaa91b11763ae43e61e445adb45c30f793
diff --git a/luni/src/main/java/java/io/FilePermission.java b/luni/src/main/java/java/io/FilePermission.java
index 193b05e..e77c424 100644
--- a/luni/src/main/java/java/io/FilePermission.java
+++ b/luni/src/main/java/java/io/FilePermission.java
@@ -20,6 +20,7 @@
 import java.security.Permission;
 import java.security.PermissionCollection;
 import java.security.PrivilegedAction;
+import java.util.Locale;
 import libcore.base.Objects;
 
 /**
@@ -125,7 +126,7 @@
      * @return the string representing this permission's actions
      */
     private String toCanonicalActionString(String action) {
-        actions = action.trim().toLowerCase();
+        actions = action.trim().toLowerCase(Locale.US);
 
         // get the numerical representation of the action list
         mask = getMask(actions);
diff --git a/luni/src/main/java/java/io/StreamTokenizer.java b/luni/src/main/java/java/io/StreamTokenizer.java
index 36c42bf..0522be6 100644
--- a/luni/src/main/java/java/io/StreamTokenizer.java
+++ b/luni/src/main/java/java/io/StreamTokenizer.java
@@ -17,6 +17,8 @@
 
 package java.io;
 
+import java.util.Locale;
+
 /**
  * Parses a stream into a set of defined tokens, one at a time. The different
  * types of tokens that can be found are numbers, identifiers, quoted strings,
@@ -346,8 +348,10 @@
                 }
             }
             peekChar = currentChar;
-            sval = forceLowercase ? word.toString().toLowerCase() : word
-                    .toString();
+            sval = word.toString();
+            if (forceLowercase) {
+                sval = sval.toLowerCase(Locale.getDefault());
+            }
             return (ttype = TT_WORD);
         }
         // Check for quoted character
diff --git a/luni/src/main/java/java/lang/Class.java b/luni/src/main/java/java/lang/Class.java
index 3c5fec9..95266a8 100644
--- a/luni/src/main/java/java/lang/Class.java
+++ b/luni/src/main/java/java/lang/Class.java
@@ -1281,7 +1281,7 @@
     @Override
     public String toString() {
         if (isPrimitive()) {
-            return getSimpleName().toLowerCase();
+            return getSimpleName();
         } else {
             return (isInterface() ? "interface " : "class ") + getName();
         }
diff --git a/luni/src/main/java/java/net/DefaultFileNameMap.java b/luni/src/main/java/java/net/DefaultFileNameMap.java
index a502a88..2f254d9 100644
--- a/luni/src/main/java/java/net/DefaultFileNameMap.java
+++ b/luni/src/main/java/java/net/DefaultFileNameMap.java
@@ -16,6 +16,7 @@
 
 package java.net;
 
+import java.util.Locale;
 import libcore.net.MimeUtils;
 
 /**
@@ -36,6 +37,6 @@
         if (firstCharInExtension > filename.lastIndexOf('/')) {
             ext = filename.substring(firstCharInExtension, lastCharInExtension);
         }
-        return MimeUtils.guessMimeTypeFromExtension(ext.toLowerCase());
+        return MimeUtils.guessMimeTypeFromExtension(ext.toLowerCase(Locale.US));
     }
 }
diff --git a/luni/src/main/java/java/net/SocketPermission.java b/luni/src/main/java/java/net/SocketPermission.java
index 4bbce35..c63872e 100644
--- a/luni/src/main/java/java/net/SocketPermission.java
+++ b/luni/src/main/java/java/net/SocketPermission.java
@@ -23,6 +23,7 @@
 import java.io.Serializable;
 import java.security.Permission;
 import java.security.PermissionCollection;
+import java.util.Locale;
 
 /**
  * Regulates the access to network operations available through sockets through
@@ -218,7 +219,7 @@
             if (pos == length) {
                 parsing = false;
             }
-            action = sb.toString().trim().toLowerCase();
+            action = sb.toString().trim().toLowerCase(Locale.US);
             if (action.equals(actionNames[SP_CONNECT])) {
                 actionsMask |= SP_CONNECT;
             } else if (action.equals(actionNames[SP_LISTEN])) {
@@ -414,7 +415,7 @@
             if (idx > -1) {
                 host = host.substring(0, idx);
             }
-            return host.toLowerCase();
+            return host.toLowerCase(Locale.US);
         }
 
         int lastIdx = host.lastIndexOf(':');
@@ -424,7 +425,7 @@
                 // only one colon, should be port
                 host = host.substring(0, idx);
             }
-            return host.toLowerCase();
+            return host.toLowerCase(Locale.US);
         }
         // maybe IPv6
         boolean isFirstBracket = (host.charAt(0) == '[');
@@ -441,7 +442,7 @@
                 host = host.substring(0, lastIdx);
             }
             if (isIP6AddressInFullForm(host)) {
-                return host.toLowerCase();
+                return host.toLowerCase(Locale.US);
             }
             throw new IllegalArgumentException("Invalid port number: " + host);
         }
@@ -453,7 +454,7 @@
         }
         host = host.substring(0, bbracketIdx + 1);
         if (isValidIP6Address(host)) {
-            return host.toLowerCase();
+            return host.toLowerCase(Locale.US);
         }
         throw new IllegalArgumentException("Invalid port number: " + host);
     }
diff --git a/luni/src/main/java/java/net/URI.java b/luni/src/main/java/java/net/URI.java
index da68970..fc513a2 100644
--- a/luni/src/main/java/java/net/URI.java
+++ b/luni/src/main/java/java/net/URI.java
@@ -22,6 +22,7 @@
 import java.io.ObjectOutputStream;
 import java.io.Serializable;
 import java.io.UnsupportedEncodingException;
+import java.util.Locale;
 import java.util.StringTokenizer;
 import org.apache.harmony.luni.platform.INetworkSystem;
 import org.apache.harmony.luni.platform.Platform;
@@ -800,7 +801,7 @@
         int index, prevIndex = 0;
         while ((index = s.indexOf('%', prevIndex)) != -1) {
             result.append(s.substring(prevIndex, index + 1));
-            result.append(s.substring(index + 1, index + 3).toLowerCase());
+            result.append(s.substring(index + 1, index + 3).toLowerCase(Locale.US));
             index += 3;
             prevIndex = index;
         }
@@ -1490,7 +1491,7 @@
     private String getHashString() {
         StringBuilder result = new StringBuilder();
         if (scheme != null) {
-            result.append(scheme.toLowerCase());
+            result.append(scheme.toLowerCase(Locale.US));
             result.append(':');
         }
         if (opaque) {
@@ -1504,7 +1505,7 @@
                     if (userInfo != null) {
                         result.append(userInfo + "@");
                     }
-                    result.append(host.toLowerCase());
+                    result.append(host.toLowerCase(Locale.US));
                     if (port != -1) {
                         result.append(":" + port);
                     }
diff --git a/luni/src/main/java/java/net/URLClassLoader.java b/luni/src/main/java/java/net/URLClassLoader.java
index 25d1cf8..da71c3b 100644
--- a/luni/src/main/java/java/net/URLClassLoader.java
+++ b/luni/src/main/java/java/net/URLClassLoader.java
@@ -984,17 +984,16 @@
     }
 
     private boolean isSealed(Manifest manifest, String dirName) {
-        Attributes mainAttributes = manifest.getMainAttributes();
-        String value = mainAttributes.getValue(Attributes.Name.SEALED);
-        boolean sealed = value != null && value.toLowerCase().equals("true");
         Attributes attributes = manifest.getAttributes(dirName);
         if (attributes != null) {
-            value = attributes.getValue(Attributes.Name.SEALED);
+            String value = attributes.getValue(Attributes.Name.SEALED);
             if (value != null) {
-                sealed = value.toLowerCase().equals("true");
+                return value.equalsIgnoreCase("true");
             }
         }
-        return sealed;
+        Attributes mainAttributes = manifest.getMainAttributes();
+        String value = mainAttributes.getValue(Attributes.Name.SEALED);
+        return (value != null && value.equalsIgnoreCase("true"));
     }
 
     /**
diff --git a/luni/src/main/java/java/net/URLConnection.java b/luni/src/main/java/java/net/URLConnection.java
index 18f008b..b915fb8 100644
--- a/luni/src/main/java/java/net/URLConnection.java
+++ b/luni/src/main/java/java/net/URLConnection.java
@@ -24,6 +24,7 @@
 import java.util.Date;
 import java.util.Hashtable;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.StringTokenizer;
 
@@ -774,7 +775,7 @@
         }
 
         // Check text types
-        String textHeader = header.trim().toUpperCase();
+        String textHeader = header.trim().toUpperCase(Locale.US);
         if (textHeader.startsWith("<!DOCTYPE HTML") ||
                 textHeader.startsWith("<HTML") ||
                 textHeader.startsWith("<HEAD") ||
diff --git a/luni/src/main/java/java/util/Date.java b/luni/src/main/java/java/util/Date.java
index 90cd270..c2aa04d 100644
--- a/luni/src/main/java/java/util/Date.java
+++ b/luni/src/main/java/java/util/Date.java
@@ -470,7 +470,7 @@
                     throw new IllegalArgumentException();
                 }
             } else if (state == LETTERS && nextState != LETTERS) {
-                String text = buffer.toString().toUpperCase();
+                String text = buffer.toString().toUpperCase(Locale.US);
                 buffer.setLength(0);
                 if (text.length() == 1) {
                     throw new IllegalArgumentException();
diff --git a/luni/src/main/java/javax/security/auth/x500/X500Principal.java b/luni/src/main/java/javax/security/auth/x500/X500Principal.java
index 9e1fb9a..f13dd4f 100644
--- a/luni/src/main/java/javax/security/auth/x500/X500Principal.java
+++ b/luni/src/main/java/javax/security/auth/x500/X500Principal.java
@@ -223,7 +223,7 @@
         String rfc1779Name = dn.getName(RFC1779);
         String rfc2253Name = dn.getName(RFC2253);
 
-        if (format.toUpperCase().equals("RFC1779")) {
+        if (format.equalsIgnoreCase("RFC1779")) {
             StringBuilder resultName = new StringBuilder(rfc1779Name);
             int fromIndex = resultName.length();
             int equalIndex = -1;
@@ -243,7 +243,7 @@
                 fromIndex = commaIndex;
             }
             return resultName.toString();
-        } else if (format.toUpperCase().equals("RFC2253")) {
+        } else if (format.equalsIgnoreCase("RFC2253")) {
             StringBuilder resultName = new StringBuilder(rfc2253Name);
             StringBuilder subsidyName = new StringBuilder(rfc1779Name);
 
diff --git a/luni/src/main/java/org/apache/harmony/luni/util/FloatingPointParser.java b/luni/src/main/java/org/apache/harmony/luni/util/FloatingPointParser.java
index 1feb323..f4774fd 100644
--- a/luni/src/main/java/org/apache/harmony/luni/util/FloatingPointParser.java
+++ b/luni/src/main/java/org/apache/harmony/luni/util/FloatingPointParser.java
@@ -282,8 +282,9 @@
             return parseDblName(s, length);
         }
 
-        // See if it could be a hexadecimal representation
-        if (s.toLowerCase().indexOf("0x") != -1) {
+        // See if it could be a hexadecimal representation.
+        // We don't use startsWith because there might be a leading sign.
+        if (s.indexOf("0x") != -1 || s.indexOf("0X") != -1) {
             return HexStringParser.parseDouble(s);
         }
 
@@ -324,7 +325,8 @@
         }
 
         // See if it could be a hexadecimal representation
-        if (s.toLowerCase().indexOf("0x") != -1) {
+        // We don't use startsWith because there might be a leading sign.
+        if (s.indexOf("0x") != -1 || s.indexOf("0X") != -1) {
             return HexStringParser.parseFloat(s);
         }
 
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/HandshakeIODataStream.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/HandshakeIODataStream.java
index 720e7a6..91e6fe7 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/HandshakeIODataStream.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/HandshakeIODataStream.java
@@ -21,6 +21,7 @@
 import java.io.PrintStream;
 import java.security.MessageDigest;
 import java.util.Arrays;
+import java.util.Locale;
 import javax.net.ssl.SSLHandshakeException;
 
 /**
@@ -375,8 +376,7 @@
         String delimiter = "";
 
         for (int i=write_pos_beg; i<write_pos; i++) {
-            String tail = Integer.toHexString(
-                    0x00ff & buffer[i]).toUpperCase();
+            String tail = Integer.toHexString(buffer[i] & 0xff).toUpperCase(Locale.US);
             if (tail.length() == 1) {
                 tail = "0" + tail;
             }
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerImpl.java
index 6955ab1..07ea571 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerImpl.java
@@ -30,6 +30,7 @@
 import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.List;
+import java.util.Locale;
 import java.util.Vector;
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.X509ExtendedKeyManager;
@@ -159,7 +160,7 @@
             final Certificate cert = chain[0];
             final String certKeyAlg = cert.getPublicKey().getAlgorithm();
             final String certSigAlg = (cert instanceof X509Certificate
-                                       ? ((X509Certificate) cert).getSigAlgName().toUpperCase()
+                                       ? ((X509Certificate) cert).getSigAlgName().toUpperCase(Locale.US)
                                        : null);
             for (String keyAlgorithm : keyTypes) {
                 if (keyAlgorithm == null) {
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java
index 009608e..551c7b3 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java
@@ -18,6 +18,7 @@
 package org.apache.harmony.xnet.provider.jsse;
 
 import java.io.PrintStream;
+import java.util.Locale;
 import libcore.base.EmptyArray;
 
 /**
@@ -78,8 +79,7 @@
                 byte[] data, int offset, int len) {
             String line = "";
             for (int i=0; i<len; i++) {
-                String tail =
-                    Integer.toHexString(0x00ff & data[i+offset]).toUpperCase();
+                String tail = Integer.toHexString(data[i+offset] & 0xff).toUpperCase(Locale.US);
                 if (tail.length() == 1) {
                     tail = "0" + tail;
                 }
diff --git a/luni/src/test/java/libcore/java/io/StreamTokenizerTest.java b/luni/src/test/java/libcore/java/io/StreamTokenizerTest.java
new file mode 100644
index 0000000..418d193
--- /dev/null
+++ b/luni/src/test/java/libcore/java/io/StreamTokenizerTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2011 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 libcore.java.io;
+
+import java.io.StreamTokenizer;
+import java.io.StringReader;
+import java.util.Locale;
+import junit.framework.TestCase;
+
+public class StreamTokenizerTest extends TestCase {
+    public void testLowerCase() throws Exception {
+        Locale.setDefault(Locale.US);
+        StreamTokenizer st = new StreamTokenizer(new StringReader("aIb aIb"));
+        st.lowerCaseMode(true);
+        st.nextToken();
+        assertEquals("aib", st.sval);
+
+        Locale.setDefault(new Locale("tr", "TR"));
+        st.nextToken();
+        assertEquals("a\u0131b", st.sval);
+    }
+}
diff --git a/xml/src/main/java/org/kxml2/io/KXmlSerializer.java b/xml/src/main/java/org/kxml2/io/KXmlSerializer.java
index d676c41..d1965d6 100644
--- a/xml/src/main/java/org/kxml2/io/KXmlSerializer.java
+++ b/xml/src/main/java/org/kxml2/io/KXmlSerializer.java
@@ -22,6 +22,7 @@
 package org.kxml2.io;
 
 import java.io.*;
+import java.util.Locale;
 import org.xmlpull.v1.*;
 
 public class KXmlSerializer implements XmlSerializer {
@@ -332,21 +333,19 @@
                 ? new OutputStreamWriter(os)
                 : new OutputStreamWriter(os, encoding));
         this.encoding = encoding;
-        if (encoding != null
-            && encoding.toLowerCase().startsWith("utf"))
+        if (encoding != null && encoding.toLowerCase(Locale.US).startsWith("utf")) {
             unicode = true;
+        }
     }
 
-    public void startDocument(
-        String encoding,
-        Boolean standalone)
-        throws IOException {
+    public void startDocument(String encoding, Boolean standalone) throws IOException {
         writer.write("<?xml version='1.0' ");
 
         if (encoding != null) {
             this.encoding = encoding;
-            if (encoding.toLowerCase().startsWith("utf"))
+            if (encoding.toLowerCase(Locale.US).startsWith("utf")) {
                 unicode = true;
+            }
         }
 
         if (this.encoding != null) {