aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--JavaLibrary.bp14
-rw-r--r--NativeCode.bp5
-rw-r--r--expectations/skippedCtsTest.txt19
-rw-r--r--expectations/skippedCtsTest_manual_base.txt20
-rw-r--r--harmony-tests/src/test/java/org/apache/harmony/tests/java/util/FormatterTest.java26
-rw-r--r--libcore.aconfig2
-rw-r--r--libjavacore.map23
-rw-r--r--luni/src/main/java/android/compat/Compatibility.java11
-rw-r--r--luni/src/test/java/libcore/android/compat/CompatibilityTest.java10
-rw-r--r--luni/src/test/java/libcore/java/io/FileTest.java127
-rw-r--r--luni/src/test/java/libcore/java/lang/OldClassTest.java4
-rw-r--r--luni/src/test/java/libcore/java/lang/reflect/OldAndroidClassTest.java4
-rw-r--r--luni/src/test/java/libcore/java/util/zip/ZipFileTest.java3
-rw-r--r--ojluni/src/main/java/java/awt/font/TEST_MAPPING9
-rw-r--r--ojluni/src/main/java/java/io/File.java16
-rw-r--r--ojluni/src/main/java/java/io/UnixFileSystem.java21
-rw-r--r--ojluni/src/main/java/java/nio/file/attribute/TEST_MAPPING9
-rw-r--r--ojluni/src/main/java/java/security/cert/TEST_MAPPING9
-rw-r--r--ojluni/src/main/java/java/time/TEST_MAPPING13
-rw-r--r--ojluni/src/main/java/java/time/chrono/TEST_MAPPING15
-rw-r--r--ojluni/src/main/java/java/time/format/TEST_MAPPING12
-rw-r--r--ojluni/src/main/java/java/time/temporal/TEST_MAPPING15
-rw-r--r--ojluni/src/main/java/java/time/zone/TEST_MAPPING15
-rw-r--r--ojluni/src/main/native/UnixFileSystem_md.c13
-rw-r--r--ojluni/src/main/native/canonicalize_md.c35
-rw-r--r--ojluni/src/main/native/java_io_UnixFileSystem.h4
-rw-r--r--ojluni/src/main/native/jni_util.c5
-rw-r--r--test-rules/src/main/java/libcore/test/annotation/NonCts.java4
28 files changed, 333 insertions, 130 deletions
diff --git a/JavaLibrary.bp b/JavaLibrary.bp
index 015f3c844f4..dde6d243e75 100644
--- a/JavaLibrary.bp
+++ b/JavaLibrary.bp
@@ -951,11 +951,19 @@ droidstubs {
// libcore/luni/annotations/flagged_api.
droidstubs {
name: "libart-sdk-stubs-no-javadoc",
- srcs: [":core_libart_api_files"],
+ srcs: [
+ ":core_libart_api_files",
+ // The definition of @SystemApi is needed to generate module-lib stubs.
+ ":framework-api-annotations",
+ ],
installable: false,
sdk_version: "none",
system_modules: "none",
- args: "--exclude-documentation-from-stubs",
+ api_levels_sdk_type: "module-lib",
+ flags: [
+ "--exclude-documentation-from-stubs",
+ "--show-for-stub-purposes-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES\\)",
+ ],
}
// Separate @FlaggedApi annotations for luni/ to avoid a circular dependency between
@@ -1003,7 +1011,7 @@ java_library {
aconfig_declarations {
name: "libcore_aconfig_flags",
package: "com.android.libcore",
- container: "system",
+ container: "com.android.art",
srcs: ["libcore.aconfig"],
}
diff --git a/NativeCode.bp b/NativeCode.bp
index defc30698a0..373761f8751 100644
--- a/NativeCode.bp
+++ b/NativeCode.bp
@@ -20,7 +20,6 @@
cc_defaults {
name: "core_native_default_flags",
- defaults: ["art_module_source_build_defaults"],
host_supported: true,
cflags: [
"-Wall",
@@ -33,6 +32,9 @@ cc_defaults {
darwin: {
enabled: false,
},
+ windows: {
+ enabled: false,
+ },
},
min_sdk_version: "S",
}
@@ -75,6 +77,7 @@ cc_library_shared {
static_libs: [
"libziparchive",
],
+ version_script: "libjavacore.map",
}
cc_library_shared {
diff --git a/expectations/skippedCtsTest.txt b/expectations/skippedCtsTest.txt
index 4ee04feb36c..ef24a987e00 100644
--- a/expectations/skippedCtsTest.txt
+++ b/expectations/skippedCtsTest.txt
@@ -127,5 +127,24 @@
"names": [
"org.apache.harmony.tests.java.util.PriorityQueueTest#test_spliterator_CME"
]
+ },
+ {
+ "bug": 338503591,
+ "description": "Test for internal APIs.",
+ "names": [
+ "libcore.java.util.LinkedHashMapTest#test_entryCompatibility_runtime",
+ "libcore.java.util.zip.ZipFileTest#testZipFileOffsetNeverChangesAfterInit",
+ "libcore.jdk.internal.misc.SharedSecretsTest#testGetJavaIOFileDescriptorAccess_notNull"
+ ]
+ },
+ {
+ "bug": 338503591,
+ "description": "The test asserts buggy or non-breaking behaviors, but the behavior has been fixed in a new mainline module version.",
+ "names": [
+ "libcore.java.lang.OldClassTest#test_getGenericInterfaces",
+ "libcore.java.lang.reflect.OldAndroidClassTest#testClassGetMethodsNoDupes",
+ "libcore.java.util.zip.ZipFileTest#test_FileNotFound",
+ "org.apache.harmony.tests.java.util.zip.DeflaterTest#test_finalize"
+ ]
}
] \ No newline at end of file
diff --git a/expectations/skippedCtsTest_manual_base.txt b/expectations/skippedCtsTest_manual_base.txt
index 9bda1f44787..45bc39c3f03 100644
--- a/expectations/skippedCtsTest_manual_base.txt
+++ b/expectations/skippedCtsTest_manual_base.txt
@@ -26,5 +26,25 @@
"bug": 316502724,
"description": "SharedSecrets class is at different location in newer ART versions",
"name": "libcore.sun.misc.SharedSecretsTest#testGetJavaIOFileDescriptorAccess_notNull"
+ },
+ {
+ "bug": 338503591,
+ "description": "Test for internal APIs.",
+ "name": "libcore.java.util.LinkedHashMapTest#test_entryCompatibility_runtime"
+ },
+ {
+ "bug": 338503591,
+ "description": "Test for internal APIs.",
+ "name": "libcore.java.util.zip.ZipFileTest#testZipFileOffsetNeverChangesAfterInit"
+ },
+ {
+ "bug": 338503591,
+ "description": "The test asserts buggy or non-breaking behaviors, but the behavior has been fixed in a new mainline module version.",
+ "name": "org.apache.harmony.tests.java.util.zip.DeflaterTest#test_finalize"
+ },
+ {
+ "bug": 338503591,
+ "description": "Test for internal APIs.",
+ "name": "libcore.jdk.internal.misc.SharedSecretsTest#testGetJavaIOFileDescriptorAccess_notNull"
}
]
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/FormatterTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/FormatterTest.java
index f59451d5034..51e4b4cfacc 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/FormatterTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/FormatterTest.java
@@ -740,7 +740,7 @@ public class FormatterTest extends TestCase {
assertEquals("1234567891110", formatter.toString());
formatter = new Formatter(Locale.JAPAN);
- formatter.format("%0$s", "hello");
+ formatter.format("%1$s", "hello");
assertEquals("hello", formatter.toString());
try {
@@ -797,8 +797,10 @@ public class FormatterTest extends TestCase {
try {
// 2147483648 is the value of Integer.MAX_VALUE + 1
formatter.format("%2147483648$s", "hello");
- fail("should throw MissingFormatArgumentException");
- } catch (MissingFormatArgumentException e) {
+ // Starting from V the line above throws IllegalFormatArgumentIndexException, but
+ // that class is package-private.
+ fail("should throw IllegalFormatException");
+ } catch (IllegalFormatException e) {
// expected
}
@@ -839,11 +841,6 @@ public class FormatterTest extends TestCase {
f.format("%1$-1%", "string");
assertEquals("%", f.toString());
- f = new Formatter(Locale.ITALY);
- // 2147483648 is the value of Integer.MAX_VALUE + 1
- f.format("%2147483648s", "string");
- assertEquals("string", f.toString());
-
// the value of Integer.MAX_VALUE will allocate about 4G bytes of
// memory.
// It may cause OutOfMemoryError, so this value is not tested
@@ -857,11 +854,6 @@ public class FormatterTest extends TestCase {
f.format("%.5s", "123456");
assertEquals("12345", f.toString());
- f = new Formatter(Locale.US);
- // 2147483648 is the value of Integer.MAX_VALUE + 1
- f.format("%.2147483648s", "...");
- assertEquals("...", f.toString());
-
// the value of Integer.MAX_VALUE will allocate about 4G bytes of
// memory.
// It may cause OutOfMemoryError, so this value is not tested
@@ -2659,10 +2651,14 @@ public class FormatterTest extends TestCase {
try {
f = new Formatter();
+ // Here the first 0 is a padding element and the rest is the width. It is expected
+ // that the value is an int, so the correct behavior is to throw
+ // IllegalFormatWidthException, but prior to V the exception was
+ // MissingFormatWidthException. Once V is finalized the catch could be simplified.
f.format("%010000000000000000000000000000000001d", new BigInteger(
"1"));
- fail("should throw MissingFormatWidthException");
- } catch (MissingFormatWidthException e) {
+ fail("should throw IllegalFormatWidthException or MissingFormatWidthException");
+ } catch (IllegalFormatWidthException | MissingFormatWidthException e) {
// expected
}
}
diff --git a/libcore.aconfig b/libcore.aconfig
index ca9f851df5c..4fb3092a72c 100644
--- a/libcore.aconfig
+++ b/libcore.aconfig
@@ -16,7 +16,7 @@
# com.android is chosen as the java_aconfig_library is normally exposed to the platform,
# but not public apps.
package: "com.android.libcore"
-container: "system"
+container: "com.android.art"
flag {
namespace: "core_libraries"
diff --git a/libjavacore.map b/libjavacore.map
new file mode 100644
index 00000000000..27bc1f0e47a
--- /dev/null
+++ b/libjavacore.map
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2024 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.
+#
+
+{
+ global:
+ JNI_OnLoad;
+ JNI_OnUnload;
+ local:
+ *;
+};
diff --git a/luni/src/main/java/android/compat/Compatibility.java b/luni/src/main/java/android/compat/Compatibility.java
index 31c1fe18e24..5340c87ef0d 100644
--- a/luni/src/main/java/android/compat/Compatibility.java
+++ b/luni/src/main/java/android/compat/Compatibility.java
@@ -114,6 +114,17 @@ public final class Compatibility {
}
/**
+ * Return the behavior change delegate
+ *
+ * @hide
+ */
+ // VisibleForTesting
+ @NonNull
+ public static BehaviorChangeDelegate getBehaviorChangeDelegate() {
+ return sCallbacks;
+ }
+
+ /**
* For use by tests only. Causes values from {@code overrides} to be returned instead of the
* real value.
*
diff --git a/luni/src/test/java/libcore/android/compat/CompatibilityTest.java b/luni/src/test/java/libcore/android/compat/CompatibilityTest.java
index e990dae9407..22be3db7abc 100644
--- a/luni/src/test/java/libcore/android/compat/CompatibilityTest.java
+++ b/luni/src/test/java/libcore/android/compat/CompatibilityTest.java
@@ -32,6 +32,7 @@ import android.compat.Compatibility.ChangeConfig;
import android.compat.Compatibility.BehaviorChangeDelegate;
import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -39,9 +40,16 @@ import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class CompatibilityTest {
+ private BehaviorChangeDelegate mDelegate;
+
+ @Before
+ public void setUp() {
+ mDelegate = Compatibility.getBehaviorChangeDelegate();
+ }
+
@After
public void reset() {
- Compatibility.clearBehaviorChangeDelegate();
+ Compatibility.setBehaviorChangeDelegate(mDelegate);
}
@Test
diff --git a/luni/src/test/java/libcore/java/io/FileTest.java b/luni/src/test/java/libcore/java/io/FileTest.java
index e61f572e8e3..e57e38b8563 100644
--- a/luni/src/test/java/libcore/java/io/FileTest.java
+++ b/luni/src/test/java/libcore/java/io/FileTest.java
@@ -28,18 +28,41 @@ import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
+import java.util.concurrent.atomic.AtomicReference;
+
import libcore.io.Libcore;
+import libcore.junit.util.compat.CoreCompatChangeRule;
import static android.system.Os.stat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import static org.junit.Assume.assumeTrue;
+
+import dalvik.annotation.compat.VersionCodes;
+import dalvik.system.VMRuntime;
+
import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
-public class FileTest extends junit.framework.TestCase {
+@RunWith(JUnit4.class)
+public class FileTest {
static {
System.loadLibrary("javacoretests");
}
+
+ @Rule
+ public final TestRule compatChangeRule = new CoreCompatChangeRule();
+
private static File createTemporaryDirectory() throws Exception {
String base = System.getProperty("java.io.tmpdir");
File directory = new File(base, UUID.randomUUID().toString());
@@ -71,6 +94,7 @@ public class FileTest extends junit.framework.TestCase {
// Rather than test all methods, assume that if createTempFile creates a long path and
// exists can see it, the code for coping with long paths (shared by all methods) works.
+ @Test
public void test_longPath() throws Exception {
File base = createTemporaryDirectory();
assertTrue(createDeepStructure(base).exists());
@@ -83,6 +107,7 @@ public class FileTest extends junit.framework.TestCase {
* isn't true on Android if you're using /sdcard (which is used if this test is
* run using vogar). It will work in /data/data/ though.
*/
+ @Test
public void test_longReadlink() throws Exception {
File base = createTemporaryDirectory();
File target = createDeepStructure(base);
@@ -98,6 +123,7 @@ public class FileTest extends junit.framework.TestCase {
// TODO: File.list is a special case too, but I haven't fixed it yet, and the new code,
// like the old code, will die of a native buffer overrun if we exercise it.
+ @Test
public void test_emptyFilename() throws Exception {
// The behavior of the empty filename is an odd mixture.
File f = new File("");
@@ -149,6 +175,7 @@ public class FileTest extends junit.framework.TestCase {
// http://b/2486943 - between eclair and froyo, we added a call to
// isAbsolute from the File constructor, potentially breaking subclasses.
+ @Test
public void test_subclassing() throws Exception {
class MyFile extends File {
private String field;
@@ -171,6 +198,7 @@ public class FileTest extends junit.framework.TestCase {
* isn't true on Android if you're using /sdcard (which is used if this test is
* run using vogar). It will work in /data/data/ though.
*/
+ @Test
public void test_getCanonicalPath() throws Exception {
File base = createTemporaryDirectory();
File target = new File(base, "target");
@@ -207,6 +235,7 @@ public class FileTest extends junit.framework.TestCase {
Libcore.os.symlink(target, linkName);
}
+ @Test
public void test_createNewFile() throws Exception {
File f = File.createTempFile("FileTest", "tmp");
assertFalse(f.createNewFile()); // EEXIST -> false
@@ -223,6 +252,7 @@ public class FileTest extends junit.framework.TestCase {
}
}
+ @Test
public void test_rename() throws Exception {
File f = File.createTempFile("FileTest", "tmp");
assertFalse(f.renameTo(new File("")));
@@ -231,6 +261,7 @@ public class FileTest extends junit.framework.TestCase {
assertTrue(f.renameTo(f));
}
+ @Test
public void test_getAbsolutePath() throws Exception {
String userDir = System.getProperty("user.dir");
if (!userDir.endsWith(File.separator)) {
@@ -241,12 +272,14 @@ public class FileTest extends junit.framework.TestCase {
assertEquals(userDir + "poop", f.getAbsolutePath());
}
+ @Test
public void test_getSpace() throws Exception {
assertTrue(new File("/").getFreeSpace() >= 0);
assertTrue(new File("/").getTotalSpace() >= 0);
assertTrue(new File("/").getUsableSpace() >= 0);
}
+ @Test
public void test_mkdirs() throws Exception {
// Set up a directory to test in.
File base = createTemporaryDirectory();
@@ -290,6 +323,7 @@ public class FileTest extends junit.framework.TestCase {
// http://b/23523042 : Test that creating a directory and file with
// a surrogate pair in the name works as expected and roundtrips correctly.
+ @Test
public void testFilesWithSurrogatePairs() throws Exception {
File base = createTemporaryDirectory();
// U+13000 encodes to the surrogate pair D80C + DC00 in UTF16
@@ -319,20 +353,37 @@ public class FileTest extends junit.framework.TestCase {
// SECCOMP filter. The SECCOMP filter is designed to not allow stat(fstatat64/newfstatat) calls
// and whenever a thread makes the system call, android.system.ErrnoException
// (EPERM - Operation not permitted) will be raised.
- public void testExistsOnSystem() throws ErrnoException, IOException {
+ @Test
+ public void testExistsOnSystem() throws IOException, InterruptedException {
File tmpFile = File.createTempFile("testExistsOnSystem", ".tmp");
- try {
- assertEquals("SECCOMP filter is not installed.", 0, installSeccompFilter());
+ AtomicReference<Throwable> pendingThrowable = new AtomicReference<>();
+ // Start a new Thread to install seccomp filter on the thread.
+ // It avoids the seccomp filter affecting other test cases.
+ // The alternative is to clean-up the seccomp filter in a teardown / finally {} block,
+ // but it would be difficult to reset if another seccomp filter was pre-set on the test
+ // thread.
+ Thread thread = new Thread(() -> {
try {
- // Verify that SECCOMP filter obstructs stat.
- stat(tmpFile.getAbsolutePath());
- fail();
- } catch (ErrnoException expected) {
- assertEquals(OsConstants.EPERM, expected.errno);
+ assertEquals("SECCOMP filter is not installed.", 0, installSeccompFilter());
+ try {
+ // Verify that SECCOMP filter obstructs stat.
+ stat(tmpFile.getAbsolutePath());
+ fail();
+ } catch (ErrnoException expected) {
+ assertEquals(OsConstants.EPERM, expected.errno);
+ }
+ assertTrue(tmpFile.exists());
+ } finally {
+ tmpFile.delete();
}
- assertTrue(tmpFile.exists());
- } finally {
- tmpFile.delete();
+ });
+ thread.setUncaughtExceptionHandler((t, throwable) -> pendingThrowable.set(throwable));
+ thread.start();
+ thread.join();
+
+ Throwable t = pendingThrowable.get();
+ if (t != null) {
+ throw new AssertionError(t);
}
}
@@ -343,6 +394,7 @@ public class FileTest extends junit.framework.TestCase {
// OpenJdk is treating empty parent string as a special case,
// it substitutes parent string with the filesystem default parent value "/"
// This wasn't the case before the switch to openJdk.
+ @Test
public void testEmptyParentString() {
File f1 = new File("", "foo.bar");
File f2 = new File((String)null, "foo.bar");
@@ -351,6 +403,7 @@ public class FileTest extends junit.framework.TestCase {
}
// http://b/27273930
+ @Test
public void testJavaIoTmpdirMutable() throws Exception {
final String oldTmpDir = System.getProperty("java.io.tmpdir");
final String directoryName = "/newTemp" + Integer.toString(Math.randomIntInternal());
@@ -369,12 +422,14 @@ public class FileTest extends junit.framework.TestCase {
private static native void nativeTestFilesWithSurrogatePairs(String base);
// http://b/27731686
+ @Test
public void testBug27731686() {
File file = new File("/test1", "/");
assertEquals("/test1", file.getPath());
assertEquals("test1", file.getName());
}
+ @Test
public void testFileNameNormalization() {
assertEquals("/", new File("/", "/").getPath());
assertEquals("/", new File("/", "").getPath());
@@ -385,6 +440,7 @@ public class FileTest extends junit.framework.TestCase {
assertEquals("/foo/bar", new File("/foo", "/bar//").getPath());
}
+ @Test
public void test_toPath() {
File file = new File("testPath");
Path filePath = file.toPath();
@@ -398,6 +454,7 @@ public class FileTest extends junit.framework.TestCase {
}
// http://b/62301183
+ @Test
public void test_canonicalCachesAreOff() throws Exception {
File tempDir = createTemporaryDirectory();
File f1 = new File(tempDir, "testCannonCachesOff1");
@@ -418,6 +475,7 @@ public class FileTest extends junit.framework.TestCase {
assertEquals(symlinkFile.getCanonicalPath(), f2.getCanonicalPath());
}
+ @Test
public void testGetCanonicalPath_duplicatePathSeparator() throws Exception {
assertCanonicalPath("/a//./b", "/a/b");
assertCanonicalPath("//a//b", "/a/b");
@@ -434,8 +492,53 @@ public class FileTest extends junit.framework.TestCase {
assertEquals(expected, file.getCanonicalFile().getPath());
}
+ @Test
public void testGetCanonicalPath_longPath() {
String p = "/a".repeat(2048);
Assert.assertThrows(IOException.class, () -> new File(p).getCanonicalFile());
}
+
+ @Test
+ @CoreCompatChangeRule.EnableCompatChanges({File.CANONICALIZE_PARENT_OF_ROOT_DIR})
+ public void testGetCanonicalPath_parentOfRootPath_afterU_whenChecksAreEnabled()
+ throws IOException {
+ assertGetCanonicalPath_commonTestCases();
+
+ var msg = "CANONICALIZE_PARENT_OF_ROOT_DIR change is are done only starting from V. "
+ + "Current SDK=" + VMRuntime.getSdkVersion();
+ assumeTrue(msg, VMRuntime.getSdkVersion() >= VersionCodes.VANILLA_ICE_CREAM);
+ assertGetCanonicalPath("/non_existing/directory/../../../new_file", "/new_file");
+ assertGetCanonicalPath("/non/existing/directory/../../../../new_file", "/new_file");
+ }
+
+ @Test
+ @CoreCompatChangeRule.DisableCompatChanges({File.CANONICALIZE_PARENT_OF_ROOT_DIR})
+ public void testGetCanonicalPath_parentOfRootPath_whenChecksAreDisabled() throws IOException {
+ assertGetCanonicalPath("/non_existing/directory/../../../new_file", "/../new_file");
+ assertGetCanonicalPath("/non/existing/directory/../../../../new_file", "/../new_file");
+
+ assertGetCanonicalPath_commonTestCases();
+ }
+
+ private void assertGetCanonicalPath_commonTestCases() throws IOException {
+ // "/.." is normally resolved by libc's realpath(3) regardless
+ // CANONICALIZE_PARENT_OF_ROOT_DIR is enabled or not.
+ assertGetCanonicalPath("/..", "/");
+ assertGetCanonicalPath("/../..", "/");
+ assertGetCanonicalPath("/../../abc", "/abc");
+ assertGetCanonicalPath("/../../abc/..", "/");
+ assertGetCanonicalPath("/../../abc/../def", "/def");
+ // getCanonicalPath treats relative path as absolute path in most cases.
+ assertGetCanonicalPath("..", "/");
+ assertGetCanonicalPath("../..", "/");
+ assertGetCanonicalPath("../../abc", "/abc");
+ assertGetCanonicalPath("../../abc/..", "/");
+ assertGetCanonicalPath("../../abc/../def", "/def");
+ }
+
+ private void assertGetCanonicalPath(String input, String expected) throws IOException {
+ File file = new File(input);
+ assertEquals(expected, file.getCanonicalPath());
+ }
+
}
diff --git a/luni/src/test/java/libcore/java/lang/OldClassTest.java b/luni/src/test/java/libcore/java/lang/OldClassTest.java
index daf32552d35..3a28398d0c0 100644
--- a/luni/src/test/java/libcore/java/lang/OldClassTest.java
+++ b/luni/src/test/java/libcore/java/lang/OldClassTest.java
@@ -16,6 +16,9 @@
package libcore.java.lang;
+import libcore.test.annotation.NonCts;
+import libcore.test.reasons.NonCtsReasons;
+
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -424,6 +427,7 @@ public class OldClassTest extends junit.framework.TestCase {
public enum TestEmptyEnum {
}
+ @NonCts(bug = 338503591, reason = NonCtsReasons.NON_BREAKING_BEHAVIOR_FIX)
public void test_getGenericInterfaces() {
Type [] types = ExtendTestClass1.class.getGenericInterfaces();
assertEquals(0, types.length);
diff --git a/luni/src/test/java/libcore/java/lang/reflect/OldAndroidClassTest.java b/luni/src/test/java/libcore/java/lang/reflect/OldAndroidClassTest.java
index 7ec843d3acb..23d1b26aba3 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/OldAndroidClassTest.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/OldAndroidClassTest.java
@@ -16,6 +16,9 @@
package libcore.java.lang.reflect;
+import libcore.test.annotation.NonCts;
+import libcore.test.reasons.NonCtsReasons;
+
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@@ -137,6 +140,7 @@ public final class OldAndroidClassTest extends TestCase {
// Regression for 1018067: Class.getMethods() returns the same method over
// and over again from all base classes
+ @NonCts(bug = 338503591, reason = NonCtsReasons.NON_BREAKING_BEHAVIOR_FIX)
public void testClassGetMethodsNoDupes() {
Method[] methods = ArrayList.class.getMethods();
Set<String> set = new HashSet<String>();
diff --git a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
index b9e942eca0d..1511471cf24 100644
--- a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
@@ -18,6 +18,8 @@ package libcore.java.util.zip;
import android.system.OsConstants;
import libcore.io.Libcore;
+import libcore.test.annotation.NonCts;
+import libcore.test.reasons.NonCtsReasons;
import java.io.BufferedOutputStream;
import java.io.File;
@@ -41,6 +43,7 @@ public final class ZipFileTest extends AbstractZipFileTest {
}
// b/31077136
+ @NonCts(bug = 338503591, reason = NonCtsReasons.NON_BREAKING_BEHAVIOR_FIX)
public void test_FileNotFound() throws Exception {
File nonExistentFile = new File("fileThatDefinitelyDoesntExist.zip");
assertFalse(nonExistentFile.exists());
diff --git a/ojluni/src/main/java/java/awt/font/TEST_MAPPING b/ojluni/src/main/java/java/awt/font/TEST_MAPPING
index 8bc46cc8702..76ff4049ee8 100644
--- a/ojluni/src/main/java/java/awt/font/TEST_MAPPING
+++ b/ojluni/src/main/java/java/awt/font/TEST_MAPPING
@@ -9,12 +9,7 @@
]
},
{
- "name": "CtsLibcoreOjTestCases",
- "options": [
- {
- "include-filter": "test.java.awt.font"
- }
- ]
+ "name": "CtsLibcoreOjTestCases_awt_font"
}
]
-} \ No newline at end of file
+}
diff --git a/ojluni/src/main/java/java/io/File.java b/ojluni/src/main/java/java/io/File.java
index 00375341184..ba90a3f6095 100644
--- a/ojluni/src/main/java/java/io/File.java
+++ b/ojluni/src/main/java/java/io/File.java
@@ -26,6 +26,11 @@
package java.io;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
+
+import dalvik.annotation.compat.VersionCodes;
+
import java.net.URI;
import java.net.URL;
import java.net.MalformedURLException;
@@ -611,6 +616,17 @@ public class File
return fs.canonicalize(fs.resolve(this));
}
+ // Android-added: Remove parent directory /.. at the rootfs. http://b/312399441
+ /**
+ * Canonicalize the parent directory of the root directory when app targets SDK level 35
+ * (Android 15) or higher. "/.." can be canonicalized into "/" according to POSIX.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = VersionCodes.VANILLA_ICE_CREAM)
+ public static final long CANONICALIZE_PARENT_OF_ROOT_DIR = 312399441L;
+
/**
* Returns the canonical form of this abstract pathname. Equivalent to
* <code>new&nbsp;File(this.{@link #getCanonicalPath})</code>.
diff --git a/ojluni/src/main/java/java/io/UnixFileSystem.java b/ojluni/src/main/java/java/io/UnixFileSystem.java
index 1bf876b1a20..32840973328 100644
--- a/ojluni/src/main/java/java/io/UnixFileSystem.java
+++ b/ojluni/src/main/java/java/io/UnixFileSystem.java
@@ -25,10 +25,17 @@
package java.io;
+import static java.io.File.CANONICALIZE_PARENT_OF_ROOT_DIR;
+
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.system.ErrnoException;
import android.system.OsConstants;
+import dalvik.annotation.compat.VersionCodes;
import dalvik.system.BlockGuard;
+import dalvik.system.VMRuntime;
import libcore.io.Libcore;
@@ -229,7 +236,19 @@ class UnixFileSystem extends FileSystem {
return res;
}
}
- private native String canonicalize0(String path) throws IOException;
+
+ // BEGIN Android-changed: Remove parent directory /.. at the rootfs. http://b/312399441
+ private String canonicalize0(String path) throws IOException {
+ boolean isAtLeastTargetSdk35 =
+ VMRuntime.getSdkVersion() >= VersionCodes.VANILLA_ICE_CREAM &&
+ Compatibility.isChangeEnabled(CANONICALIZE_PARENT_OF_ROOT_DIR);
+ return canonicalize0(path, isAtLeastTargetSdk35);
+ }
+
+ private native String canonicalize0(String path, boolean isAtLeastTargetSdk35)
+ throws IOException;
+ // END Android-changed: Remove parent directory /.. at the rootfs. http://b/312399441
+
// Best-effort attempt to get parent of this path; used for
// optimization of filename canonicalization. This must return null for
// any cases where the code in canonicalize_md.c would throw an
diff --git a/ojluni/src/main/java/java/nio/file/attribute/TEST_MAPPING b/ojluni/src/main/java/java/nio/file/attribute/TEST_MAPPING
index b6c90d44536..6c130c4f350 100644
--- a/ojluni/src/main/java/java/nio/file/attribute/TEST_MAPPING
+++ b/ojluni/src/main/java/java/nio/file/attribute/TEST_MAPPING
@@ -9,12 +9,7 @@
]
},
{
- "name": "CtsLibcoreOjTestCases",
- "options": [
- {
- "include-filter": "test.java.nio.file.attribute"
- }
- ]
+ "name": "CtsLibcoreOjTestCases_nio_file_attribute"
}
]
-} \ No newline at end of file
+}
diff --git a/ojluni/src/main/java/java/security/cert/TEST_MAPPING b/ojluni/src/main/java/java/security/cert/TEST_MAPPING
index 267b914face..0c96cc48208 100644
--- a/ojluni/src/main/java/java/security/cert/TEST_MAPPING
+++ b/ojluni/src/main/java/java/security/cert/TEST_MAPPING
@@ -18,12 +18,7 @@
]
},
{
- "name": "CtsLibcoreOjTestCases",
- "options": [
- {
- "include-filter": "test.java.security.cert"
- }
- ]
+ "name": "CtsLibcoreOjTestCases_security_cert"
}
]
-} \ No newline at end of file
+}
diff --git a/ojluni/src/main/java/java/time/TEST_MAPPING b/ojluni/src/main/java/java/time/TEST_MAPPING
index 54d2ea6bf20..6956e2fd933 100644
--- a/ojluni/src/main/java/java/time/TEST_MAPPING
+++ b/ojluni/src/main/java/java/time/TEST_MAPPING
@@ -11,18 +11,7 @@
],
"presubmit-large": [
{
- "name": "CtsLibcoreOjTestCases",
- "options": [
- {
- "include-filter": "tck.java.time"
- },
- {
- "include-filter": "tck.java.time.serial"
- },
- {
- "include-filter": "test.java.time"
- }
- ]
+ "name": "CtsLibcoreOjTestCases_time"
}
]
}
diff --git a/ojluni/src/main/java/java/time/chrono/TEST_MAPPING b/ojluni/src/main/java/java/time/chrono/TEST_MAPPING
index 461c15d4d55..0e310657f59 100644
--- a/ojluni/src/main/java/java/time/chrono/TEST_MAPPING
+++ b/ojluni/src/main/java/java/time/chrono/TEST_MAPPING
@@ -9,18 +9,7 @@
]
},
{
- "name": "CtsLibcoreOjTestCases",
- "options": [
- {
- "include-filter": "tck.java.time.chrono.serial"
- },
- {
- "include-filter": "tck.java.time.chrono"
- },
- {
- "include-filter": "test.java.time.chrono"
- }
- ]
+ "name": "CtsLibcoreOjTestCases_time_chrono"
}
]
-} \ No newline at end of file
+}
diff --git a/ojluni/src/main/java/java/time/format/TEST_MAPPING b/ojluni/src/main/java/java/time/format/TEST_MAPPING
index 42286e44667..9de70982a0f 100644
--- a/ojluni/src/main/java/java/time/format/TEST_MAPPING
+++ b/ojluni/src/main/java/java/time/format/TEST_MAPPING
@@ -9,15 +9,7 @@
]
},
{
- "name": "CtsLibcoreOjTestCases",
- "options": [
- {
- "include-filter": "tck.java.time.format"
- },
- {
- "include-filter": "test.java.time.format"
- }
- ]
+ "name": "CtsLibcoreOjTestCases_time_format"
}
]
-} \ No newline at end of file
+}
diff --git a/ojluni/src/main/java/java/time/temporal/TEST_MAPPING b/ojluni/src/main/java/java/time/temporal/TEST_MAPPING
index deac48a2378..f40206fdefd 100644
--- a/ojluni/src/main/java/java/time/temporal/TEST_MAPPING
+++ b/ojluni/src/main/java/java/time/temporal/TEST_MAPPING
@@ -9,18 +9,7 @@
]
},
{
- "name": "CtsLibcoreOjTestCases",
- "options": [
- {
- "include-filter": "test.java.time.temporal"
- },
- {
- "include-filter": "tck.java.time.temporal"
- },
- {
- "include-filter": "tck.java.time.temporal.serial"
- }
- ]
+ "name": "CtsLibcoreOjTestCases_time_temporal"
}
]
-} \ No newline at end of file
+}
diff --git a/ojluni/src/main/java/java/time/zone/TEST_MAPPING b/ojluni/src/main/java/java/time/zone/TEST_MAPPING
index 1c8c89e1255..00aefafbf02 100644
--- a/ojluni/src/main/java/java/time/zone/TEST_MAPPING
+++ b/ojluni/src/main/java/java/time/zone/TEST_MAPPING
@@ -9,18 +9,7 @@
]
},
{
- "name": "CtsLibcoreOjTestCases",
- "options": [
- {
- "include-filter": "tck.java.time.zone"
- },
- {
- "include-filter": "tck.java.time.zone.serial"
- },
- {
- "include-filter": "test.java.time.zone"
- }
- ]
+ "name": "CtsLibcoreOjTestCases_time_zone"
}
]
-} \ No newline at end of file
+}
diff --git a/ojluni/src/main/native/UnixFileSystem_md.c b/ojluni/src/main/native/UnixFileSystem_md.c
index 6f432fddf1d..02dd29794bc 100644
--- a/ojluni/src/main/native/UnixFileSystem_md.c
+++ b/ojluni/src/main/native/UnixFileSystem_md.c
@@ -41,6 +41,7 @@
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
+#include <stdbool.h>
#include "jni.h"
#include "jni_util.h"
@@ -99,18 +100,22 @@ Java_java_io_UnixFileSystem_initIDs(JNIEnv *env, jclass cls)
// Android-changed: hidden to avoid conflict with libm (b/135018555)
__attribute__((visibility("hidden")))
-extern int canonicalize(char *path, const char *out, int len);
+extern int canonicalize(char *path, const char *out, int len,
+ // Android-added: Remove parent directory /.. at the rootfs. http://b/312399441
+ bool isAtLeastTargetSdk35);
JNIEXPORT jstring JNICALL
Java_java_io_UnixFileSystem_canonicalize0(JNIEnv *env, jobject this,
- jstring pathname)
+ jstring pathname,
+ // Android-added: Remove parent directory /.. at the rootfs. http://b/312399441
+ jboolean isAtLeastTargetSdk35)
{
jstring rv = NULL;
WITH_PLATFORM_STRING(env, pathname, path) {
char canonicalPath[PATH_MAX];
if (canonicalize((char *)path,
- canonicalPath, PATH_MAX) < 0) {
+ canonicalPath, PATH_MAX, isAtLeastTargetSdk35) < 0) {
JNU_ThrowIOExceptionWithLastError(env, "Bad pathname");
} else {
#ifdef MACOSX
@@ -554,7 +559,7 @@ Java_java_io_UnixFileSystem_getNameMax0(JNIEnv *env, jobject this,
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(UnixFileSystem, initIDs, "()V"),
- NATIVE_METHOD(UnixFileSystem, canonicalize0, "(Ljava/lang/String;)Ljava/lang/String;"),
+ NATIVE_METHOD(UnixFileSystem, canonicalize0, "(Ljava/lang/String;Z)Ljava/lang/String;"),
NATIVE_METHOD(UnixFileSystem, getBooleanAttributes0, "(Ljava/lang/String;)I"),
NATIVE_METHOD(UnixFileSystem, getNameMax0, "(Ljava/lang/String;)J"),
NATIVE_METHOD(UnixFileSystem, setPermission0, "(Ljava/io/File;IZZ)Z"),
diff --git a/ojluni/src/main/native/canonicalize_md.c b/ojluni/src/main/native/canonicalize_md.c
index ed926191193..a012dbf3510 100644
--- a/ojluni/src/main/native/canonicalize_md.c
+++ b/ojluni/src/main/native/canonicalize_md.c
@@ -27,6 +27,7 @@
* Pathname canonicalization for Unix file systems
*/
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -165,12 +166,16 @@ joinNames(char *names, int nc, char **ix)
after invoking the realpath() procedure. */
static void
-collapse(char *path)
+collapse(char *path,
+ // Android-added: Remove parent directory /.. at the rootfs. http://b/312399441
+ bool isAtLeastTargetSdk35)
{
// Android-changed: Remove consecutive duplicate path separators "//". b/267617531
removeDupSeparator(path);
- char *names = (path[0] == '/') ? path + 1 : path; /* Preserve first '/' */
+ // Android-changed: Remove parent directory /.. at the rootfs. http://b/312399441
+ bool isPathAbsolute = (path[0] == '/');
+ char *names = isPathAbsolute ? path + 1 : path; /* Preserve first '/' */
int nc;
char **ix;
int i, j;
@@ -211,12 +216,18 @@ collapse(char *path)
ix[i] = 0;
}
else {
- /* If there is a preceding name, remove both that name and this
+ /* If there is a preceding name and at the rootfs, remove both that name and this
instance of ".."; otherwise, leave the ".." as is */
for (j = i - 1; j >= 0; j--) {
if (ix[j]) break;
}
- if (j < 0) continue;
+ if (j < 0) {
+ // Android-added: Remove parent directory /.. at the rootfs. http://b/312399441
+ if (isPathAbsolute && isAtLeastTargetSdk35) {
+ ix[i] = 0;
+ }
+ continue;
+ }
ix[j] = 0;
ix[i] = 0;
}
@@ -235,7 +246,9 @@ collapse(char *path)
// Android-changed: hidden to avoid conflict with libm (b/135018555)
__attribute__((visibility("hidden")))
int
-canonicalize(char *original, char *resolved, int len)
+canonicalize(char *original, char *resolved, int len,
+ // Android-added: Remove parent directory /.. at the rootfs. http://b/312399441
+ bool isAtLeastTargetSdk35)
{
if (len < PATH_MAX) {
errno = EINVAL;
@@ -252,7 +265,9 @@ canonicalize(char *original, char *resolved, int len)
/* First try realpath() on the entire path */
if (realpath(original, resolved)) {
/* That worked, so return it */
- collapse(resolved);
+ // Android-changed: Remove parent directory /.. at the rootfs. http://b/312399441
+ // collapse(resolved);
+ collapse(resolved, isAtLeastTargetSdk35);
return 0;
}
else {
@@ -326,14 +341,18 @@ canonicalize(char *original, char *resolved, int len)
p++;
}
strcpy(r + rn, p);
- collapse(r);
+ // Android-changed: Remove parent directory /.. at the rootfs. http://b/312399441
+ // collapse(r);
+ collapse(r, isAtLeastTargetSdk35);
}
else {
/* Nothing resolved, so just return the original path */
// Android-changed: Avoid crash in getCanonicalPath() due to a long path. b/266432364
nameMax = pathconf("/", _PC_NAME_MAX);
strcpy(resolved, path);
- collapse(resolved);
+ // Android-changed: Remove parent directory /.. at the rootfs. http://b/312399441
+ // collapse(resolved);
+ collapse(resolved, isAtLeastTargetSdk35);
}
// BEGIN Android-added: Avoid crash in getCanonicalPath() due to a long path. b/266432364
diff --git a/ojluni/src/main/native/java_io_UnixFileSystem.h b/ojluni/src/main/native/java_io_UnixFileSystem.h
index 87187b416c7..aa324634a27 100644
--- a/ojluni/src/main/native/java_io_UnixFileSystem.h
+++ b/ojluni/src/main/native/java_io_UnixFileSystem.h
@@ -60,7 +60,9 @@ extern "C" {
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_java_io_UnixFileSystem_canonicalize0
- (JNIEnv *, jobject, jstring);
+ (JNIEnv *, jobject, jstring,
+ // Android-added: Remove parent directory /.. at the rootfs. http://b/312399441
+ jboolean);
/*
* Class: java_io_UnixFileSystem
diff --git a/ojluni/src/main/native/jni_util.c b/ojluni/src/main/native/jni_util.c
index 6c6e5ba1629..1f7ae7d1cd2 100644
--- a/ojluni/src/main/native/jni_util.c
+++ b/ojluni/src/main/native/jni_util.c
@@ -839,6 +839,8 @@ JNU_ReleaseStringPlatformChars(JNIEnv *env, jstring jstr, const char *str)
* VM can find it when loading system classes.
*
*/
+// Android-removed: Remove unused Canonicalize().
+/*
// Android-changed: hidden to avoid conflict with libm (b/135018555)
__attribute__((visibility("hidden")))
extern int canonicalize(char *path, const char *out, int len);
@@ -846,9 +848,10 @@ extern int canonicalize(char *path, const char *out, int len);
JNIEXPORT int
Canonicalize(JNIEnv *env, char *orig, char *out, int len)
{
- /* canonicalize an already natived path */
+ * canonicalize an already natived path *
return canonicalize(orig, out, len);
}
+*/
JNIEXPORT jclass JNICALL
JNU_ClassString(JNIEnv *env)
diff --git a/test-rules/src/main/java/libcore/test/annotation/NonCts.java b/test-rules/src/main/java/libcore/test/annotation/NonCts.java
index f028ee47d43..c280bd48766 100644
--- a/test-rules/src/main/java/libcore/test/annotation/NonCts.java
+++ b/test-rules/src/main/java/libcore/test/annotation/NonCts.java
@@ -27,9 +27,13 @@ import java.lang.annotation.Target;
* Note that every annotation element below should be associated to a field in
* {@link vogar.expect.Expectation}, because it will be de- and serialized by
* {@link vogar.expect.ExpectationStore} for back-porting to an older branch.
+ *
+ * @deprecated All CTS modules supporting @NonCts annotations are expected to migrate to MCTS.
+ * Please use {@link NonMts} to skip test in the MCTS instead.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
+@Deprecated
public @interface NonCts {
/**
* Optional bug id showing why this test fails / shouldn't run in MTS.