libcore: Add hooks for Dynamite module support in GmsCompat
These hooks are necessary for GmsCompat to support Google Play Services'
dynamic module system (Dynamite) without weakening the SELinux sandbox
to allow other apps to open module APKs from
/data/user_de/0/com.google.android.gms/app_chimera/m.
To minimize changes in libcore, each hook is a simple interface method
call that delegates the actual hook code to GmsCompat in
frameworks/base.
Change-Id: I49c7c2b2fe8b356137dbc90955a7b63684253457
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 15741a7..acaaf62 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -226,6 +226,7 @@
public final class DelegateLastClassLoader extends dalvik.system.PathClassLoader {
ctor public DelegateLastClassLoader(String, String, ClassLoader, ClassLoader[]);
+ field public static volatile java.util.function.Function<java.lang.String,java.lang.String> librarySearchPathHook;
}
@Deprecated public final class DexFile {
@@ -244,6 +245,20 @@
method @Deprecated @NonNull public String getStatus();
}
+ public final class DexPathList {
+ ctor public DexPathList(ClassLoader, String);
+ ctor public DexPathList(ClassLoader, String, String, java.io.File);
+ method public void addDexPath(String, java.io.File);
+ method public void addDexPath(String, java.io.File, boolean);
+ method public void addNativePath(java.util.Collection<java.lang.String>);
+ method public Class<?> findClass(String, java.util.List<java.lang.Throwable>);
+ method public String findLibrary(String);
+ method public java.net.URL findResource(String);
+ method public java.util.Enumeration<java.net.URL> findResources(String);
+ method public java.util.List<java.io.File> getNativeLibraryDirectories();
+ field public static volatile java.util.function.Function<dalvik.system.DexPathList,java.nio.ByteBuffer[]> postConstructorBufferHook;
+ }
+
public class PathClassLoader extends dalvik.system.BaseDexClassLoader {
ctor public PathClassLoader(@NonNull String, @Nullable String, @Nullable ClassLoader, @Nullable ClassLoader[]);
}
@@ -380,6 +395,10 @@
package java.io {
+ public class File implements java.lang.Comparable<java.io.File> java.io.Serializable {
+ field public static volatile java.util.function.Function<java.io.File,java.lang.Long> lastModifiedHook;
+ }
+
public final class FileDescriptor {
method public int getInt$();
method public void setInt$(int);
diff --git a/dalvik/src/main/java/dalvik/system/DelegateLastClassLoader.java b/dalvik/src/main/java/dalvik/system/DelegateLastClassLoader.java
index 5966139..0231d9e 100644
--- a/dalvik/src/main/java/dalvik/system/DelegateLastClassLoader.java
+++ b/dalvik/src/main/java/dalvik/system/DelegateLastClassLoader.java
@@ -25,7 +25,9 @@
import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
+import java.util.function.Function;
+import libcore.api.CorePlatformApi;
import libcore.util.NonNull;
import libcore.util.Nullable;
@@ -44,6 +46,14 @@
public final class DelegateLastClassLoader extends PathClassLoader {
/**
+ * Pre-constructor librarySearchPath hook for GmsCompat
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+ public static volatile Function<String, String> librarySearchPathHook;
+
+ /**
* Whether resource loading delegates to the parent class loader. True by default.
*/
private final boolean delegateResourceLoading;
@@ -103,7 +113,7 @@
public DelegateLastClassLoader(@NonNull String dexPath, @Nullable String librarySearchPath,
@Nullable ClassLoader parent, boolean delegateResourceLoading) {
- super(dexPath, librarySearchPath, parent);
+ super(dexPath, filterLibrarySearchPath(librarySearchPath), parent);
this.delegateResourceLoading = delegateResourceLoading;
}
@@ -142,6 +152,12 @@
this.delegateResourceLoading = true;
}
+ private static String filterLibrarySearchPath(String librarySearchPath) {
+ return librarySearchPathHook == null ?
+ librarySearchPath :
+ librarySearchPathHook.apply(librarySearchPath);
+ }
+
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
// First, check whether the class has already been loaded. Return it if that's the
diff --git a/dalvik/src/main/java/dalvik/system/DexPathList.java b/dalvik/src/main/java/dalvik/system/DexPathList.java
index acf5e66..ee2bf10 100644
--- a/dalvik/src/main/java/dalvik/system/DexPathList.java
+++ b/dalvik/src/main/java/dalvik/system/DexPathList.java
@@ -16,6 +16,7 @@
package dalvik.system;
+import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.system.ErrnoException;
import android.system.StructStat;
@@ -33,6 +34,9 @@
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
+import java.util.function.Function;
+
+import libcore.api.CorePlatformApi;
import libcore.io.ClassPathURLStreamHandler;
import libcore.io.IoUtils;
import libcore.io.Libcore;
@@ -54,10 +58,20 @@
*
* @hide
*/
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+@CorePlatformApi(status = CorePlatformApi.Status.STABLE)
public final class DexPathList {
private static final String DEX_SUFFIX = ".dex";
private static final String zipSeparator = "!/";
+ /**
+ * Post-constructor hook for GmsCompat
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+ public static volatile Function<DexPathList, ByteBuffer[]> postConstructorBufferHook;
+
/** class definition context */
@UnsupportedAppUsage
private final ClassLoader definingContext;
@@ -187,6 +201,18 @@
} else {
dexElementsSuppressedExceptions = null;
}
+
+ runGmsCompatHook();
+ }
+
+ private void runGmsCompatHook() {
+ if (postConstructorBufferHook != null) {
+ ByteBuffer[] buffers = postConstructorBufferHook.apply(this);
+ if (buffers != null) {
+ dexElements = null;
+ initByteBufferDexPath(buffers);
+ }
+ }
}
@Override public String toString() {
@@ -332,6 +358,7 @@
}
// This method is not used anymore. Kept around only because there are many legacy users of it.
+ /** @hide */
@SuppressWarnings("unused")
@UnsupportedAppUsage
public static Element[] makeInMemoryDexElements(ByteBuffer[] dexFiles,
diff --git a/ojluni/src/main/java/java/io/File.java b/ojluni/src/main/java/java/io/File.java
index 0037534..06cd18c 100644
--- a/ojluni/src/main/java/java/io/File.java
+++ b/ojluni/src/main/java/java/io/File.java
@@ -26,6 +26,10 @@
package java.io;
+import android.annotation.SystemApi;
+
+import libcore.api.CorePlatformApi;
+
import java.net.URI;
import java.net.URL;
import java.net.MalformedURLException;
@@ -35,6 +39,8 @@
import java.security.AccessController;
import java.nio.file.Path;
import java.nio.file.FileSystems;
+import java.util.function.Function;
+
import sun.security.action.GetPropertyAction;
// Android-added: Info about UTF-8 usage in filenames.
@@ -162,6 +168,14 @@
private static final FileSystem fs = DefaultFileSystem.getFileSystem();
/**
+ * File#lastModified() hook for GmsCompat
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+ public static volatile Function<File, Long> lastModifiedHook;
+
+ /**
* This abstract pathname's normalized pathname string. A normalized
* pathname string uses the default name-separator character and does not
* contain any duplicate or redundant separators.
@@ -934,6 +948,12 @@
if (isInvalid()) {
return 0L;
}
+ if (lastModifiedHook != null) {
+ Long lastModified = lastModifiedHook.apply(this);
+ if (lastModified != null) {
+ return lastModified;
+ }
+ }
return fs.getLastModifiedTime(this);
}