Merge "libcore: change removal of tasks in Timer#purge"
diff --git a/luni/src/main/java/libcore/net/UriCodec.java b/luni/src/main/java/libcore/net/UriCodec.java
index 17b875d..8efa366 100644
--- a/luni/src/main/java/libcore/net/UriCodec.java
+++ b/luni/src/main/java/libcore/net/UriCodec.java
@@ -49,9 +49,7 @@
     }
 
     private boolean isWhitelistedOrRetained(char c) {
-        return (isWhitelisted(c) || isRetained(c))
-                // Make sure % is not retained.
-                && (c != '%');
+        return isWhitelisted(c) || isRetained(c);
     }
 
     /**
@@ -183,6 +181,7 @@
             }
 
             if (c == ' ' && isRetained(' ')) {
+                flushEncodingCharBuffer(builder, encoder, cBuffer);
                 builder.append('+');
                 continue;
             }
diff --git a/luni/src/test/java/libcore/net/UriCodecTest.java b/luni/src/test/java/libcore/net/UriCodecTest.java
index 503bd8b..b831bd1 100644
--- a/luni/src/test/java/libcore/net/UriCodecTest.java
+++ b/luni/src/test/java/libcore/net/UriCodecTest.java
@@ -28,9 +28,7 @@
     private static final UriCodec CODEC = new UriCodec() {
         @Override
         protected boolean isRetained(char c) {
-            // Note: this is a dubious codec specifying to retain the escape character '%'.
-            // Testing that is not treated as retained anyway..
-            return c == '$' || c == '%';
+            return c == '$';
         }
     };
 
@@ -182,13 +180,10 @@
         assertEquals("ab%2F$%C4%82%2512%20",
                 CODEC.encode("ab/$\u0102%12 ", StandardCharsets.UTF_8));
 
-
         UriCodec withWhitespaceRetained = new UriCodec() {
             @Override
             protected boolean isRetained(char c) {
-                // Note: this is a dubious codec specifying to retain the escape character '%'.
-                // Testing that is not treated as retained anyway..
-                return c == '$' || c == '%' || c == ' ';
+                return c == '$' || c == ' ';
             }
         };
         // Whitespace is retained, convert to plus.
@@ -196,6 +191,18 @@
                 withWhitespaceRetained.encode("ab/$\u0102%12 ", StandardCharsets.UTF_8));
     }
 
+    /** Confirm that '%' can be retained, disabling '%' encoding. http://b/24806835 */
+    public void testEncode_percentRetained() {
+        UriCodec withPercentRetained = new UriCodec() {
+            @Override
+            protected boolean isRetained(char c) {
+                return c == '%';
+            }
+        };
+        // Percent is retained
+        assertEquals("ab%34%20", withPercentRetained.encode("ab%34 ", StandardCharsets.UTF_8));
+    }
+
     public void testEncode_partially_returnsPercentUnchanged() {
         StringBuilder stringBuilder = new StringBuilder();
         // Check it's really appending instead of returning a new builder.
@@ -229,6 +236,18 @@
                 CODEC.encode("ab/$\u0102%\u0840", StandardCharsets.UTF_8));
     }
 
+    // Last character needs encoding (make sure we are flushing the buffer with chars to encode).
+    public void testEncode_flushBufferBeforePlusFromSpace() {
+        UriCodec withSpaceRetained = new UriCodec() {
+            @Override
+            protected boolean isRetained(char c) {
+                return c == ' ';
+            }
+        };
+        assertEquals("%2F+",
+                withSpaceRetained.encode("/ ", StandardCharsets.UTF_8));
+    }
+
     public void testDecode_emptyString_returnsEmptyString() {
         assertEquals("", UriCodec.decode(""));
     }