Add faster TextUtil function for searching delimited lists.

The previous version in Settings allocated memory.

Change-Id: I0f821112dc8f830689489f201ce268195f9e6cbd
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f37ef99..fd60115 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3481,13 +3481,7 @@
          */
         public static final boolean isLocationProviderEnabled(ContentResolver cr, String provider) {
             String allowedProviders = Settings.Secure.getString(cr, LOCATION_PROVIDERS_ALLOWED);
-            if (allowedProviders != null) {
-                return (allowedProviders.equals(provider) ||
-                        allowedProviders.contains("," + provider + ",") ||
-                        allowedProviders.startsWith(provider + ",") ||
-                        allowedProviders.endsWith("," + provider));
-            }
-            return false;
+            return TextUtils.delimitedStringContains(allowedProviders, ',', provider);
         }
 
         /**
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 9589bf3..8675d05 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -1651,7 +1651,36 @@
 
         return mode;
     }
-    
+
+    /**
+     * Does a comma-delimited list 'delimitedString' contain a certain item?
+     * (without allocating memory)
+     *
+     * @hide
+     */
+    public static boolean delimitedStringContains(
+            String delimitedString, char delimiter, String item) {
+        if (isEmpty(delimitedString) || isEmpty(item)) {
+            return false;
+        }
+        int pos = -1;
+        int length = delimitedString.length();
+        while ((pos = delimitedString.indexOf(item, pos + 1)) != -1) {
+            if (pos > 0 && delimitedString.charAt(pos - 1) != delimiter) {
+                continue;
+            }
+            int expectedDelimiterPos = pos + item.length();
+            if (expectedDelimiterPos == length) {
+                // Match at end of string.
+                return true;
+            }
+            if (delimitedString.charAt(expectedDelimiterPos) == delimiter) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private static Object sLock = new Object();
     private static char[] sTemp = null;
 }
diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java
index a5229cc..1beba53 100644
--- a/core/tests/coretests/src/android/text/TextUtilsTest.java
+++ b/core/tests/coretests/src/android/text/TextUtilsTest.java
@@ -349,6 +349,26 @@
         }
     }
 
+    @SmallTest
+    public void testDelimitedStringContains() {
+        assertFalse(TextUtils.delimitedStringContains("", ',', null));
+        assertFalse(TextUtils.delimitedStringContains(null, ',', ""));
+        // Whole match
+        assertTrue(TextUtils.delimitedStringContains("gps", ',', "gps"));
+        // At beginning.
+        assertTrue(TextUtils.delimitedStringContains("gps,gpsx,network,mock", ',', "gps"));
+        assertTrue(TextUtils.delimitedStringContains("gps,network,mock", ',', "gps"));
+        // In middle, both without, before & after a false match.
+        assertTrue(TextUtils.delimitedStringContains("network,gps,mock", ',', "gps"));
+        assertTrue(TextUtils.delimitedStringContains("network,gps,gpsx,mock", ',', "gps"));
+        assertTrue(TextUtils.delimitedStringContains("network,gpsx,gps,mock", ',', "gps"));
+        // At the end.
+        assertTrue(TextUtils.delimitedStringContains("network,mock,gps", ',', "gps"));
+        assertTrue(TextUtils.delimitedStringContains("network,mock,gpsx,gps", ',', "gps"));
+        // Not present (but with a false match)
+        assertFalse(TextUtils.delimitedStringContains("network,mock,gpsx", ',', "gps"));
+    }
+
     /**
      * CharSequence wrapper for testing the cases where text is copied into
      * a char array instead of working from a String or a Spanned.