• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package android.content.res;
17 
18 import android.annotation.IntDef;
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.compat.annotation.UnsupportedAppUsage;
22 import android.content.om.OverlayableInfo;
23 import android.content.res.loader.AssetsProvider;
24 import android.content.res.loader.ResourcesProvider;
25 import android.ravenwood.annotation.RavenwoodClassLoadHook;
26 import android.ravenwood.annotation.RavenwoodKeepWholeClass;
27 import android.text.TextUtils;
28 import android.util.Log;
29 
30 import com.android.internal.annotations.GuardedBy;
31 
32 import dalvik.annotation.optimization.CriticalNative;
33 
34 import java.io.FileDescriptor;
35 import java.io.IOException;
36 import java.io.PrintWriter;
37 import java.lang.annotation.Retention;
38 import java.lang.annotation.RetentionPolicy;
39 import java.util.Objects;
40 
41 /**
42  * The loaded, immutable, in-memory representation of an APK.
43  *
44  * The main implementation is native C++ and there is very little API surface exposed here. The APK
45  * is mainly accessed via {@link AssetManager}.
46  *
47  * Since the ApkAssets instance is immutable, it can be reused and shared across AssetManagers,
48  * making the creation of AssetManagers very cheap.
49  * @hide
50  */
51 @RavenwoodKeepWholeClass
52 @RavenwoodClassLoadHook(RavenwoodClassLoadHook.LIBANDROID_LOADING_HOOK)
53 public final class ApkAssets {
54     private static final boolean DEBUG = false;
55 
56     /**
57      * The apk assets contains framework resource values specified by the system.
58      * This allows some functions to filter out this package when computing what
59      * configurations/resources are available.
60      */
61     public static final int PROPERTY_SYSTEM = 1 << 0;
62 
63     /**
64      * The apk assets is a shared library or was loaded as a shared library by force.
65      * The package ids of dynamic apk assets are assigned at runtime instead of compile time.
66      */
67     public static final int PROPERTY_DYNAMIC = 1 << 1;
68 
69     /**
70      * The apk assets has been loaded dynamically using a {@link ResourcesProvider}.
71      * Loader apk assets overlay resources like RROs except they are not backed by an idmap.
72      */
73     public static final int PROPERTY_LOADER = 1 << 2;
74 
75     /**
76      * The apk assets is a RRO.
77      * An RRO overlays resource values of its target package.
78      */
79     private static final int PROPERTY_OVERLAY = 1 << 3;
80 
81     /**
82      * The apk assets is owned by the application running in this process and incremental crash
83      * protections for this APK must be disabled.
84      */
85     public static final int PROPERTY_DISABLE_INCREMENTAL_HARDENING = 1 << 4;
86 
87     /**
88      * The apk assets only contain the overlayable declarations information.
89      */
90     public static final int PROPERTY_ONLY_OVERLAYABLES = 1 << 5;
91 
92     /** Flags that change the behavior of loaded apk assets. */
93     @IntDef(prefix = { "PROPERTY_" }, value = {
94             PROPERTY_SYSTEM,
95             PROPERTY_DYNAMIC,
96             PROPERTY_LOADER,
97             PROPERTY_OVERLAY,
98     })
99     @Retention(RetentionPolicy.SOURCE)
100     public @interface PropertyFlags {}
101 
102     /** The path used to load the apk assets represents an APK file. */
103     private static final int FORMAT_APK = 0;
104 
105     /** The path used to load the apk assets represents an idmap file. */
106     private static final int FORMAT_IDMAP = 1;
107 
108     /** The path used to load the apk assets represents an resources.arsc file. */
109     private static final int FORMAT_ARSC = 2;
110 
111     /** the path used to load the apk assets represents a directory. */
112     private static final int FORMAT_DIR = 3;
113 
114     // Format types that change how the apk assets are loaded.
115     @IntDef(prefix = { "FORMAT_" }, value = {
116             FORMAT_APK,
117             FORMAT_IDMAP,
118             FORMAT_ARSC,
119             FORMAT_DIR
120     })
121     @Retention(RetentionPolicy.SOURCE)
122     public @interface FormatType {}
123 
124     @GuardedBy("this")
125     private long mNativePtr;  // final, except cleared in finalizer.
126 
127     @Nullable
128     @GuardedBy("this")
129     private StringBlock mStringBlock;  // null or closed if mNativePtr = 0.
130 
131     @PropertyFlags
132     private final int mFlags;
133 
134     private final boolean mIsOverlay;
135 
136     @Nullable
137     private final AssetsProvider mAssets;
138 
139     @NonNull
140     private String mName;
141 
142     private static final int UPTODATE_FALSE = 0;
143     private static final int UPTODATE_TRUE = 1;
144     private static final int UPTODATE_ALWAYS_TRUE = 2;
145 
146     // Start with the only value that may change later and would force a native call to
147     // double check it.
148     private int mPreviousUpToDateResult = UPTODATE_TRUE;
149 
150     /**
151      * Creates a new ApkAssets instance from the given path on disk.
152      *
153      * @param path The path to an APK on disk.
154      * @return a new instance of ApkAssets.
155      * @throws IOException if a disk I/O error or parsing error occurred.
156      */
loadFromPath(@onNull String path)157     public static @NonNull ApkAssets loadFromPath(@NonNull String path) throws IOException {
158         return loadFromPath(path, 0 /* flags */);
159     }
160 
161     /**
162      * Creates a new ApkAssets instance from the given path on disk.
163      *
164      * @param path The path to an APK on disk.
165      * @param flags flags that change the behavior of loaded apk assets
166      * @return a new instance of ApkAssets.
167      * @throws IOException if a disk I/O error or parsing error occurred.
168      */
loadFromPath(@onNull String path, @PropertyFlags int flags)169     public static @NonNull ApkAssets loadFromPath(@NonNull String path, @PropertyFlags int flags)
170             throws IOException {
171         return new ApkAssets(FORMAT_APK, path, flags, null /* assets */);
172     }
173 
174     /**
175      * Creates a new ApkAssets instance from the given path on disk.
176      *
177      * @param path The path to an APK on disk.
178      * @param flags flags that change the behavior of loaded apk assets
179      * @param assets The assets provider that overrides the loading of file-based resources
180      * @return a new instance of ApkAssets.
181      * @throws IOException if a disk I/O error or parsing error occurred.
182      */
loadFromPath(@onNull String path, @PropertyFlags int flags, @Nullable AssetsProvider assets)183     public static @NonNull ApkAssets loadFromPath(@NonNull String path, @PropertyFlags int flags,
184             @Nullable AssetsProvider assets) throws IOException {
185         return new ApkAssets(FORMAT_APK, path, flags, assets);
186     }
187 
188     /**
189      * Creates a new ApkAssets instance from the given file descriptor.
190      *
191      * Performs a dup of the underlying fd, so you must take care of still closing
192      * the FileDescriptor yourself (and can do that whenever you want).
193      *
194      * @param fd The FileDescriptor of an open, readable APK.
195      * @param friendlyName The friendly name used to identify this ApkAssets when logging.
196      * @param flags flags that change the behavior of loaded apk assets
197      * @param assets The assets provider that overrides the loading of file-based resources
198      * @return a new instance of ApkAssets.
199      * @throws IOException if a disk I/O error or parsing error occurred.
200      */
loadFromFd(@onNull FileDescriptor fd, @NonNull String friendlyName, @PropertyFlags int flags, @Nullable AssetsProvider assets)201     public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd,
202             @NonNull String friendlyName, @PropertyFlags int flags,
203             @Nullable AssetsProvider assets) throws IOException {
204         return new ApkAssets(FORMAT_APK, fd, friendlyName, flags, assets);
205     }
206 
207     /**
208      * Creates a new ApkAssets instance from the given file descriptor.
209      *
210      * Performs a dup of the underlying fd, so you must take care of still closing
211      * the FileDescriptor yourself (and can do that whenever you want).
212      *
213      * @param fd The FileDescriptor of an open, readable APK.
214      * @param friendlyName The friendly name used to identify this ApkAssets when logging.
215      * @param offset The location within the file that the apk starts. This must be 0 if length is
216      *               {@link AssetFileDescriptor#UNKNOWN_LENGTH}.
217      * @param length The number of bytes of the apk, or {@link AssetFileDescriptor#UNKNOWN_LENGTH}
218      *               if it extends to the end of the file.
219      * @param flags flags that change the behavior of loaded apk assets
220      * @param assets The assets provider that overrides the loading of file-based resources
221      * @return a new instance of ApkAssets.
222      * @throws IOException if a disk I/O error or parsing error occurred.
223      */
loadFromFd(@onNull FileDescriptor fd, @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags, @Nullable AssetsProvider assets)224     public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd,
225             @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags,
226             @Nullable AssetsProvider assets)
227             throws IOException {
228         return new ApkAssets(FORMAT_APK, fd, friendlyName, offset, length, flags, assets);
229     }
230 
231     /**
232      * Creates a new ApkAssets instance from the IDMAP at idmapPath. The overlay APK path
233      * is encoded within the IDMAP.
234      *
235      * @param idmapPath Path to the IDMAP of an overlay APK.
236      * @param flags flags that change the behavior of loaded apk assets
237      * @return a new instance of ApkAssets.
238      * @throws IOException if a disk I/O error or parsing error occurred.
239      */
loadOverlayFromPath(@onNull String idmapPath, @PropertyFlags int flags)240     public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath,
241             @PropertyFlags int flags) throws IOException {
242         return new ApkAssets(FORMAT_IDMAP, idmapPath, flags, null /* assets */);
243     }
244 
245     /**
246      * Creates a new ApkAssets instance from the given file descriptor representing a resources.arsc
247      * for use with a {@link ResourcesProvider}.
248      *
249      * Performs a dup of the underlying fd, so you must take care of still closing
250      * the FileDescriptor yourself (and can do that whenever you want).
251      *
252      * @param fd The FileDescriptor of an open, readable resources.arsc.
253      * @param friendlyName The friendly name used to identify this ApkAssets when logging.
254      * @param flags flags that change the behavior of loaded apk assets
255      * @param assets The assets provider that overrides the loading of file-based resources
256      * @return a new instance of ApkAssets.
257      * @throws IOException if a disk I/O error or parsing error occurred.
258      */
loadTableFromFd(@onNull FileDescriptor fd, @NonNull String friendlyName, @PropertyFlags int flags, @Nullable AssetsProvider assets)259     public static @NonNull ApkAssets loadTableFromFd(@NonNull FileDescriptor fd,
260             @NonNull String friendlyName, @PropertyFlags int flags,
261             @Nullable AssetsProvider assets) throws IOException {
262         return new ApkAssets(FORMAT_ARSC, fd, friendlyName, flags, assets);
263     }
264 
265     /**
266      * Creates a new ApkAssets instance from the given file descriptor representing a resources.arsc
267      * for use with a {@link ResourcesProvider}.
268      *
269      * Performs a dup of the underlying fd, so you must take care of still closing
270      * the FileDescriptor yourself (and can do that whenever you want).
271      *
272      * @param fd The FileDescriptor of an open, readable resources.arsc.
273      * @param friendlyName The friendly name used to identify this ApkAssets when logging.
274      * @param offset The location within the file that the table starts. This must be 0 if length is
275      *               {@link AssetFileDescriptor#UNKNOWN_LENGTH}.
276      * @param length The number of bytes of the table, or {@link AssetFileDescriptor#UNKNOWN_LENGTH}
277      *               if it extends to the end of the file.
278      * @param flags flags that change the behavior of loaded apk assets
279      * @param assets The assets provider that overrides the loading of file-based resources
280      * @return a new instance of ApkAssets.
281      * @throws IOException if a disk I/O error or parsing error occurred.
282      */
loadTableFromFd(@onNull FileDescriptor fd, @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags, @Nullable AssetsProvider assets)283     public static @NonNull ApkAssets loadTableFromFd(@NonNull FileDescriptor fd,
284             @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags,
285             @Nullable AssetsProvider assets) throws IOException {
286         return new ApkAssets(FORMAT_ARSC, fd, friendlyName, offset, length, flags, assets);
287     }
288 
289     /**
290      * Creates a new ApkAssets instance from the given directory path. The directory should have the
291      * file structure of an APK.
292      *
293      * @param path The path to a directory on disk.
294      * @param flags flags that change the behavior of loaded apk assets
295      * @param assets The assets provider that overrides the loading of file-based resources
296      * @return a new instance of ApkAssets.
297      * @throws IOException if a disk I/O error or parsing error occurred.
298      */
loadFromDir(@onNull String path, @PropertyFlags int flags, @Nullable AssetsProvider assets)299     public static @NonNull ApkAssets loadFromDir(@NonNull String path,
300             @PropertyFlags int flags, @Nullable AssetsProvider assets) throws IOException {
301         return new ApkAssets(FORMAT_DIR, path, flags, assets);
302     }
303 
304     /**
305      * Generates an entirely empty ApkAssets. Needed because the ApkAssets instance and presence
306      * is required for a lot of APIs, and it's easier to have a non-null reference rather than
307      * tracking a separate identifier.
308      *
309      * @param flags flags that change the behavior of loaded apk assets
310      * @param assets The assets provider that overrides the loading of file-based resources
311      */
312     @NonNull
loadEmptyForLoader(@ropertyFlags int flags, @Nullable AssetsProvider assets)313     public static ApkAssets loadEmptyForLoader(@PropertyFlags int flags,
314             @Nullable AssetsProvider assets) {
315         return new ApkAssets(flags, assets);
316     }
317 
ApkAssets(@ormatType int format, @NonNull String path, @PropertyFlags int flags, @Nullable AssetsProvider assets)318     private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags,
319             @Nullable AssetsProvider assets) throws IOException {
320         this(format, flags, assets, path);
321         Objects.requireNonNull(path, "path");
322         mNativePtr = nativeLoad(format, path, flags, assets);
323         mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
324     }
325 
ApkAssets(@ormatType int format, @NonNull FileDescriptor fd, @NonNull String friendlyName, @PropertyFlags int flags, @Nullable AssetsProvider assets)326     private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
327             @NonNull String friendlyName, @PropertyFlags int flags, @Nullable AssetsProvider assets)
328             throws IOException {
329         this(format, flags, assets, friendlyName);
330         Objects.requireNonNull(fd, "fd");
331         Objects.requireNonNull(friendlyName, "friendlyName");
332         mNativePtr = nativeLoadFd(format, fd, friendlyName, flags, assets);
333         mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
334     }
335 
ApkAssets(@ormatType int format, @NonNull FileDescriptor fd, @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags, @Nullable AssetsProvider assets)336     private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
337             @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags,
338             @Nullable AssetsProvider assets) throws IOException {
339         this(format, flags, assets, friendlyName);
340         Objects.requireNonNull(fd, "fd");
341         Objects.requireNonNull(friendlyName, "friendlyName");
342         mNativePtr = nativeLoadFdOffsets(format, fd, friendlyName, offset, length, flags, assets);
343         mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
344     }
345 
ApkAssets(@ropertyFlags int flags, @Nullable AssetsProvider assets)346     private ApkAssets(@PropertyFlags int flags, @Nullable AssetsProvider assets) {
347         this(FORMAT_APK, flags, assets, "empty");
348         mNativePtr = nativeLoadEmpty(flags, assets);
349         mStringBlock = null;
350     }
351 
ApkAssets(@ormatType int format, @PropertyFlags int flags, @Nullable AssetsProvider assets, @NonNull String name)352     private ApkAssets(@FormatType int format, @PropertyFlags int flags,
353             @Nullable AssetsProvider assets, @NonNull String name) {
354         mFlags = flags;
355         mAssets = assets;
356         mIsOverlay = format == FORMAT_IDMAP;
357         if (DEBUG) mName = name;
358     }
359 
360     @UnsupportedAppUsage
getAssetPath()361     public @NonNull String getAssetPath() {
362         synchronized (this) {
363             return TextUtils.emptyIfNull(nativeGetAssetPath(mNativePtr));
364         }
365     }
366 
367     /** @hide */
getDebugName()368     public @NonNull String getDebugName() {
369         synchronized (this) {
370             return mNativePtr == 0 ? "<destroyed>" : nativeGetDebugName(mNativePtr);
371         }
372     }
373 
374     @Nullable
getStringFromPool(int idx)375     CharSequence getStringFromPool(int idx) {
376         if (mStringBlock == null) {
377             return null;
378         }
379 
380         synchronized (this) {
381             return mStringBlock.getSequence(idx);
382         }
383     }
384 
385     /** Returns whether this apk assets was loaded using a {@link ResourcesProvider}. */
isForLoader()386     public boolean isForLoader() {
387         return (mFlags & PROPERTY_LOADER) != 0;
388     }
389 
390     /**
391      * Returns the assets provider that overrides the loading of assets present in this apk assets.
392      */
393     @Nullable
getAssetsProvider()394     public AssetsProvider getAssetsProvider() {
395         return mAssets;
396     }
397 
398     /**
399      * Retrieve a parser for a compiled XML file. This is associated with a single APK and
400      * <em>NOT</em> a full AssetManager. This means that shared-library references will not be
401      * dynamically assigned runtime package IDs.
402      *
403      * @param fileName The path to the file within the APK.
404      * @return An XmlResourceParser.
405      * @throws IOException if the file was not found or an error occurred retrieving it.
406      */
openXml(@onNull String fileName)407     public @NonNull XmlResourceParser openXml(@NonNull String fileName) throws IOException {
408         Objects.requireNonNull(fileName, "fileName");
409         synchronized (this) {
410             long nativeXmlPtr = nativeOpenXml(mNativePtr, fileName);
411             try (XmlBlock block = new XmlBlock(null, nativeXmlPtr, true)) {
412                 XmlResourceParser parser = block.newParser();
413                 // If nativeOpenXml doesn't throw, it will always return a valid native pointer,
414                 // which makes newParser always return non-null. But let's be careful.
415                 if (parser == null) {
416                     throw new AssertionError("block.newParser() returned a null parser");
417                 }
418                 return parser;
419             }
420         }
421     }
422 
423     /** @hide */
424     @Nullable
getOverlayableInfo(String overlayableName)425     public OverlayableInfo getOverlayableInfo(String overlayableName) throws IOException {
426         synchronized (this) {
427             return nativeGetOverlayableInfo(mNativePtr, overlayableName);
428         }
429     }
430 
431     /** @hide */
definesOverlayable()432     public boolean definesOverlayable() throws IOException {
433         synchronized (this) {
434             return nativeDefinesOverlayable(mNativePtr);
435         }
436     }
437 
intervalMs(long beginNs, long endNs)438     private static double intervalMs(long beginNs, long endNs) {
439         return (endNs - beginNs) / 1000000.0;
440     }
441 
442     /**
443      * Returns false if the underlying APK was changed since this ApkAssets was loaded.
444      */
isUpToDate()445     public boolean isUpToDate() {
446         // This function is performance-critical - it's called multiple times on every Resources
447         // object creation, and on few other cache accesses - so it's important to avoid the native
448         // call when we know for sure what it will return (which is the case for both ALWAYS_TRUE
449         // and FALSE).
450         if (mPreviousUpToDateResult != UPTODATE_TRUE) {
451             return mPreviousUpToDateResult == UPTODATE_ALWAYS_TRUE;
452         }
453         final long beforeTs, afterLockTs, afterNativeTs, afterUnlockTs;
454         if (DEBUG) beforeTs = System.nanoTime();
455         final int res;
456         synchronized (this) {
457             if (DEBUG) afterLockTs = System.nanoTime();
458             res = nativeIsUpToDate(mNativePtr);
459             if (DEBUG) afterNativeTs = System.nanoTime();
460         }
461         if (DEBUG) {
462             afterUnlockTs = System.nanoTime();
463             if (afterUnlockTs - beforeTs >= 10L * 1000000) {
464                 Log.d("ApkAssets", "isUpToDate(" + mName + ") took "
465                         + intervalMs(beforeTs, afterUnlockTs)
466                         + " ms: " + intervalMs(beforeTs, afterLockTs)
467                         + " / " + intervalMs(afterLockTs, afterNativeTs)
468                         + " / " + intervalMs(afterNativeTs, afterUnlockTs));
469             }
470         }
471         mPreviousUpToDateResult = res;
472         return res != UPTODATE_FALSE;
473     }
474 
isSystem()475     public boolean isSystem() {
476         return (mFlags & PROPERTY_SYSTEM) != 0;
477     }
478 
isSharedLib()479     public boolean isSharedLib() {
480         return (mFlags & PROPERTY_DYNAMIC) != 0;
481     }
482 
isOverlay()483     public boolean isOverlay() {
484         return mIsOverlay;
485     }
486 
487     @Override
toString()488     public String toString() {
489         return "ApkAssets{path=" + getDebugName() + "}";
490     }
491 
492     @Override
finalize()493     protected void finalize() throws Throwable {
494         close();
495     }
496 
497     /**
498      * Closes this class and the contained {@link #mStringBlock}.
499      */
close()500     public void close() {
501         synchronized (this) {
502             if (mNativePtr != 0) {
503                 if (mStringBlock != null) {
504                     mStringBlock.close();
505                 }
506                 nativeDestroy(mNativePtr);
507                 mNativePtr = 0;
508             }
509         }
510     }
511 
dump(PrintWriter pw, String prefix)512     void dump(PrintWriter pw, String prefix) {
513         pw.println(prefix + "class=" + getClass());
514         pw.println(prefix + "debugName=" + getDebugName());
515         pw.println(prefix + "assetPath=" + getAssetPath());
516     }
517 
nativeLoad(@ormatType int format, @NonNull String path, @PropertyFlags int flags, @Nullable AssetsProvider asset)518     private static native long nativeLoad(@FormatType int format, @NonNull String path,
519             @PropertyFlags int flags, @Nullable AssetsProvider asset) throws IOException;
nativeLoadEmpty(@ropertyFlags int flags, @Nullable AssetsProvider asset)520     private static native long nativeLoadEmpty(@PropertyFlags int flags,
521             @Nullable AssetsProvider asset);
nativeLoadFd(@ormatType int format, @NonNull FileDescriptor fd, @NonNull String friendlyName, @PropertyFlags int flags, @Nullable AssetsProvider asset)522     private static native long nativeLoadFd(@FormatType int format, @NonNull FileDescriptor fd,
523             @NonNull String friendlyName, @PropertyFlags int flags,
524             @Nullable AssetsProvider asset) throws IOException;
nativeLoadFdOffsets(@ormatType int format, @NonNull FileDescriptor fd, @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags, @Nullable AssetsProvider asset)525     private static native long nativeLoadFdOffsets(@FormatType int format,
526             @NonNull FileDescriptor fd, @NonNull String friendlyName, long offset, long length,
527             @PropertyFlags int flags, @Nullable AssetsProvider asset) throws IOException;
nativeDestroy(long ptr)528     private static native void nativeDestroy(long ptr);
nativeGetAssetPath(long ptr)529     private static native @NonNull String nativeGetAssetPath(long ptr);
nativeGetDebugName(long ptr)530     private static native @NonNull String nativeGetDebugName(long ptr);
nativeGetStringBlock(long ptr)531     private static native long nativeGetStringBlock(long ptr);
nativeIsUpToDate(long ptr)532     @CriticalNative private static native int nativeIsUpToDate(long ptr);
nativeOpenXml(long ptr, @NonNull String fileName)533     private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException;
nativeGetOverlayableInfo(long ptr, String overlayableName)534     private static native @Nullable OverlayableInfo nativeGetOverlayableInfo(long ptr,
535             String overlayableName) throws IOException;
nativeDefinesOverlayable(long ptr)536     private static native boolean nativeDefinesOverlayable(long ptr) throws IOException;
537 }
538