Merge "Apply upstream OpenJDK 9 compilation fix to JapaneseDate."
diff --git a/dalvik/src/main/java/dalvik/system/DexFile.java b/dalvik/src/main/java/dalvik/system/DexFile.java
index bbf0d99..3cd10e3 100644
--- a/dalvik/src/main/java/dalvik/system/DexFile.java
+++ b/dalvik/src/main/java/dalvik/system/DexFile.java
@@ -496,11 +496,11 @@
throws FileNotFoundException;
/**
- * Returns the full file path of the optimized dex file {@code fileName}. The returned string
- * is the full file name including path of optimized dex file, if it exists.
+ * Returns the paths of the optimized files generated for {@code fileName}.
+ * If no optimized code exists the method returns null.
* @hide
*/
- public static native String getDexFileOutputPath(String fileName, String instructionSet)
+ public static native String[] getDexFileOutputPaths(String fileName, String instructionSet)
throws FileNotFoundException;
/**
diff --git a/expectations/brokentests.txt b/expectations/brokentests.txt
deleted file mode 100644
index 59546b5..0000000
--- a/expectations/brokentests.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-/*
- * TODO: Remove this file entirely.
- */
-[
-
-]
diff --git a/expectations/icebox.txt b/expectations/icebox.txt
deleted file mode 100644
index 59546b5..0000000
--- a/expectations/icebox.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-/*
- * TODO: Remove this file entirely.
- */
-[
-
-]
diff --git a/expectations/taggedtests.txt b/expectations/taggedtests.txt
deleted file mode 100644
index d13cdd4..0000000
--- a/expectations/taggedtests.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-/*
- * TODO: Remove this file entirely.
- */
-[
-]
diff --git a/luni/src/test/java/libcore/java/net/FtpURLConnectionTest.java b/luni/src/test/java/libcore/java/net/FtpURLConnectionTest.java
index 2f5400c..6588e2d 100644
--- a/luni/src/test/java/libcore/java/net/FtpURLConnectionTest.java
+++ b/luni/src/test/java/libcore/java/net/FtpURLConnectionTest.java
@@ -42,6 +42,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
@@ -55,10 +56,10 @@
public class FtpURLConnectionTest extends TestCase {
private static final String FILE_PATH = "test/file/for/FtpURLConnectionTest.txt";
- private static final String USER = "user";
- private static final String PASSWORD = "password";
private static final String SERVER_HOSTNAME = "localhost";
- private static final String USER_HOME_DIR = "/home/user";
+ private static final String VALID_USER = "user";
+ private static final String VALID_PASSWORD = "password";
+ private static final String VALID_USER_HOME_DIR = "/home/user";
private FakeFtpServer fakeFtpServer;
private UnixFakeFileSystem fileSystem;
@@ -68,10 +69,11 @@
super.setUp();
fakeFtpServer = new FakeFtpServer();
fakeFtpServer.setServerControlPort(0 /* allocate port number automatically */);
- fakeFtpServer.addUserAccount(new UserAccount(USER, PASSWORD, USER_HOME_DIR));
+ fakeFtpServer.addUserAccount(new UserAccount(VALID_USER, VALID_PASSWORD,
+ VALID_USER_HOME_DIR));
fileSystem = new UnixFakeFileSystem();
fakeFtpServer.setFileSystem(fileSystem);
- fileSystem.add(new DirectoryEntry(USER_HOME_DIR));
+ fileSystem.add(new DirectoryEntry(VALID_USER_HOME_DIR));
fakeFtpServer.start();
}
@@ -83,17 +85,45 @@
public void testInputUrl() throws Exception {
byte[] fileContents = "abcdef 1234567890".getBytes(UTF_8);
- URL fileUrl = addFileEntry(FILE_PATH, fileContents);
+ addFileEntry(FILE_PATH, fileContents);
+ URL fileUrl = getFileUrlWithCredentials(VALID_USER, VALID_PASSWORD, FILE_PATH);
URLConnection connection = fileUrl.openConnection();
assertContents(fileContents, connection.getInputStream());
}
+ public void testInputUrl_invalidUserOrPassword() throws Exception {
+ checkInputUrl_invalidUserOrPassword("wrong_user", VALID_PASSWORD);
+ checkInputUrl_invalidUserOrPassword(VALID_USER, "wrong password");
+ }
+
+ public void testInputUrl_missingPassword() throws Exception {
+ URL noPasswordUrl = getFileUrlWithCredentials(VALID_USER, null, FILE_PATH);
+ URLConnection noPasswordConnection = noPasswordUrl.openConnection();
+ try {
+ noPasswordConnection.getInputStream();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ private void checkInputUrl_invalidUserOrPassword(String user, String password)
+ throws IOException {
+ URL fileUrl = getFileUrlWithCredentials(user, password, FILE_PATH);
+ URLConnection connection = fileUrl.openConnection();
+ try {
+ connection.getInputStream();
+ fail();
+ } catch (sun.net.ftp.FtpLoginException expected) {
+ assertEquals("Invalid username/password", expected.getMessage());
+ }
+ }
+
public void testOutputUrl() throws Exception {
byte[] fileContents = "abcdef 1234567890".getBytes(UTF_8);
addFileEntry("test/output-url/existing file.txt", fileContents);
byte[] newFileContents = "contents of brand new file".getBytes(UTF_8);
String filePath = "test/output-url/file that is newly created.txt";
- URL fileUrl = new URL(getFileUrlString(filePath));
+ URL fileUrl = getFileUrlWithCredentials(VALID_USER, VALID_PASSWORD, filePath);
URLConnection connection = fileUrl.openConnection();
connection.setDoInput(false);
connection.setDoOutput(true);
@@ -218,7 +248,7 @@
}
private InputStream openFileSystemContents(String fileName) throws IOException {
- String fullFileName = USER_HOME_DIR + "/" + fileName;
+ String fullFileName = VALID_USER_HOME_DIR + "/" + fileName;
FileEntry entry = (FileEntry) fileSystem.getEntry(fullFileName);
assertNotNull("File must exist with name " + fullFileName, entry);
return entry.createInputStream();
@@ -243,18 +273,13 @@
}
}
- private String getFileUrlString(String filePath) {
+ private URL getFileUrlWithCredentials(String user, String password, String filePath) {
+ Objects.requireNonNull(user);
+ Objects.requireNonNull(filePath);
int serverPort = fakeFtpServer.getServerControlPort();
- String urlString = String.format(Locale.US, "ftp://%s:%s@%s:%s/%s",
- USER, PASSWORD, SERVER_HOSTNAME, serverPort, filePath);
- return urlString;
- }
-
- private URL addFileEntry(String filePath, byte[] fileContents) {
- FileEntry fileEntry = new FileEntry(USER_HOME_DIR + "/" + filePath);
- fileEntry.setContents(fileContents);
- fileSystem.add(fileEntry);
- String urlString = getFileUrlString(filePath);
+ String credentials = user + (password == null ? "" : (":" + password));
+ String urlString = String.format(Locale.US, "ftp://%s@%s:%s/%s",
+ credentials, SERVER_HOSTNAME, serverPort, filePath);
try {
return new URL(urlString);
} catch (MalformedURLException e) {
@@ -263,6 +288,13 @@
}
}
+ private URL addFileEntry(String filePath, byte[] fileContents) {
+ FileEntry fileEntry = new FileEntry(VALID_USER_HOME_DIR + "/" + filePath);
+ fileEntry.setContents(fileContents);
+ fileSystem.add(fileEntry);
+ return getFileUrlWithCredentials(VALID_USER, VALID_PASSWORD, filePath);
+ }
+
/**
* A {@link ProxySelector} that selects the same (given) Proxy for all URIs.
*/
diff --git a/luni/src/test/java/libcore/java/nio/channels/DatagramChannelMulticastTest.java b/luni/src/test/java/libcore/java/nio/channels/DatagramChannelMulticastTest.java
index ae45c0d..69110e0 100644
--- a/luni/src/test/java/libcore/java/nio/channels/DatagramChannelMulticastTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/DatagramChannelMulticastTest.java
@@ -711,6 +711,9 @@
/** Checks that block() works when the receiver is bound to the multicast group address */
public void test_block_filtersAsExpected_groupBind_ipv4() throws Exception {
+ if (!supportsMulticast) {
+ return;
+ }
InetAddress ipv4LocalAddress = getLocalIpv4Address(ipv4NetworkInterface);
test_block_filtersAsExpected(
ipv4LocalAddress /* senderBindAddress */,
@@ -721,6 +724,9 @@
/** Checks that block() works when the receiver is bound to the multicast group address */
public void test_block_filtersAsExpected_groupBind_ipv6() throws Exception {
+ if (!supportsMulticast) {
+ return;
+ }
InetAddress ipv6LocalAddress = getLocalIpv6Address(ipv6NetworkInterface);
test_block_filtersAsExpected(
ipv6LocalAddress /* senderBindAddress */,
@@ -731,6 +737,9 @@
/** Checks that block() works when the receiver is bound to the "any" address */
public void test_block_filtersAsExpected_anyBind_ipv4() throws Exception {
+ if (!supportsMulticast) {
+ return;
+ }
InetAddress ipv4LocalAddress = getLocalIpv4Address(ipv4NetworkInterface);
test_block_filtersAsExpected(
ipv4LocalAddress /* senderBindAddress */,
@@ -741,6 +750,9 @@
/** Checks that block() works when the receiver is bound to the "any" address */
public void test_block_filtersAsExpected_anyBind_ipv6() throws Exception {
+ if (!supportsMulticast) {
+ return;
+ }
InetAddress ipv6LocalAddress = getLocalIpv6Address(ipv6NetworkInterface);
test_block_filtersAsExpected(
ipv6LocalAddress /* senderBindAddress */,
@@ -753,9 +765,6 @@
InetAddress senderBindAddress, InetAddress receiverBindAddress,
InetAddress groupAddress, NetworkInterface networkInterface)
throws Exception {
- if (!supportsMulticast) {
- return;
- }
DatagramChannel sendingChannel = DatagramChannel.open();
// In order to block a sender the sender's address must be known. The sendingChannel is
// explicitly bound to a known, non-loopback address.
@@ -973,6 +982,9 @@
* address
*/
public void test_joinSourceSpecific_null() throws Exception {
+ if (!supportsMulticast) {
+ return;
+ }
InetAddress ipv4LocalAddress = getLocalIpv4Address(ipv4NetworkInterface);
test_joinSourceSpecific(
ipv4LocalAddress /* senderBindAddress */,
@@ -987,6 +999,9 @@
* address
*/
public void test_joinSourceSpecific_groupBind_ipv4() throws Exception {
+ if (!supportsMulticast) {
+ return;
+ }
InetAddress ipv4LocalAddress = getLocalIpv4Address(ipv4NetworkInterface);
test_joinSourceSpecific(
ipv4LocalAddress /* senderBindAddress */,
@@ -1001,6 +1016,9 @@
* address
*/
public void test_joinSourceSpecific_groupBind_ipv6() throws Exception {
+ if (!supportsMulticast) {
+ return;
+ }
InetAddress ipv6LocalAddress = getLocalIpv6Address(ipv6NetworkInterface);
test_joinSourceSpecific(
ipv6LocalAddress /* senderBindAddress */,
@@ -1012,6 +1030,9 @@
/** Checks that a source-specific join() works when the receiver is bound to the "any" address */
public void test_joinSourceSpecific_anyBind_ipv4() throws Exception {
+ if (!supportsMulticast) {
+ return;
+ }
InetAddress ipv4LocalAddress = getLocalIpv4Address(ipv4NetworkInterface);
test_joinSourceSpecific(
ipv4LocalAddress /* senderBindAddress */,
@@ -1023,6 +1044,9 @@
/** Checks that a source-specific join() works when the receiver is bound to the "any" address */
public void test_joinSourceSpecific_anyBind_ipv6() throws Exception {
+ if (!supportsMulticast) {
+ return;
+ }
InetAddress ipv6LocalAddress = getLocalIpv6Address(ipv6NetworkInterface);
test_joinSourceSpecific(
ipv6LocalAddress /* senderBindAddress */,
@@ -1044,9 +1068,6 @@
InetAddress senderBindAddress, InetAddress receiverBindAddress, InetAddress groupAddress,
InetAddress badSenderAddress, NetworkInterface networkInterface)
throws Exception {
- if (!supportsMulticast) {
- return;
- }
DatagramChannel sendingChannel = DatagramChannel.open();
// In order to be source-specific the sender's address must be known. The sendingChannel is
// explicitly bound to a known, non-loopback address.
diff --git a/luni/src/test/java/libcore/sun/net/util/IPAddressUtilTest.java b/luni/src/test/java/libcore/sun/net/util/IPAddressUtilTest.java
new file mode 100644
index 0000000..9294309
--- /dev/null
+++ b/luni/src/test/java/libcore/sun/net/util/IPAddressUtilTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 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.sun.net.util;
+
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+
+import static sun.net.util.IPAddressUtil.textToNumericFormatV4;
+
+public class IPAddressUtilTest extends TestCase {
+
+ public void test_textToNumericFormatV4_valid() {
+ assertBytesEquals(0, 0, 0, 0, textToNumericFormatV4("0.0.0.0"));
+ assertBytesEquals(1, 2, 3, 4, textToNumericFormatV4("1.2.3.4"));
+ assertBytesEquals(255, 255, 255, 255, textToNumericFormatV4("255.255.255.255"));
+ assertBytesEquals(123, 23, 255, 37, textToNumericFormatV4("123.23.255.37"));
+ assertBytesEquals(192, 168, 0, 42, textToNumericFormatV4("192.168.0.42"));
+ }
+
+ public void test_textToNumericFormatV4_invalid() {
+ // Wrong number of components
+ assertNull(textToNumericFormatV4("1"));
+ assertNull(textToNumericFormatV4("1.2"));
+ assertNull(textToNumericFormatV4("1.2.3"));
+ assertNull(textToNumericFormatV4("1.2.3.4.5"));
+
+ // Extra dots in various places
+ assertNull(textToNumericFormatV4("1..3.4"));
+ assertNull(textToNumericFormatV4("1..2.3.4"));
+ assertNull(textToNumericFormatV4(".1.2.3"));
+ assertNull(textToNumericFormatV4(".1.2.3.4"));
+ assertNull(textToNumericFormatV4("1.2.3.4."));
+
+ // Out of bounds values
+ assertNull(textToNumericFormatV4("256.2.3.4"));
+ assertNull(textToNumericFormatV4("1.256.3.4"));
+ assertNull(textToNumericFormatV4("1.2.256.4"));
+ assertNull(textToNumericFormatV4("1.2.3.256"));
+ assertNull(textToNumericFormatV4("1.-2.3.4"));
+ }
+
+ private static void assertBytesEquals(int a, int b, int c, int d, byte[] actual) {
+ byte[] expected = new byte[] { (byte) a, (byte) b, (byte) c, (byte) d };
+ assertTrue("Expected " + Arrays.toString(expected) + ", got " + Arrays.toString(actual),
+ Arrays.equals(expected, actual));
+ }
+
+}
diff --git a/ojluni/src/main/java/java/io/FilePermission.java b/ojluni/src/main/java/java/io/FilePermission.java
index 814a695..f9544c7 100644
--- a/ojluni/src/main/java/java/io/FilePermission.java
+++ b/ojluni/src/main/java/java/io/FilePermission.java
@@ -27,6 +27,8 @@
import java.security.*;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/java/lang/RuntimePermission.java b/ojluni/src/main/java/java/lang/RuntimePermission.java
index 19dff55..2d05ba1 100644
--- a/ojluni/src/main/java/java/lang/RuntimePermission.java
+++ b/ojluni/src/main/java/java/lang/RuntimePermission.java
@@ -27,6 +27,8 @@
import java.security.*;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/java/lang/SecurityManager.java b/ojluni/src/main/java/java/lang/SecurityManager.java
index 64f2d53..5ef885e 100644
--- a/ojluni/src/main/java/java/lang/SecurityManager.java
+++ b/ojluni/src/main/java/java/lang/SecurityManager.java
@@ -30,6 +30,10 @@
import java.io.FileDescriptor;
import java.net.InetAddress;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// SecurityManager can only check access by Java code, so it can be bypassed by using
+// native code. Applications should rely on Android permissions, process separation,
+// other other methods for security purposes.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/java/lang/reflect/ReflectPermission.java b/ojluni/src/main/java/java/lang/reflect/ReflectPermission.java
index 96a2d21..a5e5be1 100644
--- a/ojluni/src/main/java/java/lang/reflect/ReflectPermission.java
+++ b/ojluni/src/main/java/java/lang/reflect/ReflectPermission.java
@@ -25,6 +25,8 @@
package java.lang.reflect;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/java/net/NetPermission.java b/ojluni/src/main/java/java/net/NetPermission.java
index 8eb7892..b5a0eab 100644
--- a/ojluni/src/main/java/java/net/NetPermission.java
+++ b/ojluni/src/main/java/java/net/NetPermission.java
@@ -27,6 +27,8 @@
import java.security.*;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/java/net/SocketPermission.java b/ojluni/src/main/java/java/net/SocketPermission.java
index cdd9f15..2195ecc 100644
--- a/ojluni/src/main/java/java/net/SocketPermission.java
+++ b/ojluni/src/main/java/java/net/SocketPermission.java
@@ -28,6 +28,8 @@
import java.security.Permission;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/java/security/AccessControlContext.java b/ojluni/src/main/java/java/security/AccessControlContext.java
index e95cff2..506898f 100644
--- a/ojluni/src/main/java/java/security/AccessControlContext.java
+++ b/ojluni/src/main/java/java/security/AccessControlContext.java
@@ -30,6 +30,8 @@
import java.util.List;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/java/security/AccessController.java b/ojluni/src/main/java/java/security/AccessController.java
index b4d544c..ad844ba 100644
--- a/ojluni/src/main/java/java/security/AccessController.java
+++ b/ojluni/src/main/java/java/security/AccessController.java
@@ -27,6 +27,8 @@
package java.security;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/java/security/AllPermission.java b/ojluni/src/main/java/java/security/AllPermission.java
index 20e452b..61bcaea 100644
--- a/ojluni/src/main/java/java/security/AllPermission.java
+++ b/ojluni/src/main/java/java/security/AllPermission.java
@@ -25,6 +25,8 @@
package java.security;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/java/security/AuthProvider.java b/ojluni/src/main/java/java/security/AuthProvider.java
index 23ddb0a..e87daa9 100644
--- a/ojluni/src/main/java/java/security/AuthProvider.java
+++ b/ojluni/src/main/java/java/security/AuthProvider.java
@@ -29,6 +29,8 @@
import javax.security.auth.login.LoginException;
import javax.security.auth.callback.CallbackHandler;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/java/security/BasicPermission.java b/ojluni/src/main/java/java/security/BasicPermission.java
index 0a069b0..1836b10 100644
--- a/ojluni/src/main/java/java/security/BasicPermission.java
+++ b/ojluni/src/main/java/java/security/BasicPermission.java
@@ -25,6 +25,8 @@
package java.security;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/java/security/CodeSource.java b/ojluni/src/main/java/java/security/CodeSource.java
index 7396d91..fce8aa1 100644
--- a/ojluni/src/main/java/java/security/CodeSource.java
+++ b/ojluni/src/main/java/java/security/CodeSource.java
@@ -35,6 +35,8 @@
import java.io.IOException;
import java.security.cert.*;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/java/security/DomainCombiner.java b/ojluni/src/main/java/java/security/DomainCombiner.java
index 5426bf6..e9c010f 100644
--- a/ojluni/src/main/java/java/security/DomainCombiner.java
+++ b/ojluni/src/main/java/java/security/DomainCombiner.java
@@ -25,6 +25,8 @@
package java.security;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/java/security/Permission.java b/ojluni/src/main/java/java/security/Permission.java
index 8d170c0..54163b9 100644
--- a/ojluni/src/main/java/java/security/Permission.java
+++ b/ojluni/src/main/java/java/security/Permission.java
@@ -25,6 +25,8 @@
package java.security;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/java/security/PermissionCollection.java b/ojluni/src/main/java/java/security/PermissionCollection.java
index e356524..f1015e3 100644
--- a/ojluni/src/main/java/java/security/PermissionCollection.java
+++ b/ojluni/src/main/java/java/security/PermissionCollection.java
@@ -27,6 +27,8 @@
import java.util.*;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/java/security/Permissions.java b/ojluni/src/main/java/java/security/Permissions.java
index 7411e06..9796f01 100644
--- a/ojluni/src/main/java/java/security/Permissions.java
+++ b/ojluni/src/main/java/java/security/Permissions.java
@@ -29,6 +29,8 @@
import java.io.Serializable;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/java/security/Policy.java b/ojluni/src/main/java/java/security/Policy.java
index 71c6cd0..cb7fb3b 100644
--- a/ojluni/src/main/java/java/security/Policy.java
+++ b/ojluni/src/main/java/java/security/Policy.java
@@ -29,9 +29,8 @@
import java.util.Enumeration;
-
-/* Not used on Android, removed most of the code and made the class methods no-ops */
-
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/java/security/PrivilegedAction.java b/ojluni/src/main/java/java/security/PrivilegedAction.java
index bef7e44..5844b4d 100644
--- a/ojluni/src/main/java/java/security/PrivilegedAction.java
+++ b/ojluni/src/main/java/java/security/PrivilegedAction.java
@@ -26,6 +26,8 @@
package java.security;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/java/security/PrivilegedActionException.java b/ojluni/src/main/java/java/security/PrivilegedActionException.java
index 83d855a..b1eb28f 100644
--- a/ojluni/src/main/java/java/security/PrivilegedActionException.java
+++ b/ojluni/src/main/java/java/security/PrivilegedActionException.java
@@ -25,6 +25,8 @@
package java.security;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*
diff --git a/ojluni/src/main/java/java/security/PrivilegedExceptionAction.java b/ojluni/src/main/java/java/security/PrivilegedExceptionAction.java
index bae883e..4023150 100644
--- a/ojluni/src/main/java/java/security/PrivilegedExceptionAction.java
+++ b/ojluni/src/main/java/java/security/PrivilegedExceptionAction.java
@@ -26,6 +26,8 @@
package java.security;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/java/security/ProtectionDomain.java b/ojluni/src/main/java/java/security/ProtectionDomain.java
index 7cacc35..9e117a6 100644
--- a/ojluni/src/main/java/java/security/ProtectionDomain.java
+++ b/ojluni/src/main/java/java/security/ProtectionDomain.java
@@ -26,6 +26,8 @@
package java.security;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/java/security/SecurityPermission.java b/ojluni/src/main/java/java/security/SecurityPermission.java
index 7d2ec47..f02755c 100644
--- a/ojluni/src/main/java/java/security/SecurityPermission.java
+++ b/ojluni/src/main/java/java/security/SecurityPermission.java
@@ -25,6 +25,8 @@
package java.security;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/java/security/UnresolvedPermission.java b/ojluni/src/main/java/java/security/UnresolvedPermission.java
index 6d97fbe..6f3bf4a 100644
--- a/ojluni/src/main/java/java/security/UnresolvedPermission.java
+++ b/ojluni/src/main/java/java/security/UnresolvedPermission.java
@@ -34,6 +34,8 @@
import java.lang.reflect.*;
import java.security.cert.*;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/java/security/acl/Permission.java b/ojluni/src/main/java/java/security/acl/Permission.java
index cd84a48..72412de 100644
--- a/ojluni/src/main/java/java/security/acl/Permission.java
+++ b/ojluni/src/main/java/java/security/acl/Permission.java
@@ -25,6 +25,8 @@
package java.security.acl;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/java/sql/SQLPermission.java b/ojluni/src/main/java/java/sql/SQLPermission.java
index 050f763..505202c 100644
--- a/ojluni/src/main/java/java/sql/SQLPermission.java
+++ b/ojluni/src/main/java/java/sql/SQLPermission.java
@@ -27,6 +27,8 @@
import java.security.*;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/java/util/PropertyPermission.java b/ojluni/src/main/java/java/util/PropertyPermission.java
index 8b73ce1..ac7b521 100644
--- a/ojluni/src/main/java/java/util/PropertyPermission.java
+++ b/ojluni/src/main/java/java/util/PropertyPermission.java
@@ -27,6 +27,8 @@
import java.security.*;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/java/util/logging/LoggingPermission.java b/ojluni/src/main/java/java/util/logging/LoggingPermission.java
index 23c5432..5cea7a5 100644
--- a/ojluni/src/main/java/java/util/logging/LoggingPermission.java
+++ b/ojluni/src/main/java/java/util/logging/LoggingPermission.java
@@ -26,6 +26,8 @@
package java.util.logging;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/javax/crypto/CryptoAllPermission.java b/ojluni/src/main/java/javax/crypto/CryptoAllPermission.java
index 24268b8..8a973fc 100644
--- a/ojluni/src/main/java/javax/crypto/CryptoAllPermission.java
+++ b/ojluni/src/main/java/javax/crypto/CryptoAllPermission.java
@@ -25,6 +25,8 @@
package javax.crypto;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/javax/crypto/CryptoPermission.java b/ojluni/src/main/java/javax/crypto/CryptoPermission.java
index ec09287..56973b6 100644
--- a/ojluni/src/main/java/javax/crypto/CryptoPermission.java
+++ b/ojluni/src/main/java/javax/crypto/CryptoPermission.java
@@ -28,6 +28,8 @@
import java.security.*;
import java.security.spec.AlgorithmParameterSpec;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/javax/crypto/CryptoPermissions.java b/ojluni/src/main/java/javax/crypto/CryptoPermissions.java
index 0e62b18..eef08b7 100644
--- a/ojluni/src/main/java/javax/crypto/CryptoPermissions.java
+++ b/ojluni/src/main/java/javax/crypto/CryptoPermissions.java
@@ -31,6 +31,8 @@
import java.io.InputStream;
import java.io.IOException;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/javax/crypto/CryptoPolicyParser.java b/ojluni/src/main/java/javax/crypto/CryptoPolicyParser.java
index a5a3818..10bdc55 100644
--- a/ojluni/src/main/java/javax/crypto/CryptoPolicyParser.java
+++ b/ojluni/src/main/java/javax/crypto/CryptoPolicyParser.java
@@ -29,6 +29,8 @@
import java.security.GeneralSecurityException;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/javax/crypto/JceSecurityManager.java b/ojluni/src/main/java/javax/crypto/JceSecurityManager.java
index 38d9f19..aafaa58 100644
--- a/ojluni/src/main/java/javax/crypto/JceSecurityManager.java
+++ b/ojluni/src/main/java/javax/crypto/JceSecurityManager.java
@@ -25,6 +25,8 @@
package javax.crypto;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/javax/net/ssl/HttpsURLConnection.java b/ojluni/src/main/java/javax/net/ssl/HttpsURLConnection.java
index 9660643..ec4254a 100644
--- a/ojluni/src/main/java/javax/net/ssl/HttpsURLConnection.java
+++ b/ojluni/src/main/java/javax/net/ssl/HttpsURLConnection.java
@@ -178,6 +178,10 @@
}
}
+ // BEGIN Android-changed: Use lazily-created OkHttp hostname verifier
+ // The RI default hostname verifier is a static member of the class, which means
+ // it's created when the class is initialized. As well, its default verifier
+ // just fails all verification attempts, whereas we use OkHttp's verifier.
/*
* Holds the default instance so class preloading doesn't create an instance of
* it.
@@ -207,6 +211,7 @@
* The <code>hostnameVerifier</code> for this object.
*/
protected HostnameVerifier hostnameVerifier;
+ // END Android-changed: Use lazily-created OkHttp hostname verifier
/**
* Sets the default <code>HostnameVerifier</code> inherited by a
@@ -279,6 +284,7 @@
* @see #setDefaultHostnameVerifier(HostnameVerifier)
*/
public HostnameVerifier getHostnameVerifier() {
+ // Android-added: Use the default verifier if none is set
if (hostnameVerifier == null) {
hostnameVerifier = NoPreloadHolder.defaultHostnameVerifier;
}
diff --git a/ojluni/src/main/java/javax/net/ssl/SNIServerName.java b/ojluni/src/main/java/javax/net/ssl/SNIServerName.java
index c5bb253..1ef9d05 100644
--- a/ojluni/src/main/java/javax/net/ssl/SNIServerName.java
+++ b/ojluni/src/main/java/javax/net/ssl/SNIServerName.java
@@ -210,3 +210,4 @@
return sb.toString();
}
}
+
diff --git a/ojluni/src/main/java/javax/net/ssl/SSLContext.java b/ojluni/src/main/java/javax/net/ssl/SSLContext.java
index 71cfc23..4105282 100644
--- a/ojluni/src/main/java/javax/net/ssl/SSLContext.java
+++ b/ojluni/src/main/java/javax/net/ssl/SSLContext.java
@@ -178,7 +178,7 @@
* @return the new <code>SSLContext</code> object.
*
* @exception NoSuchAlgorithmException if no Provider supports a
- * TrustManagerFactorySpi implementation for the
+ * SSLContextSpi implementation for the
* specified protocol.
* @exception NullPointerException if protocol is null.
*
diff --git a/ojluni/src/main/java/javax/net/ssl/SSLEngine.java b/ojluni/src/main/java/javax/net/ssl/SSLEngine.java
index aa15ccc..20d7fa9 100644
--- a/ojluni/src/main/java/javax/net/ssl/SSLEngine.java
+++ b/ojluni/src/main/java/javax/net/ssl/SSLEngine.java
@@ -329,6 +329,7 @@
* is saved. All future delegated tasks will be processed using this
* context: that is, all access control decisions will be made using the
* context captured at engine creation.
+ *
* <HR>
*
* <B>Concurrency Notes</B>:
@@ -1183,7 +1184,7 @@
* If this <code>SSLEngine</code> has not yet started its initial
* handshake, this method will automatically start the handshake.
* <P>
- * This method will attempt to produce one SSL/TLS packet, and will
+ * This method will attempt to produce SSL/TLS records, and will
* consume as much source data as possible, but will never consume
* more than the sum of the bytes remaining in each buffer. Each
* <code>ByteBuffer</code>'s position is updated to reflect the
diff --git a/ojluni/src/main/java/javax/net/ssl/SSLPermission.java b/ojluni/src/main/java/javax/net/ssl/SSLPermission.java
index 0f6b5db..a23e1ce 100644
--- a/ojluni/src/main/java/javax/net/ssl/SSLPermission.java
+++ b/ojluni/src/main/java/javax/net/ssl/SSLPermission.java
@@ -27,6 +27,8 @@
import java.security.*;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/javax/security/auth/AuthPermission.java b/ojluni/src/main/java/javax/security/auth/AuthPermission.java
index 651e9a3..71c3aaf 100644
--- a/ojluni/src/main/java/javax/security/auth/AuthPermission.java
+++ b/ojluni/src/main/java/javax/security/auth/AuthPermission.java
@@ -25,6 +25,8 @@
package javax.security.auth;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/javax/security/auth/PrivateCredentialPermission.java b/ojluni/src/main/java/javax/security/auth/PrivateCredentialPermission.java
index ca52a1b2..9733932 100644
--- a/ojluni/src/main/java/javax/security/auth/PrivateCredentialPermission.java
+++ b/ojluni/src/main/java/javax/security/auth/PrivateCredentialPermission.java
@@ -29,6 +29,8 @@
import java.security.Permission;
import java.security.Principal;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/javax/security/auth/SubjectDomainCombiner.java b/ojluni/src/main/java/javax/security/auth/SubjectDomainCombiner.java
index 8e07d4b..868ed5f 100644
--- a/ojluni/src/main/java/javax/security/auth/SubjectDomainCombiner.java
+++ b/ojluni/src/main/java/javax/security/auth/SubjectDomainCombiner.java
@@ -27,6 +27,8 @@
import java.security.ProtectionDomain;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/jdk/net/NetworkPermission.java b/ojluni/src/main/java/jdk/net/NetworkPermission.java
index 420c74f..96d7a8e 100644
--- a/ojluni/src/main/java/jdk/net/NetworkPermission.java
+++ b/ojluni/src/main/java/jdk/net/NetworkPermission.java
@@ -27,6 +27,8 @@
import java.security.BasicPermission;
+// Android-changed: Stubbed the implementation. Android doesn't support SecurityManager.
+// See comments in java.lang.SecurityManager for details.
/**
* Legacy security code; do not use.
*/
diff --git a/ojluni/src/main/java/sun/net/ExtendedOptionsImpl.java b/ojluni/src/main/java/sun/net/ExtendedOptionsImpl.java
index 94005e0..0b58a99 100644
--- a/ojluni/src/main/java/sun/net/ExtendedOptionsImpl.java
+++ b/ojluni/src/main/java/sun/net/ExtendedOptionsImpl.java
@@ -43,6 +43,17 @@
*/
public class ExtendedOptionsImpl {
+ // Android-removed: System.loadLibrary("net") is not available on Android.
+ /*
+ static {
+ AccessController.doPrivileged((PrivilegedAction<Void>)() -> {
+ System.loadLibrary("net");
+ return null;
+ });
+ init();
+ }
+ */
+
private ExtendedOptionsImpl() {}
public static void checkSetOptionPermission(SocketOption<?> option) {
@@ -71,6 +82,9 @@
}
}
+ // Android-removed: Native initialization logic that doesn't exist on Android.
+ // private static native void init();
+
/*
* Extension native implementations
*
diff --git a/ojluni/src/main/java/sun/net/NetHooks.java b/ojluni/src/main/java/sun/net/NetHooks.java
index 05100d9..153096e 100644
--- a/ojluni/src/main/java/sun/net/NetHooks.java
+++ b/ojluni/src/main/java/sun/net/NetHooks.java
@@ -38,43 +38,45 @@
public final class NetHooks {
- // Android-removed: No SDP support.
- // /**
- // * A provider with hooks to allow sockets be converted prior to binding or
- // * connecting a TCP socket.
- // *
- // * <p> Concrete implementations of this class should define a zero-argument
- // * constructor and implement the abstract methods specified below.
- // */
- // public static abstract class Provider {
- // /**
- // * Initializes a new instance of this class.
- // */
- // protected Provider() {}
- //
- // /**
- // * Invoked prior to binding a TCP socket.
- // */
- // public abstract void implBeforeTcpBind(FileDescriptor fdObj,
- // InetAddress address,
- // int port)
- // throws IOException;
- //
- // /**
- // * Invoked prior to connecting an unbound TCP socket.
- // */
- // public abstract void implBeforeTcpConnect(FileDescriptor fdObj,
- // InetAddress address,
- // int port)
- // throws IOException;
- // }
+ // Android-removed: Android doesn't support Session Description Protocol (SDP).
+ /*
+ /**
+ * A provider with hooks to allow sockets be converted prior to binding or
+ * connecting a TCP socket.
+ *
+ * <p> Concrete implementations of this class should define a zero-argument
+ * constructor and implement the abstract methods specified below.
+ *
+ public static abstract class Provider {
+ /**
+ * Initializes a new instance of this class.
+ *
+ protected Provider() {}
+
+ /**
+ * Invoked prior to binding a TCP socket.
+ *
+ public abstract void implBeforeTcpBind(FileDescriptor fdObj,
+ InetAddress address,
+ int port)
+ throws IOException;
+
+ /**
+ * Invoked prior to connecting an unbound TCP socket.
+ *
+ public abstract void implBeforeTcpConnect(FileDescriptor fdObj,
+ InetAddress address,
+ int port)
+ throws IOException;
+ }
+ */
/**
* For now, we load the SDP provider on Solaris. In the future this may
* be changed to use the ServiceLoader facility to allow the deployment of
* other providers.
*/
- // Android-removed: No SDP support
+ // Android-removed: Android doesn't support Session Description Protocol (SDP).
// private static final Provider provider = new sun.net.sdp.SdpProvider();
/**
@@ -85,7 +87,7 @@
int port)
throws IOException
{
- // Android-removed: No SDP support
+ // Android-removed: Android doesn't support Session Description Protocol (SDP).
// provider.implBeforeTcpBind(fdObj, address, port);
}
diff --git a/ojluni/src/main/java/sun/net/NetworkClient.java b/ojluni/src/main/java/sun/net/NetworkClient.java
index 1b311bc..0f76bf6 100644
--- a/ojluni/src/main/java/sun/net/NetworkClient.java
+++ b/ojluni/src/main/java/sun/net/NetworkClient.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -139,7 +139,7 @@
serverSocket.getOutputStream()),
true, encoding);
} catch (UnsupportedEncodingException e) {
- throw new InternalError(encoding +"encoding not found");
+ throw new InternalError(encoding +"encoding not found", e);
}
serverInput = new BufferedInputStream(serverSocket.getInputStream());
}
@@ -244,7 +244,7 @@
* Sets the read timeout.
*
* Note: Public URLConnection (and protocol specific implementations)
- * protect against negative timeout values being set. This implemenation,
+ * protect against negative timeout values being set. This implementation,
* and protocol specific implementations, use -1 to represent the default
* read timeout.
*
diff --git a/ojluni/src/main/java/sun/net/TelnetOutputStream.java b/ojluni/src/main/java/sun/net/TelnetOutputStream.java
index 432e975..74f6c9c 100644
--- a/ojluni/src/main/java/sun/net/TelnetOutputStream.java
+++ b/ojluni/src/main/java/sun/net/TelnetOutputStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2002, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -81,7 +81,7 @@
}
/**
- * set the stickyCRLF flag. Tells wether the terminal considers CRLF as a single
+ * set the stickyCRLF flag. Tells whether the terminal considers CRLF as a single
* char.
*
* @param on the <code>boolean</code> to set the flag to.
diff --git a/ojluni/src/main/java/sun/net/spi/DefaultProxySelector.java b/ojluni/src/main/java/sun/net/spi/DefaultProxySelector.java
index 6ba9b9a..fac57e2 100644
--- a/ojluni/src/main/java/sun/net/spi/DefaultProxySelector.java
+++ b/ojluni/src/main/java/sun/net/spi/DefaultProxySelector.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -33,13 +33,14 @@
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
-import java.util.StringTokenizer;
import java.io.IOException;
-import sun.misc.RegexpPool;
import java.security.AccessController;
import java.security.PrivilegedAction;
+import java.util.StringJoiner;
+import java.util.regex.Pattern;
import sun.net.NetProperties;
import sun.net.SocksProxy;
+import static java.util.regex.Pattern.quote;
/**
* Supports proxy settings using system properties This proxy selector
@@ -87,9 +88,31 @@
private static boolean hasSystemProxies = false;
+ // Android-removed: Nonfunctional init logic: "net" library does not exist on Android.
+ /*
+ static {
+ final String key = "java.net.useSystemProxies";
+ Boolean b = AccessController.doPrivileged(
+ new PrivilegedAction<Boolean>() {
+ public Boolean run() {
+ return NetProperties.getBoolean(key);
+ }});
+ if (b != null && b.booleanValue()) {
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction<Void>() {
+ public Void run() {
+ System.loadLibrary("net");
+ return null;
+ }
+ });
+ hasSystemProxies = init();
+ }
+ }
+ */
+
/**
* How to deal with "non proxy hosts":
- * since we do have to generate a RegexpPool we don't want to do that if
+ * since we do have to generate a pattern we don't want to do that if
* it's not necessary. Therefore we do cache the result, on a per-protocol
* basis, and change it only when the "source", i.e. the system property,
* did change.
@@ -101,17 +124,19 @@
static final String defStringVal = "localhost|127.*|[::1]|0.0.0.0|[::0]";
String hostsSource;
- RegexpPool hostsPool;
+ Pattern pattern;
final String property;
final String defaultVal;
static NonProxyInfo ftpNonProxyInfo = new NonProxyInfo("ftp.nonProxyHosts", null, null, defStringVal);
static NonProxyInfo httpNonProxyInfo = new NonProxyInfo("http.nonProxyHosts", null, null, defStringVal);
+ static NonProxyInfo socksNonProxyInfo = new NonProxyInfo("socksNonProxyHosts", null, null, defStringVal);
+ // Android-changed: Different NonProxyInfo flags for https hosts vs. http.
static NonProxyInfo httpsNonProxyInfo = new NonProxyInfo("https.nonProxyHosts", null, null, defStringVal);
- NonProxyInfo(String p, String s, RegexpPool pool, String d) {
+ NonProxyInfo(String p, String s, Pattern pattern, String d) {
property = p;
hostsSource = s;
- hostsPool = pool;
+ this.pattern = pattern;
defaultVal = d;
}
}
@@ -166,11 +191,13 @@
} else if ("https".equalsIgnoreCase(protocol)) {
// HTTPS uses the same property as HTTP, for backward
// compatibility
- //
- // Android-changed: Allow a different set of flags for https hosts.
+ // Android-changed: Different NonProxyInfo flags for https hosts vs. http.
+ // pinfo = NonProxyInfo.httpNonProxyInfo;
pinfo = NonProxyInfo.httpsNonProxyInfo;
} else if ("ftp".equalsIgnoreCase(protocol)) {
pinfo = NonProxyInfo.ftpNonProxyInfo;
+ } else if ("socket".equalsIgnoreCase(protocol)) {
+ pinfo = NonProxyInfo.socksNonProxyInfo;
}
/**
@@ -214,7 +241,20 @@
* settings (Gnome & Windows) if we were
* instructed to.
*/
- // Android-changed, hasSystemProxies is always false
+ // Android-removed: Dead code, hasSystemProxies is always false.
+ /*
+ if (hasSystemProxies) {
+ String sproto;
+ if (proto.equalsIgnoreCase("socket"))
+ sproto = "socks";
+ else
+ sproto = proto;
+ Proxy sproxy = getSystemProxy(sproto, urlhost);
+ if (sproxy != null) {
+ return sproxy;
+ }
+ }
+ */
return Proxy.NO_PROXY;
}
// If a Proxy Host is defined for that protocol
@@ -227,7 +267,7 @@
nphosts = nprop.defaultVal;
} else {
nprop.hostsSource = null;
- nprop.hostsPool = null;
+ nprop.pattern = null;
}
} else if (nphosts.length() != 0) {
// add the required default patterns
@@ -238,20 +278,11 @@
}
if (nphosts != null) {
if (!nphosts.equals(nprop.hostsSource)) {
- RegexpPool pool = new RegexpPool();
- StringTokenizer st = new StringTokenizer(nphosts, "|", false);
- try {
- while (st.hasMoreTokens()) {
- pool.add(st.nextToken().toLowerCase(), Boolean.TRUE);
- }
- } catch (sun.misc.REException ex) {
- }
- nprop.hostsPool = pool;
+ nprop.pattern = toPattern(nphosts);
nprop.hostsSource = nphosts;
}
}
- if (nprop.hostsPool != null &&
- nprop.hostsPool.match(urlhost) != null) {
+ if (shouldNotUseProxyFor(nprop.pattern, urlhost)) {
return Proxy.NO_PROXY;
}
}
@@ -324,4 +355,55 @@
return -1;
}
}
+
+ // Android-removed: Native logic not available/used on Android.
+ /*
+ private native static boolean init();
+ private synchronized native Proxy getSystemProxy(String protocol, String host);
+ */
+
+ /**
+ * @return {@code true} if given this pattern for non-proxy hosts and this
+ * urlhost the proxy should NOT be used to access this urlhost
+ */
+ static boolean shouldNotUseProxyFor(Pattern pattern, String urlhost) {
+ if (pattern == null || urlhost.isEmpty())
+ return false;
+ boolean matches = pattern.matcher(urlhost).matches();
+ return matches;
+ }
+
+ /**
+ * @param mask non-null mask
+ * @return {@link java.util.regex.Pattern} corresponding to this mask
+ * or {@code null} in case mask should not match anything
+ */
+ static Pattern toPattern(String mask) {
+ boolean disjunctionEmpty = true;
+ StringJoiner joiner = new StringJoiner("|");
+ for (String disjunct : mask.split("\\|")) {
+ if (disjunct.isEmpty())
+ continue;
+ disjunctionEmpty = false;
+ String regex = disjunctToRegex(disjunct.toLowerCase());
+ joiner.add(regex);
+ }
+ return disjunctionEmpty ? null : Pattern.compile(joiner.toString());
+ }
+
+ /**
+ * @param disjunct non-null mask disjunct
+ * @return java regex string corresponding to this mask
+ */
+ static String disjunctToRegex(String disjunct) {
+ String regex;
+ if (disjunct.startsWith("*")) {
+ regex = ".*" + quote(disjunct.substring(1));
+ } else if (disjunct.endsWith("*")) {
+ regex = quote(disjunct.substring(0, disjunct.length() - 1)) + ".*";
+ } else {
+ regex = quote(disjunct);
+ }
+ return regex;
+ }
}
diff --git a/ojluni/src/main/java/sun/net/util/IPAddressUtil.java b/ojluni/src/main/java/sun/net/util/IPAddressUtil.java
index 7ca0f12..4a58e5f 100644
--- a/ojluni/src/main/java/sun/net/util/IPAddressUtil.java
+++ b/ojluni/src/main/java/sun/net/util/IPAddressUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -37,89 +37,84 @@
* @param src a String representing an IPv4 address in standard format
* @return a byte array representing the IPv4 numeric address
*/
+ @SuppressWarnings("fallthrough")
public static byte[] textToNumericFormatV4(String src)
{
- if (src.length() == 0) {
+ byte[] res = new byte[INADDR4SZ];
+
+ long tmpValue = 0;
+ int currByte = 0;
+ boolean newOctet = true;
+
+ int len = src.length();
+ if (len == 0 || len > 15) {
return null;
}
-
- byte[] res = new byte[INADDR4SZ];
- String[] s = src.split("\\.", -1);
- long val;
- try {
- switch(s.length) {
- // BEGIN Android-removed
- /*
- case 1:
- // When only one part is given, the value is stored directly in
- // the network address without any byte rearrangement.
-
- val = Long.parseLong(s[0]);
- if (val < 0 || val > 0xffffffffL)
+ /*
+ * When only one part is given, the value is stored directly in
+ * the network address without any byte rearrangement.
+ *
+ * When a two part address is supplied, the last part is
+ * interpreted as a 24-bit quantity and placed in the right
+ * most three bytes of the network address. This makes the
+ * two part address format convenient for specifying Class A
+ * network addresses as net.host.
+ *
+ * When a three part address is specified, the last part is
+ * interpreted as a 16-bit quantity and placed in the right
+ * most two bytes of the network address. This makes the
+ * three part address format convenient for specifying
+ * Class B net- work addresses as 128.net.host.
+ *
+ * When four parts are specified, each is interpreted as a
+ * byte of data and assigned, from left to right, to the
+ * four bytes of an IPv4 address.
+ *
+ * We determine and parse the leading parts, if any, as single
+ * byte values in one pass directly into the resulting byte[],
+ * then the remainder is treated as a 8-to-32-bit entity and
+ * translated into the remaining bytes in the array.
+ */
+ for (int i = 0; i < len; i++) {
+ char c = src.charAt(i);
+ if (c == '.') {
+ if (newOctet || tmpValue < 0 || tmpValue > 0xff || currByte == 3) {
return null;
- res[0] = (byte) ((val >> 24) & 0xff);
- res[1] = (byte) (((val & 0xffffff) >> 16) & 0xff);
- res[2] = (byte) (((val & 0xffff) >> 8) & 0xff);
- res[3] = (byte) (val & 0xff);
- break;
- case 2:
- // When a two part address is supplied, the last part is
- // interpreted as a 24-bit quantity and placed in the right
- // most three bytes of the network address. This makes the
- // two part address format convenient for specifying Class A
- // network addresses as net.host.
-
- val = Integer.parseInt(s[0]);
- if (val < 0 || val > 0xff)
- return null;
- res[0] = (byte) (val & 0xff);
- val = Integer.parseInt(s[1]);
- if (val < 0 || val > 0xffffff)
- return null;
- res[1] = (byte) ((val >> 16) & 0xff);
- res[2] = (byte) (((val & 0xffff) >> 8) &0xff);
- res[3] = (byte) (val & 0xff);
- break;
- case 3:
- //
- // When a three part address is specified, the last part is
- // interpreted as a 16-bit quantity and placed in the right
- // most two bytes of the network address. This makes the
- // three part address format convenient for specifying
- // Class B net- work addresses as 128.net.host.
- for (int i = 0; i < 2; i++) {
- val = Integer.parseInt(s[i]);
- if (val < 0 || val > 0xff)
- return null;
- res[i] = (byte) (val & 0xff);
}
- val = Integer.parseInt(s[2]);
- if (val < 0 || val > 0xffff)
+ res[currByte++] = (byte) (tmpValue & 0xff);
+ tmpValue = 0;
+ newOctet = true;
+ } else {
+ int digit = Character.digit(c, 10);
+ if (digit < 0) {
return null;
- res[2] = (byte) ((val >> 8) & 0xff);
- res[3] = (byte) (val & 0xff);
- break;
- */
- // END Android-removed
- case 4:
- /*
- * When four parts are specified, each is interpreted as a
- * byte of data and assigned, from left to right, to the
- * four bytes of an IPv4 address.
- */
- for (int i = 0; i < 4; i++) {
- val = Integer.parseInt(s[i]);
- if (val < 0 || val > 0xff)
- return null;
- res[i] = (byte) (val & 0xff);
}
- break;
- default:
- return null;
+ tmpValue *= 10;
+ tmpValue += digit;
+ newOctet = false;
}
- } catch(NumberFormatException e) {
+ }
+ if (newOctet || tmpValue < 0 || tmpValue >= (1L << ((4 - currByte) * 8))) {
return null;
}
+ switch (currByte) {
+ // BEGIN Android-changed: Require all four parts to be given for an IPv4 address.
+ /*
+ case 0:
+ res[0] = (byte) ((tmpValue >> 24) & 0xff);
+ case 1:
+ res[1] = (byte) ((tmpValue >> 16) & 0xff);
+ case 2:
+ res[2] = (byte) ((tmpValue >> 8) & 0xff);
+ */
+ case 0:
+ case 1:
+ case 2:
+ return null;
+ // END Android-changed: Require all four parts to be given for an IPv4 address.
+ case 3:
+ res[3] = (byte) ((tmpValue >> 0) & 0xff);
+ }
return res;
}
diff --git a/ojluni/src/main/java/sun/net/util/URLUtil.java b/ojluni/src/main/java/sun/net/util/URLUtil.java
index 08c1e43..c25defb 100644
--- a/ojluni/src/main/java/sun/net/util/URLUtil.java
+++ b/ojluni/src/main/java/sun/net/util/URLUtil.java
@@ -76,5 +76,29 @@
return strForm.toString();
}
+
+ // Android-removed: Android doesn't support SecurityManager, so Permissions logic is dead code.
+ /*
+ public static Permission getConnectPermission(URL url) throws IOException {
+ String urlStringLowerCase = url.toString().toLowerCase();
+ if (urlStringLowerCase.startsWith("http:") || urlStringLowerCase.startsWith("https:")) {
+ return getURLConnectPermission(url);
+ } else if (urlStringLowerCase.startsWith("jar:http:") || urlStringLowerCase.startsWith("jar:https:")) {
+ String urlString = url.toString();
+ int bangPos = urlString.indexOf("!/");
+ urlString = urlString.substring(4, bangPos > -1 ? bangPos : urlString.length());
+ URL u = new URL(urlString);
+ return getURLConnectPermission(u);
+ // If protocol is HTTP or HTTPS than use URLPermission object
+ } else {
+ return url.openConnection().getPermission();
+ }
+ }
+
+ private static Permission getURLConnectPermission(URL url) {
+ String urlString = url.getProtocol() + "://" + url.getAuthority() + url.getPath();
+ return new URLPermission(urlString);
+ }
+ */
}
diff --git a/ojluni/src/main/java/sun/net/www/MessageHeader.java b/ojluni/src/main/java/sun/net/www/MessageHeader.java
index 0237a85..34b6307 100644
--- a/ojluni/src/main/java/sun/net/www/MessageHeader.java
+++ b/ojluni/src/main/java/sun/net/www/MessageHeader.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -31,13 +31,7 @@
import java.io.*;
import java.util.Collections;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Set;
-import java.util.Iterator;
-import java.util.NoSuchElementException;
+import java.util.*;
/** An RFC 844 or MIME message header. Includes methods
for parsing headers from incoming streams, fetching
@@ -61,6 +55,17 @@
}
/**
+ * Returns list of header names in a comma separated list
+ */
+ public synchronized String getHeaderNamesInList() {
+ StringJoiner joiner = new StringJoiner(",");
+ for (int i=0; i<nkeys; i++) {
+ joiner.add(keys[i]);
+ }
+ return joiner.toString();
+ }
+
+ /**
* Reset a message header (all key/values removed)
*/
public synchronized void reset() {
@@ -148,7 +153,7 @@
for (int i=0; i<nkeys; i++) {
if (k.equalsIgnoreCase(keys[i])
&& values[i] != null && values[i].length() > 5
- && values[i].regionMatches(true, 0, "NTLM ", 0, 5)) {
+ && values[i].substring(0, 5).equalsIgnoreCase("NTLM ")) {
found = true;
break;
}
@@ -236,7 +241,8 @@
return filterAndAddHeaders(excludeList, null);
}
- public synchronized Map<String, List<String>> filterAndAddHeaders(String[] excludeList, Map<String, List<String>> include) {
+ public synchronized Map<String, List<String>> filterAndAddHeaders(
+ String[] excludeList, Map<String, List<String>> include) {
boolean skipIt = false;
Map<String, List<String>> m = new HashMap<String, List<String>>();
for (int i = nkeys; --i >= 0;) {
@@ -265,15 +271,13 @@
}
if (include != null) {
- Iterator entries = include.entrySet().iterator();
- while (entries.hasNext()) {
- Map.Entry entry = (Map.Entry)entries.next();
- List l = (List)m.get(entry.getKey());
+ for (Map.Entry<String,List<String>> entry: include.entrySet()) {
+ List<String> l = m.get(entry.getKey());
if (l == null) {
- l = new ArrayList();
- m.put((String)entry.getKey(), l);
+ l = new ArrayList<String>();
+ m.put(entry.getKey(), l);
}
- l.add(entry.getValue());
+ l.addAll(entry.getValue());
}
}
@@ -437,6 +441,7 @@
}
/** Parse and merge a MIME header from an input stream. */
+ @SuppressWarnings("fallthrough")
public void mergeHeader(InputStream is) throws java.io.IOException {
if (is == null)
return;
@@ -458,6 +463,7 @@
break;
case '\t':
c = ' ';
+ /*fall through*/
case ' ':
inKey = false;
break;
diff --git a/ojluni/src/main/java/sun/net/www/MeteredStream.java b/ojluni/src/main/java/sun/net/www/MeteredStream.java
index 17c8b83..26fe85d 100644
--- a/ojluni/src/main/java/sun/net/www/MeteredStream.java
+++ b/ojluni/src/main/java/sun/net/www/MeteredStream.java
@@ -142,15 +142,17 @@
return 0;
}
-// Android-changed: Remove support for Android-removed class ChunkedInputSTream
-// if (in instanceof ChunkedInputStream) {
-// n = in.skip(n);
-// }
-// else {
+ // Android-removed: Removed support for Android-removed class ChunkedInputStream.
+ /*
+ if (in instanceof ChunkedInputStream) {
+ n = in.skip(n);
+ }
+ else {
+ */
// just skip min(n, num_bytes_left)
long min = (n > expected - count) ? expected - count: n;
n = in.skip(min);
-// }
+ // }
justRead(n);
return n;
}
diff --git a/ojluni/src/main/java/sun/net/www/URLConnection.java b/ojluni/src/main/java/sun/net/www/URLConnection.java
index 2760acc..c495928 100644
--- a/ojluni/src/main/java/sun/net/www/URLConnection.java
+++ b/ojluni/src/main/java/sun/net/www/URLConnection.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,12 +26,7 @@
package sun.net.www;
import java.net.URL;
-import java.net.ContentHandler;
import java.util.*;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.BufferedInputStream;
-import java.net.UnknownServiceException;
/**
* A class to represent an active connection to an object
@@ -99,7 +94,7 @@
public Map<String,List<String>> getRequestProperties() {
if (connected)
throw new IllegalStateException("Already connected");
- return Collections.EMPTY_MAP;
+ return Collections.emptyMap();
}
public String getHeaderField(String name) {
diff --git a/ojluni/src/main/java/sun/net/www/protocol/ftp/FtpURLConnection.java b/ojluni/src/main/java/sun/net/www/protocol/ftp/FtpURLConnection.java
index 24f2f3b..e9f3d78 100644
--- a/ojluni/src/main/java/sun/net/www/protocol/ftp/FtpURLConnection.java
+++ b/ojluni/src/main/java/sun/net/www/protocol/ftp/FtpURLConnection.java
@@ -81,10 +81,12 @@
*/
public class FtpURLConnection extends URLConnection {
-// Android-changed: Removed support for proxying FTP over HTTP since it
-// relies on the removed sun.net.www.protocol.http.HttpURLConnection API.
-// // In case we have to use proxies, we use HttpURLConnection
-// HttpURLConnection http = null;
+ // Android-changed: Removed support for proxying FTP over HTTP.
+ // It relies on the removed sun.net.www.protocol.http.HttpURLConnection API.
+ /*
+ // In case we have to use proxies, we use HttpURLConnection
+ HttpURLConnection http = null;
+ */
private Proxy instProxy;
InputStream is = null;
@@ -163,6 +165,8 @@
*
* @param url The <code>URL</code> to retrieve or store.
*/
+ // Android-changed: Ctors can throw IOException for NetworkSecurityPolicy enforcement.
+ // public FtpURLConnection(URL url) {
public FtpURLConnection(URL url) throws IOException {
this(url, null);
}
@@ -170,19 +174,22 @@
/**
* Same as FtpURLconnection(URL) with a per connection proxy specified
*/
+ // Android-changed: Ctors can throw IOException for NetworkSecurityPolicy enforcement.
+ // FtpURLConnection(URL url, Proxy p) {
FtpURLConnection(URL url, Proxy p) throws IOException {
super(url);
instProxy = p;
host = url.getHost();
port = url.getPort();
String userInfo = url.getUserInfo();
-
+ // BEGIN Android-added: Enforce NetworkSecurityPolicy.isClearTextTrafficPermitted().
if (!NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted()) {
// Cleartext network traffic is not permitted -- refuse this connection.
throw new IOException("Cleartext traffic not permitted: "
+ url.getProtocol() + "://" + host
+ ((url.getPort() >= 0) ? (":" + url.getPort()) : ""));
}
+ // END Android-added: Enforce NetworkSecurityPolicy.isClearTextTrafficPermitted().
if (userInfo != null) { // get the user and password
int delimiter = userInfo.indexOf(':');
@@ -246,50 +253,56 @@
continue;
}
// OK, we have an http proxy
- // Android-changed: Removed support for proxying FTP over HTTP since it
- // relies on the removed sun.net.www.protocol.http.HttpURLConnection API.
+ // BEGIN Android-changed: Removed support for proxying FTP over HTTP.
+ // It relies on the removed sun.net.www.protocol.http.HttpURLConnection API.
+ /*
+ InetSocketAddress paddr = (InetSocketAddress) p.address();
+ try {
+ http = new HttpURLConnection(url, p);
+ http.setDoInput(getDoInput());
+ http.setDoOutput(getDoOutput());
+ if (connectTimeout >= 0) {
+ http.setConnectTimeout(connectTimeout);
+ }
+ if (readTimeout >= 0) {
+ http.setReadTimeout(readTimeout);
+ }
+ http.connect();
+ connected = true;
+ return;
+ } catch (IOException ioe) {
+ sel.connectFailed(uri, paddr, ioe);
+ http = null;
+ }
+ */
sel.connectFailed(uri, p.address(), new IOException("FTP connections over HTTP proxy not supported"));
continue;
-// InetSocketAddress paddr = (InetSocketAddress) p.address();
-// try {
-// http = new HttpURLConnection(url, p);
-// http.setDoInput(getDoInput());
-// http.setDoOutput(getDoOutput());
-// if (connectTimeout >= 0) {
-// http.setConnectTimeout(connectTimeout);
-// }
-// if (readTimeout >= 0) {
-// http.setReadTimeout(readTimeout);
-// }
-// http.connect();
-// connected = true;
-// return;
-// } catch (IOException ioe) {
-// sel.connectFailed(uri, paddr, ioe);
-// http = null;
-// }
+ // END Android-changed: Removed support for proxying FTP over HTTP.
}
}
} else { // per connection proxy specified
p = instProxy;
-// Android-changed: Removed support for proxying FTP over HTTP since it
-// relies on the removed sun.net.www.protocol.http.HttpURLConnection API.
-// As specified in the documentation for URL.openConnection(Proxy), we
-// ignore the unsupported proxy and attempt a normal (direct) connection
-// if (p.type() == Proxy.Type.HTTP) {
-// http = new HttpURLConnection(url, instProxy);
-// http.setDoInput(getDoInput());
-// http.setDoOutput(getDoOutput());
-// if (connectTimeout >= 0) {
-// http.setConnectTimeout(connectTimeout);
-// }
-// if (readTimeout >= 0) {
-// http.setReadTimeout(readTimeout);
-// }
-// http.connect();
-// connected = true;
-// return;
-// }
+ // BEGIN Android-changed: Removed support for proxying FTP over HTTP.
+ // It relies on the removed sun.net.www.protocol.http.HttpURLConnection API.
+ // As specified in the documentation for URL.openConnection(Proxy), we
+ // ignore the unsupported proxy and attempt a normal (direct) connection
+ /*
+ if (p.type() == Proxy.Type.HTTP) {
+ http = new HttpURLConnection(url, instProxy);
+ http.setDoInput(getDoInput());
+ http.setDoOutput(getDoOutput());
+ if (connectTimeout >= 0) {
+ http.setConnectTimeout(connectTimeout);
+ }
+ if (readTimeout >= 0) {
+ http.setReadTimeout(readTimeout);
+ }
+ http.connect();
+ connected = true;
+ return;
+ }
+ */
+ // END Android-changed: Removed support for proxying FTP over HTTP.
}
if (user == null) {
@@ -319,7 +332,7 @@
throw new IOException(fe);
}
try {
- ftp.login(user, password.toCharArray());
+ ftp.login(user, password == null ? null : password.toCharArray());
} catch (sun.net.ftp.FtpProtocolException e) {
ftp.close();
// Backward compatibility
@@ -410,11 +423,14 @@
if (!connected) {
connect();
}
- // Android-changed: Removed support for proxying FTP over HTTP since it
- // relies on the removed sun.net.www.protocol.http.HttpURLConnection API.
-// if (http != null) {
-// return http.getInputStream();
-// }
+
+ // Android-changed: Removed support for proxying FTP over HTTP.
+ // It relies on the removed sun.net.www.protocol.http.HttpURLConnection API.
+ /*
+ if (http != null) {
+ return http.getInputStream();
+ }
+ */
if (os != null) {
throw new IOException("Already opened for output");
@@ -525,15 +541,18 @@
if (!connected) {
connect();
}
-// Android-changed: Removed support for proxying FTP over HTTP since it
-// relies on the removed sun.net.www.protocol.http.HttpURLConnection API.
-// if (http != null) {
-// OutputStream out = http.getOutputStream();
-// // getInputStream() is neccessary to force a writeRequests()
-// // on the http client.
-// http.getInputStream();
-// return out;
-// }
+
+ // Android-changed: Removed support for proxying FTP over HTTP.
+ // It relies on the removed sun.net.www.protocol.http.HttpURLConnection API.
+ /*
+ if (http != null) {
+ OutputStream out = http.getOutputStream();
+ // getInputStream() is neccessary to force a writeRequests()
+ // on the http client.
+ http.getInputStream();
+ return out;
+ }
+ */
if (is != null) {
throw new IOException("Already opened for input");
diff --git a/ojluni/src/main/java/sun/net/www/protocol/jar/Handler.java b/ojluni/src/main/java/sun/net/www/protocol/jar/Handler.java
index 62686c6..8e9f8e3 100644
--- a/ojluni/src/main/java/sun/net/www/protocol/jar/Handler.java
+++ b/ojluni/src/main/java/sun/net/www/protocol/jar/Handler.java
@@ -123,6 +123,7 @@
@Override
+ @SuppressWarnings("deprecation")
protected void parseURL(URL url, String spec,
int start, int limit) {
String file = null;
diff --git a/ojluni/src/main/java/sun/net/www/protocol/jar/JarFileFactory.java b/ojluni/src/main/java/sun/net/www/protocol/jar/JarFileFactory.java
index ae86964..edd99ff 100644
--- a/ojluni/src/main/java/sun/net/www/protocol/jar/JarFileFactory.java
+++ b/ojluni/src/main/java/sun/net/www/protocol/jar/JarFileFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
diff --git a/ojluni/src/main/java/sun/net/www/protocol/jar/JarURLConnection.java b/ojluni/src/main/java/sun/net/www/protocol/jar/JarURLConnection.java
index 4c5dc87..d7c4424 100644
--- a/ojluni/src/main/java/sun/net/www/protocol/jar/JarURLConnection.java
+++ b/ojluni/src/main/java/sun/net/www/protocol/jar/JarURLConnection.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -125,7 +125,9 @@
* to get the jarFile, and set it as our permission.
*/
if (getUseCaches()) {
+ boolean oldUseCaches = jarFileURLConnection.getUseCaches();
jarFileURLConnection = factory.getConnection(jarFile);
+ jarFileURLConnection.setUseCaches(oldUseCaches);
}
if ((entryName != null)) {
diff --git a/ojluni/src/main/java/sun/security/jca/JCAUtil.java b/ojluni/src/main/java/sun/security/jca/JCAUtil.java
index b7bae41..fde9be5 100644
--- a/ojluni/src/main/java/sun/security/jca/JCAUtil.java
+++ b/ojluni/src/main/java/sun/security/jca/JCAUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2015 Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -41,12 +41,6 @@
// no instantiation
}
- // lock to use for synchronization
- private static final Object LOCK = JCAUtil.class;
-
- // cached SecureRandom instance
- private static volatile SecureRandom secureRandom;
-
// size of the temporary arrays we use. Should fit into the CPU's 1st
// level cache and could be adjusted based on the platform
private final static int ARRAY_SIZE = 4096;
@@ -60,26 +54,19 @@
return Math.min(ARRAY_SIZE, totalSize);
}
+ // cached SecureRandom instance
+ private static class CachedSecureRandomHolder {
+ public static SecureRandom instance = new SecureRandom();
+ }
+
/**
- * Get a SecureRandom instance. This method should me used by JDK
+ * Get a SecureRandom instance. This method should be used by JDK
* internal code in favor of calling "new SecureRandom()". That needs to
* iterate through the provider table to find the default SecureRandom
* implementation, which is fairly inefficient.
*/
public static SecureRandom getSecureRandom() {
- // we use double checked locking to minimize synchronization
- // works because we use a volatile reference
- SecureRandom r = secureRandom;
- if (r == null) {
- synchronized (LOCK) {
- r = secureRandom;
- if (r == null) {
- r = new SecureRandom();
- secureRandom = r;
- }
- }
- }
- return r;
+ return CachedSecureRandomHolder.instance;
}
}
diff --git a/ojluni/src/main/java/sun/security/util/DerInputStream.java b/ojluni/src/main/java/sun/security/util/DerInputStream.java
index b182d4e..6608676 100644
--- a/ojluni/src/main/java/sun/security/util/DerInputStream.java
+++ b/ojluni/src/main/java/sun/security/util/DerInputStream.java
@@ -325,6 +325,7 @@
* (used to initialize an auto-growing data structure)
* @return array of the values in the sequence
*/
+ // BEGIN Android-changed: Original encoded form needed for APKs parsing/validation
public DerValue[] getSequence(int startLen,
boolean originalEncodedFormRetained) throws IOException {
tag = (byte)buffer.read();
@@ -347,6 +348,7 @@
return getSequence(
startLen,
false); // no need to retain original encoded form
+ // END Android-changed: Original encoded form needed for APKs parsing/validation
}
/**
@@ -379,6 +381,7 @@
*/
public DerValue[] getSet(int startLen, boolean implicit)
throws IOException {
+ // BEGIN Android-changed: Original encoded form needed for APKs parsing/validation
return getSet(
startLen,
implicit,
@@ -395,6 +398,7 @@
}
}
return (readVector(startLen, originalEncodedFormRetained));
+ // END Android-changed: Original encoded form needed for APKs parsing/validation
}
/*
@@ -403,6 +407,7 @@
* this same helper routine.
*/
protected DerValue[] readVector(int startLen) throws IOException {
+ // BEGIN Android-changed: Original encoded form needed for APKs parsing/validation
return readVector(
startLen,
false); // no need to retain original encoded form
@@ -415,6 +420,7 @@
*/
protected DerValue[] readVector(int startLen,
boolean originalEncodedFormRetained) throws IOException {
+ // END Android-changed: Original encoded form needed for APKs parsing/validation
DerInputStream newstr;
byte lenByte = (byte)buffer.read();
@@ -459,6 +465,7 @@
DerValue value;
do {
+ // Android-changed: Original encoded form needed for APKs parsing/validation
value = new DerValue(newstr.buffer, originalEncodedFormRetained);
vec.addElement(value);
} while (newstr.available() > 0);
diff --git a/ojluni/src/main/java/sun/security/util/DerValue.java b/ojluni/src/main/java/sun/security/util/DerValue.java
index 21116dc..3045995 100644
--- a/ojluni/src/main/java/sun/security/util/DerValue.java
+++ b/ojluni/src/main/java/sun/security/util/DerValue.java
@@ -72,11 +72,13 @@
private int length;
+ // BEGIN Android-added: Original encoded form needed for APKs parsing/validation
/**
* The original encoded form of the whole value (tag, length, and value)
* or null if the form was not provided or was not retained during parsing.
*/
private byte[] originalEncodedForm;
+ // END Android-added: Original encoded form needed for APKs parsing/validation
/*
* The type starts at the first byte of the encoding, and
@@ -249,6 +251,7 @@
/*
* package private
*/
+ // BEGIN Android-changed: Original encoded form needed for APKs parsing/validation
DerValue(DerInputBuffer in, boolean originalEncodedFormRetained)
throws IOException {
// XXX must also parse BER-encoded constructed
@@ -294,6 +297,7 @@
int consumed = in.getPos() - startPosInInput;
originalEncodedForm = in.getSlice(startPosInInput, consumed);
}
+ // END Android-changed: Original encoded form needed for APKs parsing/validation
}
/**
@@ -834,6 +838,7 @@
}
}
+ // BEGIN Android-added: Original encoded form needed for APKs parsing/validation
/**
* Returns the original encoded form or {@code null} if the form was not
* retained or is not available.
@@ -842,6 +847,7 @@
return (originalEncodedForm != null)
? originalEncodedForm.clone() : null;
}
+ // END Android-added: Original encoded form needed for APKs parsing/validation
/**
* Returns a DER-encoded value, such that if it's passed to the
diff --git a/ojluni/src/main/java/sun/security/util/DisabledAlgorithmConstraints.java b/ojluni/src/main/java/sun/security/util/DisabledAlgorithmConstraints.java
index aecb341..a46b318 100644
--- a/ojluni/src/main/java/sun/security/util/DisabledAlgorithmConstraints.java
+++ b/ojluni/src/main/java/sun/security/util/DisabledAlgorithmConstraints.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -95,6 +95,7 @@
@Override
final public boolean permits(Set<CryptoPrimitive> primitives,
String algorithm, AlgorithmParameters parameters) {
+
if (primitives == null || primitives.isEmpty()) {
throw new IllegalArgumentException(
"No cryptographic primitive specified");
@@ -297,6 +298,7 @@
c = new jdkCAConstraint(algorithm);
jdkCALimit = true;
}
+
// Link multiple conditions for a single constraint
// into a linked list.
if (lastConstraint == null) {
diff --git a/ojluni/src/main/java/sun/security/util/KeyUtil.java b/ojluni/src/main/java/sun/security/util/KeyUtil.java
index e3efcff..c12a4ce 100644
--- a/ojluni/src/main/java/sun/security/util/KeyUtil.java
+++ b/ojluni/src/main/java/sun/security/util/KeyUtil.java
@@ -1,6 +1,6 @@
/*
* Copyright 2016 The Android Open Source Project
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012,2016 Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -91,18 +91,8 @@
// END Android-changed
} else if (key instanceof DSAKey) {
DSAKey pubk = (DSAKey)key;
- // BEGIN Android-changed
- // Was: size = pubk.getParams().getP().bitLength();
- DSAParams params = pubk.getParams();
- // According to RFC 3279 section 2.3.2, DSA keys are allowed
- // to inherit parameters in an X.509 certificate issuer's
- // key parameters, so the parameters may be null. The parent
- // key will be rejected if its parameters don't pass, so this
- // is okay.
- if (params != null) {
- size = params.getP().bitLength();
- }
- // END Android-changed
+ DSAParams params = pubk.getParams(); // params can be null
+ size = (params != null) ? params.getP().bitLength() : -1;
} else if (key instanceof DHKey) {
DHKey pubk = (DHKey)key;
size = pubk.getParams().getP().bitLength();
@@ -163,8 +153,6 @@
/**
* Returns whether the specified provider is Oracle provider or not.
- * <P>
- * Note that this method is only apply to SunJCE and SunPKCS11 at present.
*
* @param providerName
* the provider name
@@ -172,8 +160,11 @@
* {@code providerName} is Oracle provider
*
public static final boolean isOracleJCEProvider(String providerName) {
- return providerName != null && (providerName.equals("SunJCE") ||
- providerName.startsWith("SunPKCS11"));
+ return providerName != null &&
+ (providerName.equals("SunJCE") ||
+ providerName.equals("SunMSCAPI") ||
+ providerName.equals("OracleUcrypto") ||
+ providerName.startsWith("SunPKCS11"));
}
/**
@@ -218,7 +209,7 @@
byte[] encoded, boolean isFailOver) {
if (random == null) {
- random = new SecureRandom();
+ random = JCAUtil.getSecureRandom();
}
byte[] replacer = new byte[48];
random.nextBytes(replacer);
@@ -322,5 +313,5 @@
}
*/
// END Android-removed
-}
+}
diff --git a/ojluni/src/main/java/sun/security/x509/AVA.java b/ojluni/src/main/java/sun/security/x509/AVA.java
index ff9c62a..a047500 100644
--- a/ojluni/src/main/java/sun/security/x509/AVA.java
+++ b/ojluni/src/main/java/sun/security/x509/AVA.java
@@ -150,9 +150,6 @@
/**
* Parse an AVA string formatted according to format.
- *
- * XXX format RFC1779 should only allow RFC1779 syntax but is
- * actually DEFAULT with RFC1779 keywords.
*/
AVA(Reader in, int format) throws IOException {
this(in, format, Collections.<String, String>emptyMap());
@@ -272,7 +269,7 @@
break;
}
- // Android-changed: Skip trailing whitespace.
+ // BEGIN Android-added: AVA: Support DerValue hex strings that contain ' ' or '\n'
if (c == ' ' || c == '\n') {
do {
if (c != ' ' && c != '\n') {
@@ -282,7 +279,7 @@
} while (!isTerminator(c, format));
break;
}
-
+ // END Android-added: AVA: Support DerValue hex strings that contain ' ' or '\n'
int cVal = hexDigits.indexOf(Character.toUpperCase((char)c));
if (cVal == -1) {
@@ -384,11 +381,17 @@
PRESERVE_OLD_DC_ENCODING == false)) {
// EmailAddress and DomainComponent must be IA5String
return new DerValue(DerValue.tag_IA5String,
+ // Android-changed: Do not trim() DerValue strings.
+ // temp.toString().trim());
temp.toString());
} else if (isPrintableString) {
+ // Android-changed: Do not trim() DerValue strings.
+ //return new DerValue(temp.toString().trim());
return new DerValue(temp.toString());
} else {
return new DerValue(DerValue.tag_UTF8String,
+ // Android-changed: Do not trim() DerValue strings.
+ // temp.toString().trim());
temp.toString());
}
}
@@ -900,6 +903,8 @@
* the dotted-decimal form.
*/
if ((typeAndValue.charAt(0) >= '0' && typeAndValue.charAt(0) <= '9') ||
+ // Android-changed: AVA: Support DerValue hex strings that contain ' ' or '\n'
+ //!isDerString(value, true))
(!isDerString(value, true) && value.tag != DerValue.tag_T61String))
{
byte[] data = null;
diff --git a/ojluni/src/main/java/sun/security/x509/AlgorithmId.java b/ojluni/src/main/java/sun/security/x509/AlgorithmId.java
index 378ca2f..260d4aa 100644
--- a/ojluni/src/main/java/sun/security/x509/AlgorithmId.java
+++ b/ojluni/src/main/java/sun/security/x509/AlgorithmId.java
@@ -121,18 +121,14 @@
try {
algParams = AlgorithmParameters.getInstance(algidString);
} catch (NoSuchAlgorithmException e) {
- // BEGIN Android-changed
- // It was searching for the EC parameters in an internal provider in the deleted package
- // sun.security.ec before setting them to null. Since EC is in the OpenSSL provider,
- // there's no need for such fallback. Setting it to null directly.
/*
* This algorithm parameter type is not supported, so we cannot
* parse the parameters.
*/
algParams = null;
return;
- // END Android-changed
}
+
// Decode (parse) the parameters
algParams.init(params.toByteArray());
}
@@ -246,11 +242,13 @@
}
}
+ // BEGIN Android-added: Update algorithm mapping tables for names when OID is used
// Try to update the name <-> OID mapping table.
synchronized (oidTable) {
reinitializeMappingTableLocked();
algName = nameTable.get(algid);
}
+ // END Android-added: Update algorithm mapping tables for names when OID is used
return (algName == null) ? algid.toString() : algName;
}
@@ -566,6 +564,7 @@
// See if any of the installed providers supply a mapping from
// the given algorithm name to an OID string
+ // BEGIN Android-changed: Update algorithm mapping tables for names when OID is used
synchronized (oidTable) {
reinitializeMappingTableLocked();
return oidTable.get(name.toUpperCase(Locale.ENGLISH));
@@ -644,17 +643,20 @@
initOidTableVersion = currentVersion;
}
+ // END Android-changed: Update algorithm mapping tables for names when OID is used
}
private static ObjectIdentifier oid(int ... values) {
return ObjectIdentifier.newInternal(values);
}
+ // BEGIN Android-changed: Parsing mapping as OID even if "OID." prefix isn't specified
private static int initOidTableVersion = -1;
private static final Map<String,ObjectIdentifier> oidTable =
new HashMap<String,ObjectIdentifier>(1);
private static final Map<ObjectIdentifier,String> nameTable =
new HashMap<ObjectIdentifier,String>();
+ // END Android-changed: Parsing mapping as OID even if "OID." prefix isn't specified
/*****************************************************************/
@@ -939,6 +941,8 @@
*/
sha1WithDSA_oid = ObjectIdentifier.newInternal(dsaWithSHA1_PKIX_data);
+ // Android-removed: Parsing mapping as OID even if "OID." prefix isn't specified
+ //nameTable = new HashMap<ObjectIdentifier,String>();
nameTable.put(MD5_oid, "MD5");
nameTable.put(MD2_oid, "MD2");
nameTable.put(SHA_oid, "SHA-1");
diff --git a/ojluni/src/main/java/sun/security/x509/OIDMap.java b/ojluni/src/main/java/sun/security/x509/OIDMap.java
index 9eed0f2..0e3ae16 100644
--- a/ojluni/src/main/java/sun/security/x509/OIDMap.java
+++ b/ojluni/src/main/java/sun/security/x509/OIDMap.java
@@ -112,6 +112,8 @@
/** Map String(friendly name) -> OIDInfo(info) */
private final static Map<String,OIDInfo> nameMap;
+ // BEGIN Android-changed: Specify Class objects rather for oidMap rather than String
+ // literals + reflection.
static {
oidMap = new HashMap<ObjectIdentifier,OIDInfo>();
nameMap = new HashMap<String,OIDInfo>();
@@ -200,6 +202,8 @@
return clazz;
}
}
+ // END Android-changed: Specify Class objects rather for oidMap rather than String
+ // literals + reflection.
/**
* Add a name to lookup table.
diff --git a/ojluni/src/main/java/sun/security/x509/RDN.java b/ojluni/src/main/java/sun/security/x509/RDN.java
index 5a4c243..3bfc524 100644
--- a/ojluni/src/main/java/sun/security/x509/RDN.java
+++ b/ojluni/src/main/java/sun/security/x509/RDN.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 2002, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -478,11 +478,11 @@
* AVA's containing a standard keyword are ordered alphabetically,
* followed by AVA's containing an OID keyword, ordered numerically
*/
- @Override
public int compare(AVA a1, AVA a2) {
boolean a1Has2253 = a1.hasRFC2253Keyword();
boolean a2Has2253 = a2.hasRFC2253Keyword();
+ // BEGIN Android-changed: Keep sort order of RDN from Android M
if (a1Has2253) {
if (a2Has2253) {
return a1.toRFC2253CanonicalString().compareTo
@@ -506,6 +506,7 @@
a1Oid[pos] - a2Oid[pos];
}
}
+ // BEGIN Android-changed: Keep sort order of RDN from prev impl
}
}
diff --git a/ojluni/src/main/java/sun/security/x509/X500Name.java b/ojluni/src/main/java/sun/security/x509/X500Name.java
index 166ce3c..e708837 100644
--- a/ojluni/src/main/java/sun/security/x509/X500Name.java
+++ b/ojluni/src/main/java/sun/security/x509/X500Name.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -876,6 +876,7 @@
return;
}
+ // Android-added: refuse DN starting with new line or tab
checkNoNewLinesNorTabsAtBeginningOfDN(input);
List<RDN> dnVector = new ArrayList<>();
@@ -944,6 +945,7 @@
names = dnVector.toArray(new RDN[dnVector.size()]);
}
+ // BEGIN Android-added: refuse DN starting with new line or tab
/**
* Disallow new lines and tabs at the beginning of DN.
*
@@ -960,6 +962,7 @@
}
}
}
+ // END Android-added: refuse DN starting with new line or tab
private void parseRFC2253DN(String dnString) throws IOException {
if (dnString.length() == 0) {
@@ -1019,6 +1022,7 @@
static int countQuotes(String string, int from, int to) {
int count = 0;
+ // BEGIN Android-changed: Fix countQuotes in case of escaped backslashes: \\"
int escape = 0;
for (int i = from; i < to; i++) {
if (string.charAt(i) == '"' && escape % 2 == 0) {
@@ -1026,6 +1030,7 @@
}
escape = (string.charAt(i) == '\\') ? escape + 1 : 0;
}
+ // END Android-changed: Fix countQuotes in case of escaped backslashes: \\"
return count;
}
diff --git a/tzdata/prototype_data/Android.mk b/tzdata/prototype_data/Android.mk
new file mode 100644
index 0000000..568bbaf
--- /dev/null
+++ b/tzdata/prototype_data/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2017 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_PACKAGE_NAME := PrototypeTimeZoneDataApp
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
+include $(BUILD_PACKAGE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_PACKAGE_NAME := PrototypeTimeZoneDataApp_data
+LOCAL_CERTIFICATE := platform
+# Needed to ensure the .apk can be installed. Without it the .apk is missing a .dex.
+LOCAL_DEX_PREOPT := false
+include $(BUILD_PACKAGE)
diff --git a/tzdata/prototype_data/AndroidManifest.xml b/tzdata/prototype_data/AndroidManifest.xml
new file mode 100644
index 0000000..ae0fe6b
--- /dev/null
+++ b/tzdata/prototype_data/AndroidManifest.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="libcore.tzdata.prototype_data"
+ android:versionCode="1">
+
+ <application
+ android:allowBackup="false"
+ android:label="@string/app_name">
+
+ <provider
+ android:name="libcore.tzdata.prototypedata.TimeZoneRulesDataProvider"
+ android:authorities="com.android.timezone"
+ android:grantUriPermissions="true"
+ android:readPermission="android.permission.UPDATE_TIME_ZONE_RULES"
+ android:exported="true">
+ <meta-data android:name="android.timezoneprovider.OPERATION"
+ android:value="INSTALL"/>
+ <meta-data android:name="android.timezoneprovider.DATA_ASSET"
+ android:value="test_2030a.zip"/>
+ <meta-data android:name="android.timezoneprovider.DISTRO_MAJOR_VERSION"
+ android:value="1"/>
+ <meta-data android:name="android.timezoneprovider.DISTRO_MINOR_VERSION"
+ android:value="1"/>
+ <meta-data android:name="android.timezoneprovider.RULES_VERSION"
+ android:value="2030a"/>
+ <meta-data android:name="android.timezoneprovider.REVISION"
+ android:value="1"/>
+ </provider>
+ </application>
+</manifest>
diff --git a/tzdata/prototype_data/assets/test_2030a.zip b/tzdata/prototype_data/assets/test_2030a.zip
new file mode 100644
index 0000000..ae274cd
--- /dev/null
+++ b/tzdata/prototype_data/assets/test_2030a.zip
Binary files differ
diff --git a/tzdata/prototype_data/res/values/strings.xml b/tzdata/prototype_data/res/values/strings.xml
new file mode 100644
index 0000000..e25341c
--- /dev/null
+++ b/tzdata/prototype_data/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 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.
+ -->
+
+<resources>
+ <string name="app_name">DataPrototypeApp</string>
+</resources>
diff --git a/tzdata/prototype_data/src/libcore/tzdata/prototypedata/TimeZoneRulesDataProvider.java b/tzdata/prototype_data/src/libcore/tzdata/prototypedata/TimeZoneRulesDataProvider.java
new file mode 100644
index 0000000..c7be72a
--- /dev/null
+++ b/tzdata/prototype_data/src/libcore/tzdata/prototypedata/TimeZoneRulesDataProvider.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2017 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.tzdata.prototypedata;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.database.AbstractCursor;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ParcelFileDescriptor;
+import android.provider.TimeZoneRulesDataContract;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import libcore.io.Streams;
+
+import static android.content.res.AssetManager.ACCESS_STREAMING;
+import static android.provider.TimeZoneRulesDataContract.COLUMN_DISTRO_MAJOR_VERSION;
+import static android.provider.TimeZoneRulesDataContract.COLUMN_DISTRO_MINOR_VERSION;
+import static android.provider.TimeZoneRulesDataContract.COLUMN_OPERATION;
+import static android.provider.TimeZoneRulesDataContract.COLUMN_REVISION;
+import static android.provider.TimeZoneRulesDataContract.COLUMN_RULES_VERSION;
+import static android.provider.TimeZoneRulesDataContract.OPERATION_INSTALL;
+
+/**
+ * A basic implementation of a time zone data provider that can be used by OEMs to implement
+ * an APK asset-based solution for time zone updates.
+ */
+public final class TimeZoneRulesDataProvider extends ContentProvider {
+
+ static final String TAG = "TimeZoneRulesDataProvider";
+
+ private static final String METADATA_KEY_OPERATION = "android.timezoneprovider.OPERATION";
+ private static final String METADATA_KEY_ASSET = "android.timezoneprovider.DATA_ASSET";
+ private static final String METADATA_KEY_DISTRO_MAJOR_VERSION
+ = "android.timezoneprovider.DISTRO_MAJOR_VERSION";
+ private static final String METADATA_KEY_DISTRO_MINOR_VERSION
+ = "android.timezoneprovider.DISTRO_MINOR_VERSION";
+ private static final String METADATA_KEY_RULES_VERSION
+ = "android.timezoneprovider.RULES_VERSION";
+ private static final String METADATA_KEY_REVISION
+ = "android.timezoneprovider.REVISION";
+
+ private static final Set<String> KNOWN_COLUMN_NAMES;
+ private static final Map<String, Class<?>> KNOWN_COLUMN_TYPES;
+ static {
+ Set<String> columnNames = new HashSet<>();
+ columnNames.add(COLUMN_OPERATION);
+ columnNames.add(COLUMN_DISTRO_MAJOR_VERSION);
+ columnNames.add(COLUMN_DISTRO_MINOR_VERSION);
+ columnNames.add(COLUMN_RULES_VERSION);
+ columnNames.add(COLUMN_REVISION);
+ KNOWN_COLUMN_NAMES = Collections.unmodifiableSet(columnNames);
+
+ Map<String, Class<?>> columnTypes = new HashMap<>();
+ columnTypes.put(COLUMN_OPERATION, String.class);
+ columnTypes.put(COLUMN_DISTRO_MAJOR_VERSION, Integer.class);
+ columnTypes.put(COLUMN_DISTRO_MINOR_VERSION, Integer.class);
+ columnTypes.put(COLUMN_RULES_VERSION, String.class);
+ columnTypes.put(COLUMN_REVISION, Integer.class);
+ KNOWN_COLUMN_TYPES = Collections.unmodifiableMap(columnTypes);
+ }
+
+ private Map<String, Object> mColumnData = new HashMap<>();
+ private String mAssetName;
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public void attachInfo(Context context, ProviderInfo info) {
+ super.attachInfo(context, info);
+
+ // Sanity check our security
+ if (!TimeZoneRulesDataContract.AUTHORITY.equals(info.authority)) {
+ // The authority looked for by the time zone updater is fixed.
+ throw new SecurityException(
+ "android:authorities must be \"" + TimeZoneRulesDataContract.AUTHORITY + "\"");
+ }
+ if (!info.grantUriPermissions) {
+ throw new SecurityException("Provider must grant uri permissions");
+ }
+ if (!info.exported) {
+ // The content provider is accessed directly so must be exported.
+ throw new SecurityException("android:exported must be \"true\"");
+ }
+ if (info.pathPermissions != null || info.writePermission != null) {
+ // Use readPermission only to implement permissions.
+ throw new SecurityException("Use android:readPermission only");
+ }
+ if (!android.Manifest.permission.UPDATE_TIME_ZONE_RULES.equals(info.readPermission)) {
+ // Writing is not supported.
+ throw new SecurityException("android:readPermission must be set to \""
+ + android.Manifest.permission.UPDATE_TIME_ZONE_RULES
+ + "\" is: " + info.readPermission);
+ }
+
+ // info.metadata is not filled in by default. Must ask for it again.
+ final ProviderInfo infoWithMetadata = context.getPackageManager()
+ .resolveContentProvider(info.authority, PackageManager.GET_META_DATA);
+ Bundle metaData = infoWithMetadata.metaData;
+ if (metaData == null) {
+ throw new SecurityException("meta-data must be set");
+ }
+
+ String operation;
+ try {
+ operation = getMandatoryMetaDataString(metaData, METADATA_KEY_OPERATION);
+ mColumnData.put(COLUMN_OPERATION, operation);
+ } catch (IllegalArgumentException e) {
+ throw new SecurityException(METADATA_KEY_OPERATION + " meta-data not set.");
+ }
+ if (OPERATION_INSTALL.equals(operation)) {
+ mColumnData.put(
+ COLUMN_DISTRO_MAJOR_VERSION,
+ getMandatoryMetaDataInt(metaData, METADATA_KEY_DISTRO_MAJOR_VERSION));
+ mColumnData.put(
+ COLUMN_DISTRO_MINOR_VERSION,
+ getMandatoryMetaDataInt(metaData, METADATA_KEY_DISTRO_MINOR_VERSION));
+ mColumnData.put(
+ COLUMN_RULES_VERSION,
+ getMandatoryMetaDataString(metaData, METADATA_KEY_RULES_VERSION));
+ mColumnData.put(
+ COLUMN_REVISION,
+ getMandatoryMetaDataInt(metaData, METADATA_KEY_REVISION));
+
+ // Make sure the asset containing the data to install exists.
+ String assetName = getMandatoryMetaDataString(metaData, METADATA_KEY_ASSET);
+ try {
+ InputStream is = context.getAssets().open(assetName);
+ // An exception is thrown if the asset does not exist. list(assetName) appears not
+ // to work with file paths.
+ is.close();
+ } catch (IOException e) {
+ throw new SecurityException("Unable to open asset:" + assetName);
+ }
+ mAssetName = assetName;
+ }
+ }
+
+ @Override
+ public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
+ @Nullable String[] selectionArgs, @Nullable String sortOrder) {
+ if (!TimeZoneRulesDataContract.OPERATION_URI.equals(uri)) {
+ return null;
+ }
+ final List<String> projectionList = Arrays.asList(projection);
+ if (projection != null && !KNOWN_COLUMN_NAMES.containsAll(projectionList)) {
+ throw new UnsupportedOperationException(
+ "Only " + KNOWN_COLUMN_NAMES + " columns supported.");
+ }
+
+ return new AbstractCursor() {
+ @Override
+ public int getCount() {
+ return 1;
+ }
+
+ @Override
+ public String[] getColumnNames() {
+ return projectionList.toArray(new String[0]);
+ }
+
+ @Override
+ public int getType(int column) {
+ String columnName = projectionList.get(column);
+ Class<?> columnJavaType = KNOWN_COLUMN_TYPES.get(columnName);
+ if (columnJavaType == String.class) {
+ return Cursor.FIELD_TYPE_STRING;
+ } else if (columnJavaType == Integer.class) {
+ return Cursor.FIELD_TYPE_INTEGER;
+ } else {
+ throw new UnsupportedOperationException(
+ "Unsupported type: " + columnJavaType + " for " + columnName);
+ }
+ }
+
+ @Override
+ public String getString(int column) {
+ checkPosition();
+ String columnName = projectionList.get(column);
+ if (KNOWN_COLUMN_TYPES.get(columnName) != String.class) {
+ throw new UnsupportedOperationException();
+ }
+ return (String) mColumnData.get(columnName);
+ }
+
+ @Override
+ public short getShort(int column) {
+ checkPosition();
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getInt(int column) {
+ checkPosition();
+ String columnName = projectionList.get(column);
+ if (KNOWN_COLUMN_TYPES.get(columnName) != Integer.class) {
+ throw new UnsupportedOperationException();
+ }
+ return (Integer) mColumnData.get(columnName);
+ }
+
+ @Override
+ public long getLong(int column) {
+ return getInt(column);
+ }
+
+ @Override
+ public float getFloat(int column) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public double getDouble(int column) {
+ checkPosition();
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isNull(int column) {
+ checkPosition();
+ return column != 0;
+ }
+ };
+ }
+
+ @Override
+ public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode)
+ throws FileNotFoundException {
+ if (!TimeZoneRulesDataContract.DATA_URI.equals(uri)) {
+ return null;
+ }
+ if (mAssetName == null) {
+ throw new FileNotFoundException();
+ }
+ if (!mode.equals("r")) {
+ throw new SecurityException("Only read-only access supported.");
+ }
+
+ // Extract the asset to a local dir. We do it every time: we don't make assumptions that the
+ // current copy (if any) is valid.
+ File localFile = extractAssetToLocalFile();
+
+ // Create a read-only ParcelFileDescriptor that can be passed to the caller process.
+ try {
+ return ParcelFileDescriptor.open(localFile, ParcelFileDescriptor.MODE_READ_ONLY,
+ new Handler(Looper.getMainLooper()),
+ e -> {
+ if (e != null) {
+ Log.w(TAG, "Error in OnCloseListener for " + localFile, e);
+ }
+ localFile.delete();
+ });
+ } catch (IOException e) {
+ throw new RuntimeException("Unable to open asset file", e);
+ }
+ }
+
+ private File extractAssetToLocalFile() throws FileNotFoundException {
+ File extractedFile = new File(getContext().getFilesDir(), "timezone_data.zip");
+ InputStream is;
+ try {
+ is = getContext().getAssets().open(mAssetName, ACCESS_STREAMING);
+ } catch (FileNotFoundException e) {
+ throw e;
+ } catch (IOException e) {
+ FileNotFoundException fnfe = new FileNotFoundException("Problem reading asset");
+ fnfe.initCause(e);
+ throw fnfe;
+ }
+
+ try (InputStream fis = is;
+ FileOutputStream fos = new FileOutputStream(extractedFile, false /* append */)) {
+ Streams.copy(fis, fos);
+ } catch (IOException e) {
+ throw new RuntimeException("Unable to create asset storage file: " + extractedFile, e);
+ }
+ return extractedFile;
+ }
+
+ @Override
+ public String getType(@NonNull Uri uri) {
+ return null;
+ }
+
+ @Override
+ public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int delete(@NonNull Uri uri, @Nullable String selection,
+ @Nullable String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection,
+ @Nullable String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+ private static String getMandatoryMetaDataString(Bundle metaData, String key) {
+ if (!metaData.containsKey(key)) {
+ throw new SecurityException("No metadata with key " + key + " found.");
+ }
+ return metaData.getString(key);
+ }
+
+ private static int getMandatoryMetaDataInt(Bundle metaData, String key) {
+ if (!metaData.containsKey(key)) {
+ throw new SecurityException("No metadata with key " + key + " found.");
+ }
+ return metaData.getInt(key, -1);
+ }
+}
diff --git a/tzdata/prototype_updater/Android.mk b/tzdata/prototype_updater/Android.mk
new file mode 100644
index 0000000..0ddaa45
--- /dev/null
+++ b/tzdata/prototype_updater/Android.mk
@@ -0,0 +1,24 @@
+# Copyright (C) 2017 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_PACKAGE_NAME := PrototypeTimeZoneUpdaterApp
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
+include $(BUILD_PACKAGE)
diff --git a/tzdata/prototype_updater/AndroidManifest.xml b/tzdata/prototype_updater/AndroidManifest.xml
new file mode 100644
index 0000000..9b73b0b
--- /dev/null
+++ b/tzdata/prototype_updater/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="libcore.tzdata.prototype_updater"
+ android:versionCode="1">
+
+ <uses-permission android:name="android.permission.UPDATE_TIME_ZONE_RULES" />
+
+ <application
+ android:allowBackup="false"
+ android:label="@string/app_name">
+
+ <receiver android:name=".RulesCheckReceiver"
+ android:permission="android.permission.TRIGGER_TIME_ZONE_RULES_CHECK"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.timezone.TRIGGER_RULES_UPDATE_CHECK" />
+ </intent-filter>
+ </receiver>
+
+ </application>
+
+</manifest>
diff --git a/tzdata/prototype_updater/res/values/strings.xml b/tzdata/prototype_updater/res/values/strings.xml
new file mode 100644
index 0000000..204ae27
--- /dev/null
+++ b/tzdata/prototype_updater/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 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.
+ -->
+
+<resources>
+ <string name="app_name">PrototypeUpdaterApp</string>
+</resources>
diff --git a/tzdata/prototype_updater/src/libcore/tzdata/prototype_updater/RulesCheckReceiver.java b/tzdata/prototype_updater/src/libcore/tzdata/prototype_updater/RulesCheckReceiver.java
new file mode 100644
index 0000000..9f3a3b8
--- /dev/null
+++ b/tzdata/prototype_updater/src/libcore/tzdata/prototype_updater/RulesCheckReceiver.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2017 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.tzdata.prototype_updater;
+
+import android.app.timezone.Callback;
+import android.app.timezone.DistroFormatVersion;
+import android.app.timezone.DistroRulesVersion;
+import android.app.timezone.RulesManager;
+import android.app.timezone.RulesState;
+import android.app.timezone.RulesUpdaterContract;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.os.ParcelFileDescriptor;
+import android.provider.TimeZoneRulesDataContract;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import libcore.io.Streams;
+
+// TODO(nfuller): Prevent multiple broadcasts being handled at once?
+// TODO(nfuller): Improve logging
+// TODO(nfuller): Make the rules check async?
+// TODO(nfuller): Need async generally for SystemService calls from BroadcastReceiver?
+public class RulesCheckReceiver extends BroadcastReceiver {
+ final static String TAG = "RulesCheckReceiver";
+
+ private RulesManager mRulesManager;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!RulesUpdaterContract.ACTION_TRIGGER_RULES_UPDATE_CHECK.equals(intent.getAction())) {
+ // Unknown. Do nothing.
+ Log.w(TAG, "Unrecognized intent action received: " + intent
+ + ", action=" + intent.getAction());
+ return;
+ }
+
+ mRulesManager = (RulesManager) context.getSystemService("timezone");
+
+ byte[] token = intent.getByteArrayExtra(RulesUpdaterContract.EXTRA_CHECK_TOKEN);
+
+ // Note: We rely on the system server to check that the configured data application is the
+ // one that exposes the content provider with the well-known authority, and is a privileged
+ // application as required. It is *not* checked here and it is assumed the updater can trust
+ // the data application.
+
+ // Obtain the information about what the data app is telling us to do.
+ String operation = getOperation(context);
+ if (operation == null) {
+ // TODO Log
+ boolean success = true; // No point in retrying.
+ handleCheckComplete(token, success);
+ return;
+ }
+ switch (operation) {
+ case TimeZoneRulesDataContract.OPERATION_NO_OP:
+ // TODO Log
+ // No-op. Just acknowledge the check.
+ handleCheckComplete(token, true /* success */);
+ break;
+ case TimeZoneRulesDataContract.OPERATION_UNINSTALL:
+ // TODO Log
+ handleUninstall(token);
+ break;
+ case TimeZoneRulesDataContract.OPERATION_INSTALL:
+ // TODO Log
+ DistroVersionInfo distroVersionInfo = getDistroVersionInfo(context);
+ handleCopyAndInstall(context, token, distroVersionInfo);
+ break;
+ default:
+ // TODO Log
+ final boolean success = true; // No point in retrying.
+ handleCheckComplete(token, success);
+ }
+ }
+
+ private String getOperation(Context context) {
+ Cursor cursor = context.getContentResolver()
+ .query(TimeZoneRulesDataContract.OPERATION_URI,
+ new String[] { TimeZoneRulesDataContract.COLUMN_OPERATION },
+ null /* selection */, null /* selectionArgs */, null /* sortOrder */);
+ if (cursor == null) {
+ Log.e(TAG, "getOperation: query returned null");
+ return null;
+ }
+ if (!cursor.moveToFirst()) {
+ Log.e(TAG, "getOperation: query returned empty results");
+ return null;
+ }
+
+ try {
+ return cursor.getString(0);
+ } catch (Exception e) {
+ Log.e(TAG, "getOperation: getString() threw an exception", e);
+ return null;
+ }
+ }
+
+ private DistroVersionInfo getDistroVersionInfo(Context context) {
+ Cursor cursor = context.getContentResolver()
+ .query(TimeZoneRulesDataContract.OPERATION_URI,
+ new String[] {
+ TimeZoneRulesDataContract.COLUMN_DISTRO_MAJOR_VERSION,
+ TimeZoneRulesDataContract.COLUMN_DISTRO_MINOR_VERSION,
+ TimeZoneRulesDataContract.COLUMN_RULES_VERSION,
+ TimeZoneRulesDataContract.COLUMN_REVISION},
+ null /* selection */, null /* selectionArgs */, null /* sortOrder */);
+ if (cursor == null) {
+ Log.e(TAG, "getDistroVersionInfo: query returned null");
+ return null;
+ }
+ if (!cursor.moveToFirst()) {
+ Log.e(TAG, "getDistroVersionInfo: query returned empty results");
+ return null;
+ }
+
+ try {
+ return new DistroVersionInfo(
+ cursor.getInt(0),
+ cursor.getInt(1),
+ cursor.getString(2),
+ cursor.getInt(3));
+ } catch (Exception e) {
+ Log.e(TAG, "getDistroVersionInfo: getInt()/getString() threw an exception", e);
+ return null;
+ }
+ }
+
+ private void handleCopyAndInstall(Context context, byte[] checkToken,
+ DistroVersionInfo distroVersionInfo) {
+
+ // Decide whether to proceed with the install.
+ RulesState rulesState = mRulesManager.getRulesState();
+ if (!(rulesState.isDistroFormatVersionSupported(distroVersionInfo.mDistroFormatVersion)
+ && rulesState.isSystemVersionOlderThan(distroVersionInfo.mDistroRulesVersion))) {
+ // Nothing to do.
+ handleCheckComplete(checkToken, true /* success */);
+ return;
+ }
+
+ // Copy the data locally before passing it on....security and whatnot.
+ // TODO(nfuller): Need to do the copy here?
+ File file = copyDataToLocalFile(context);
+ if (file == null) {
+ // It's possible this may get better if the problem is related to storage space.
+ boolean success = false;
+ handleCheckComplete(checkToken, success);
+ return;
+ }
+ handleInstall(checkToken, file);
+ }
+
+ private static File copyDataToLocalFile(Context context) {
+ File extractedFile = new File(context.getFilesDir(), "temp.zip");
+ ParcelFileDescriptor fileDescriptor;
+ try {
+ fileDescriptor = context.getContentResolver().openFileDescriptor(
+ TimeZoneRulesDataContract.DATA_URI, "r");
+ if (fileDescriptor == null) {
+ throw new FileNotFoundException("ContentProvider returned null");
+ }
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "copyDataToLocalFile: Unable to open file descriptor"
+ + TimeZoneRulesDataContract.DATA_URI, e);
+ return null;
+ }
+
+ try (ParcelFileDescriptor pfd = fileDescriptor;
+ InputStream fis = new FileInputStream(pfd.getFileDescriptor());
+ FileOutputStream fos = new FileOutputStream(extractedFile, false /* append */)) {
+ Streams.copy(fis, fos);
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to create asset storage file: " + extractedFile, e);
+ return null;
+ }
+ return extractedFile;
+ }
+
+ private void handleInstall(final byte[] checkToken, final File contentFile) {
+ // Convert the distroFile to a ParcelFileDescriptor.
+ final ParcelFileDescriptor distroFileDescriptor;
+ try {
+ distroFileDescriptor =
+ ParcelFileDescriptor.open(contentFile, ParcelFileDescriptor.MODE_READ_ONLY);
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "Unable to create ParcelFileDescriptor from " + contentFile);
+ handleCheckComplete(checkToken, false /* success */);
+ return;
+ }
+
+ Callback callback = new Callback() {
+ @Override
+ public void onFinished(int status) {
+ Log.i(TAG, "onFinished: Finished install: " + status);
+
+ // TODO(nfuller): Can this be closed sooner?
+ try {
+ distroFileDescriptor.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to close ParcelFileDescriptor for " + contentFile, e);
+ } finally {
+ // Delete the file we no longer need.
+ contentFile.delete();
+ }
+ }
+ };
+
+ try {
+ int requestStatus =
+ mRulesManager.requestInstall(distroFileDescriptor, checkToken, callback);
+ Log.i(TAG, "handleInstall: Request sent:" + requestStatus);
+ } catch (Exception e) {
+ Log.e(TAG, "handleInstall: Error", e);
+ }
+ }
+
+ private void handleUninstall(byte[] checkToken) {
+ Callback callback = new Callback() {
+ @Override
+ public void onFinished(int status) {
+ Log.i(TAG, "onFinished: Finished uninstall: " + status);
+ }
+ };
+
+ try {
+ int requestStatus =
+ mRulesManager.requestUninstall(checkToken, callback);
+ Log.i(TAG, "handleUninstall: Request sent" + requestStatus);
+ } catch (Exception e) {
+ Log.e(TAG, "handleUninstall: Error", e);
+ }
+ }
+
+ private void handleCheckComplete(final byte[] token, final boolean success) {
+ try {
+ mRulesManager.requestNothing(token, success);
+ Log.i(TAG, "doInBackground: Called checkComplete: token="
+ + Arrays.toString(token) + ", success=" + success);
+ } catch (Exception e) {
+ Log.e(TAG, "doInBackground: Error calling checkComplete()", e);
+ }
+ }
+
+ private static class DistroVersionInfo {
+
+ final DistroFormatVersion mDistroFormatVersion;
+ final DistroRulesVersion mDistroRulesVersion;
+
+ DistroVersionInfo(int distroMajorVersion, int distroMinorVersion,
+ String rulesVersion, int revision) {
+ mDistroFormatVersion = new DistroFormatVersion(distroMajorVersion, distroMinorVersion);
+ mDistroRulesVersion = new DistroRulesVersion(rulesVersion, revision);
+ }
+ }
+}
diff --git a/tzdata/update_test_app2/Android.mk b/tzdata/update_test_app2/Android.mk
new file mode 100644
index 0000000..c1ae636
--- /dev/null
+++ b/tzdata/update_test_app2/Android.mk
@@ -0,0 +1,25 @@
+# Copyright (C) 2015 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_PACKAGE_NAME := UpdateTestApp2
+LOCAL_CERTIFICATE := platform
+include $(BUILD_PACKAGE)
diff --git a/tzdata/update_test_app2/AndroidManifest.xml b/tzdata/update_test_app2/AndroidManifest.xml
new file mode 100644
index 0000000..fd42bb5
--- /dev/null
+++ b/tzdata/update_test_app2/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="libcore.tzdata.update_test_app2.installupdatetestapp" >
+
+ <uses-permission android:name="android.permission.UPDATE_TIME_ZONE_RULES" />
+
+ <application
+ android:allowBackup="false"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@android:style/Theme.Holo.Light">
+ <activity
+ android:name=".MainActivity"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/tzdata/update_test_app2/res/drawable/ic_launcher.png b/tzdata/update_test_app2/res/drawable/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/tzdata/update_test_app2/res/drawable/ic_launcher.png
Binary files differ
diff --git a/tzdata/update_test_app2/res/layout/activity_main.xml b/tzdata/update_test_app2/res/layout/activity_main.xml
new file mode 100644
index 0000000..16e4b11
--- /dev/null
+++ b/tzdata/update_test_app2/res/layout/activity_main.xml
@@ -0,0 +1,98 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ tools:context=".MainActivity">
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/content_path"
+ android:id="@+id/content_path_label" />
+
+ <EditText
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/default_content_path"
+ android:id="@+id/content_path" />
+
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/trigger_install_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:text="@string/trigger_install" />
+
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="2dip"
+ android:background="#FF00FF00" />
+
+ <Button
+ android:id="@+id/trigger_uninstall_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:text="@string/trigger_uninstall" />
+
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="2dip"
+ android:background="#FF00FF00" />
+
+ <CheckBox android:id="@+id/success_checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/success" />
+
+ <Button
+ android:id="@+id/trigger_nothing"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:text="@string/trigger_nothing" />
+
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="2dip"
+ android:background="#FF00FF00" />
+
+ <Button
+ android:id="@+id/get_rules_state_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:text="@string/get_rules_state" />
+
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="2dip"
+ android:background="#FF00FF00" />
+
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scrollbars="vertical"
+ android:fillViewport="true">
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:id="@+id/log"
+ android:singleLine="false" />
+
+ </ScrollView>
+
+</LinearLayout>
diff --git a/tzdata/update_test_app2/res/values/strings.xml b/tzdata/update_test_app2/res/values/strings.xml
new file mode 100644
index 0000000..9bb5f57
--- /dev/null
+++ b/tzdata/update_test_app2/res/values/strings.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">InstallUpdateTestApp2</string>
+ <string name="content_path">Content Path</string>
+ <string name="default_content_path">/data/local/tmp/out.zip</string>
+ <string name="trigger_install">Request Install</string>
+ <string name="trigger_uninstall">Request Uninstall</string>
+ <string name="trigger_nothing">Request Nothing</string>
+ <string name="success">Success</string>
+ <string name="get_rules_state">Get rules state</string>
+</resources>
diff --git a/tzdata/update_test_app2/src/libcore/tzdata/update_test_app2/installupdatetestapp/MainActivity.java b/tzdata/update_test_app2/src/libcore/tzdata/update_test_app2/installupdatetestapp/MainActivity.java
new file mode 100644
index 0000000..caa52c4
--- /dev/null
+++ b/tzdata/update_test_app2/src/libcore/tzdata/update_test_app2/installupdatetestapp/MainActivity.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2015 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.tzdata.update_test_app2.installupdatetestapp;
+
+import android.app.Activity;
+import android.app.timezone.Callback;
+import android.app.timezone.RulesManager;
+import android.app.timezone.RulesState;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Date;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class MainActivity extends Activity implements View.OnClickListener {
+
+ private EditText contentPathEditText;
+ private TextView logView;
+
+ private RulesManager rulesManager;
+ private Callback callback;
+ private ExecutorService executor;
+ private CheckBox successCheckbox;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ Button triggerInstallButton = (Button) findViewById(R.id.trigger_install_button);
+ triggerInstallButton.setOnClickListener(this);
+ Button triggerUninstallButton = (Button) findViewById(R.id.trigger_uninstall_button);
+ triggerUninstallButton.setOnClickListener(this);
+ Button triggerCheckCompleteButton = (Button) findViewById(R.id.trigger_nothing);
+ triggerCheckCompleteButton.setOnClickListener(this);
+ successCheckbox = (CheckBox) findViewById(R.id.success_checkbox);
+
+ Button getRulesStateButton = (Button) findViewById(R.id.get_rules_state_button);
+ getRulesStateButton.setOnClickListener(this);
+
+ contentPathEditText = (EditText) findViewById(R.id.content_path);
+ logView = (TextView) findViewById(R.id.log);
+ executor = Executors.newFixedThreadPool(1);
+ rulesManager = (RulesManager) getSystemService("timezone");
+ callback = new Callback() {
+ @Override
+ public void onFinished(int status) {
+ logString("Operation finished. Status=" + status);
+ }
+ };
+ }
+
+ private abstract class MyAsyncTask extends AsyncTask<Void, String, Void> {
+ @Override
+ protected void onProgressUpdate(String... values) {
+ for (String message : values) {
+ addToLog(message, null);
+ }
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ MyAsyncTask task;
+ if (v.getId() == R.id.trigger_install_button) {
+ final String contentPath = contentPathEditText.getText().toString();
+ final File contentFile = new File(contentPath);
+
+ // Convert the contentFile to a ParcelFileDescriptor.
+ final ParcelFileDescriptor distroFileDescriptor =
+ createParcelFileDescriptor(contentFile);
+ if (distroFileDescriptor == null) {
+ return;
+ }
+
+ Callback callback = new Callback() {
+ @Override
+ public void onFinished(int status) {
+ logString("onFinished: Finished install: " + status);
+
+ // TODO(nfuller): Can this be closed sooner?
+ try {
+ distroFileDescriptor.close();
+ } catch (IOException e) {
+ logString("Unable to close ParcelFileDescriptor for " + contentFile + ": "
+ + exceptionToString(e));
+ }
+ }
+ };
+
+ task = new MyAsyncTask() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ if (!contentFile.exists()) {
+ publishProgress("Error: " + contentFile + " does not exist.");
+ return null;
+ }
+ try {
+ int requestStatus = rulesManager.requestInstall(
+ distroFileDescriptor, null /* checkToken */, callback);
+ publishProgress("Request sent:" + requestStatus);
+ } catch (Exception e) {
+ publishProgress("Error", exceptionToString(e));
+ }
+ return null;
+ }
+ };
+ } else if (v.getId() == R.id.trigger_uninstall_button) {
+ task = new MyAsyncTask() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ try {
+ int requestStatus = rulesManager.requestUninstall(
+ null /* checkToken */, callback);
+ publishProgress("Request sent:" + requestStatus);
+ } catch (Exception e) {
+ publishProgress("Error", exceptionToString(e));
+ }
+ return null;
+ }
+ };
+ } else if (v.getId() == R.id.trigger_nothing) {
+ final boolean success = successCheckbox.isChecked();
+ task = new MyAsyncTask() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ try {
+ rulesManager.requestNothing(null /* checkToken */, success);
+ publishProgress("Request sent");
+ } catch (Exception e) {
+ publishProgress("Error", exceptionToString(e));
+ }
+ return null;
+ }
+ };
+ } else if (v.getId() == R.id.get_rules_state_button) {
+ task = new MyAsyncTask() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ try {
+ RulesState rulesState = rulesManager.getRulesState();
+ publishProgress("Rules state: " + rulesState);
+ } catch (Exception e) {
+ publishProgress("Error", exceptionToString(e));
+ }
+ return null;
+ }
+ };
+ } else {
+ addToLog("Unknown button", null);
+ return;
+ }
+ task.executeOnExecutor(executor);
+ }
+
+ private ParcelFileDescriptor createParcelFileDescriptor(File contentFile) {
+ try {
+ return ParcelFileDescriptor.open(contentFile, ParcelFileDescriptor.MODE_READ_ONLY);
+ } catch (FileNotFoundException e) {
+ logString("Unable to create ParcelFileDescriptor from " + contentFile + ": "
+ + exceptionToString(e));
+ return null;
+ }
+ }
+
+ private void addToLog(String message, Exception e) {
+ logString(message);
+ if (e != null) {
+ String text = exceptionToString(e);
+ logString(text);
+ }
+ }
+
+ private void logString(String value) {
+ logView.append(new Date() + " " + value + "\n");
+ int scrollAmount =
+ logView.getLayout().getLineTop(logView.getLineCount()) - logView.getHeight();
+ logView.scrollTo(0, scrollAmount);
+ }
+
+ private static String exceptionToString(Exception e) {
+ StringWriter writer = new StringWriter();
+ e.printStackTrace(new PrintWriter(writer));
+ return writer.getBuffer().toString();
+ }
+}