• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric.shadows;
2 
3 import static android.os.Build.VERSION_CODES.M;
4 import static android.os.Build.VERSION_CODES.N_MR1;
5 import static android.os.Build.VERSION_CODES.O;
6 import static android.os.Build.VERSION_CODES.O_MR1;
7 import static android.os.Build.VERSION_CODES.P;
8 import static org.robolectric.res.android.Asset.SEEK_CUR;
9 import static org.robolectric.res.android.Asset.SEEK_SET;
10 import static org.robolectric.res.android.AttributeResolution.kThrowOnBadId;
11 import static org.robolectric.res.android.Errors.BAD_INDEX;
12 import static org.robolectric.res.android.Errors.NO_ERROR;
13 import static org.robolectric.res.android.Util.ALOGV;
14 import static org.robolectric.res.android.Util.isTruthy;
15 import static org.robolectric.util.reflector.Reflector.reflector;
16 
17 import android.content.res.AssetManager;
18 import android.os.Build.VERSION_CODES;
19 import android.os.ParcelFileDescriptor;
20 import android.util.SparseArray;
21 import android.util.TypedValue;
22 import com.google.common.annotations.VisibleForTesting;
23 import com.google.common.base.Preconditions;
24 import com.google.common.base.Strings;
25 import dalvik.system.VMRuntime;
26 import java.io.FileDescriptor;
27 import java.io.FileNotFoundException;
28 import java.io.IOException;
29 import java.nio.file.Files;
30 import java.nio.file.Path;
31 import java.util.ArrayList;
32 import java.util.Collection;
33 import java.util.HashMap;
34 import java.util.List;
35 import java.util.Map;
36 import org.robolectric.RuntimeEnvironment;
37 import org.robolectric.annotation.HiddenApi;
38 import org.robolectric.annotation.Implementation;
39 import org.robolectric.annotation.Implements;
40 import org.robolectric.annotation.RealObject;
41 import org.robolectric.annotation.Resetter;
42 import org.robolectric.res.Fs;
43 import org.robolectric.res.android.Asset;
44 import org.robolectric.res.android.Asset.AccessMode;
45 import org.robolectric.res.android.AssetDir;
46 import org.robolectric.res.android.AssetPath;
47 import org.robolectric.res.android.AttributeResolution;
48 import org.robolectric.res.android.CppAssetManager;
49 import org.robolectric.res.android.DataType;
50 import org.robolectric.res.android.DynamicRefTable;
51 import org.robolectric.res.android.Ref;
52 import org.robolectric.res.android.Registries;
53 import org.robolectric.res.android.ResStringPool;
54 import org.robolectric.res.android.ResTable;
55 import org.robolectric.res.android.ResTable.ResourceName;
56 import org.robolectric.res.android.ResTable.bag_entry;
57 import org.robolectric.res.android.ResTableTheme;
58 import org.robolectric.res.android.ResTable_config;
59 import org.robolectric.res.android.ResXMLParser;
60 import org.robolectric.res.android.ResXMLTree;
61 import org.robolectric.res.android.ResourceTypes.Res_value;
62 import org.robolectric.res.android.String8;
63 import org.robolectric.shadow.api.Shadow;
64 import org.robolectric.shadows.ShadowAssetManager.Picker;
65 
66 // native method impls transliterated from
67 // https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/core/jni/android_util_AssetManager.cpp
68 @Implements(value = AssetManager.class, maxSdk = VERSION_CODES.O_MR1, shadowPicker = Picker.class)
69 @SuppressWarnings("NewApi")
70 public class ShadowArscAssetManager extends ShadowAssetManager.ArscBase {
71 
72   private static final int STYLE_NUM_ENTRIES = 6;
73   private static final int STYLE_TYPE = 0;
74   private static final int STYLE_DATA = 1;
75   private static final int STYLE_ASSET_COOKIE = 2;
76   private static final int STYLE_RESOURCE_ID = 3;
77   private static final int STYLE_CHANGING_CONFIGURATIONS = 4;
78   private static final int STYLE_DENSITY = 5;
79 
80   @RealObject protected AssetManager realObject;
81 
82   private CppAssetManager cppAssetManager;
83 
84   @Resetter
reset()85   public static void reset() {
86     // todo: ShadowPicker doesn't discriminate properly between concrete shadow classes for
87     // resetters...
88     if (RuntimeEnvironment.getApiLevel() < P) {
89       reflector(_AssetManager_.class).setSystem(null);
90       // NATIVE_THEME_REGISTRY.clear();
91       // nativeXMLParserRegistry.clear(); // todo: shouldn't these be freed explicitly? [yes! xw]
92       // NATIVE_ASSET_REGISTRY.clear();
93     }
94   }
95 
96   @Implementation
list(String path)97   protected String[] list(String path) throws IOException {
98     CppAssetManager am = assetManagerForJavaObject();
99 
100     String fileName8 = path;
101     if (fileName8 == null) {
102       return null;
103     }
104 
105     AssetDir dir = am.openDir(fileName8);
106 
107     if (dir == null) {
108       throw new FileNotFoundException(fileName8);
109     }
110 
111     int N = dir.getFileCount();
112 
113     String[] array = new String[dir.getFileCount()];
114 
115     for (int i = 0; i < N; i++) {
116       String8 name = dir.getFileName(i);
117       array[i] = name.string();
118     }
119 
120     return array;
121   }
122 
123   // @HiddenApi @Implementation(minSdk = VERSION_CODES.P)
124   // public void setApkAssets(Object apkAssetsObjects, Object invalidateCaches) {
125   //   throw new UnsupportedOperationException("implement me");
126   // }
127 
128   @HiddenApi
129   @Implementation(maxSdk = N_MR1)
setConfiguration( int mcc, int mnc, String locale, int orientation, int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout, int uiMode, int sdkVersion)130   public final void setConfiguration(
131       int mcc,
132       int mnc,
133       String locale,
134       int orientation,
135       int touchscreen,
136       int density,
137       int keyboard,
138       int keyboardHidden,
139       int navigation,
140       int screenWidth,
141       int screenHeight,
142       int smallestScreenWidthDp,
143       int screenWidthDp,
144       int screenHeightDp,
145       int screenLayout,
146       int uiMode,
147       int sdkVersion) {
148     setConfiguration(
149         mcc,
150         mnc,
151         locale,
152         orientation,
153         touchscreen,
154         density,
155         keyboard,
156         keyboardHidden,
157         navigation,
158         screenWidth,
159         screenHeight,
160         smallestScreenWidthDp,
161         screenWidthDp,
162         screenHeightDp,
163         screenLayout,
164         uiMode,
165         0,
166         sdkVersion);
167   }
168 
169   @HiddenApi
170   @Implementation(minSdk = O)
setConfiguration( int mcc, int mnc, String locale, int orientation, int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout, int uiMode, int colorMode, int sdkVersion)171   public void setConfiguration(
172       int mcc,
173       int mnc,
174       String locale,
175       int orientation,
176       int touchscreen,
177       int density,
178       int keyboard,
179       int keyboardHidden,
180       int navigation,
181       int screenWidth,
182       int screenHeight,
183       int smallestScreenWidthDp,
184       int screenWidthDp,
185       int screenHeightDp,
186       int screenLayout,
187       int uiMode,
188       int colorMode,
189       int sdkVersion) {
190     CppAssetManager am = assetManagerForJavaObject();
191     if (am == null) {
192       return;
193     }
194 
195     ResTable_config config = new ResTable_config();
196     //    memset(&config, 0, sizeof(config));
197 
198     //    const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL;
199 
200     // Constants duplicated from Java class android.content.res.Configuration.
201     int kScreenLayoutRoundMask = 0x300;
202     int kScreenLayoutRoundShift = 8;
203 
204     config.mcc = mcc;
205     config.mnc = mnc;
206     config.orientation = orientation;
207     config.touchscreen = touchscreen;
208     config.density = density;
209     config.keyboard = keyboard;
210     config.inputFlags = keyboardHidden;
211     config.navigation = navigation;
212     config.screenWidth = screenWidth;
213     config.screenHeight = screenHeight;
214     config.smallestScreenWidthDp = smallestScreenWidthDp;
215     config.screenWidthDp = screenWidthDp;
216     config.screenHeightDp = screenHeightDp;
217     config.screenLayout = screenLayout;
218     config.uiMode = uiMode;
219     config.colorMode = (byte) colorMode;
220     config.sdkVersion = sdkVersion;
221     config.minorVersion = 0;
222 
223     // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer
224     // in C++. We must extract the round qualifier out of the Java screenLayout and put it
225     // into screenLayout2.
226     config.screenLayout2 =
227         (byte) ((screenLayout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift);
228 
229     am.setConfiguration(config, locale);
230 
231     //    if (locale != null) env->ReleaseStringUTFChars(locale, locale8);
232   }
233 
234   @HiddenApi
235   @Implementation
dumpTheme(long theme, int priority, String tag, String prefix)236   protected static void dumpTheme(long theme, int priority, String tag, String prefix) {
237     throw new UnsupportedOperationException("not yet implemented");
238   }
239 
240   @Implementation
getResourceName(int resid)241   protected String getResourceName(int resid) {
242     CppAssetManager am = assetManagerForJavaObject();
243 
244     ResourceName name = new ResourceName();
245     if (!am.getResources().getResourceName(resid, true, name)) {
246       return null;
247     }
248 
249     StringBuilder str = new StringBuilder();
250     if (name.packageName != null) {
251       str.append(name.packageName.trim());
252     }
253     if (name.type != null) {
254       if (str.length() > 0) {
255         char div = ':';
256         str.append(div);
257       }
258       str.append(name.type);
259     }
260     if (name.name != null) {
261       if (str.length() > 0) {
262         char div = '/';
263         str.append(div);
264       }
265       str.append(name.name);
266     }
267     return str.toString();
268   }
269 
270   @Implementation
getResourcePackageName(int resid)271   protected String getResourcePackageName(int resid) {
272     CppAssetManager cppAssetManager = assetManagerForJavaObject();
273 
274     ResourceName name = new ResourceName();
275     if (!cppAssetManager.getResources().getResourceName(resid, true, name)) {
276       return null;
277     }
278 
279     return name.packageName.trim();
280   }
281 
282   @Implementation
getResourceTypeName(int resid)283   protected String getResourceTypeName(int resid) {
284     CppAssetManager cppAssetManager = assetManagerForJavaObject();
285 
286     ResourceName name = new ResourceName();
287     if (!cppAssetManager.getResources().getResourceName(resid, true, name)) {
288       return null;
289     }
290 
291     return name.type;
292   }
293 
294   @Implementation
getResourceEntryName(int resid)295   protected String getResourceEntryName(int resid) {
296     CppAssetManager cppAssetManager = assetManagerForJavaObject();
297 
298     ResourceName name = new ResourceName();
299     if (!cppAssetManager.getResources().getResourceName(resid, true, name)) {
300       return null;
301     }
302 
303     return name.name;
304   }
305 
306   //////////// native method implementations
307 
308   //  public native final String[] list(String path)
309   //      throws IOException;
310 
311   //  @HiddenApi @Implementation(minSdk = VERSION_CODES.P)
312   //  public void setApkAssets(Object apkAssetsObjects, Object invalidateCaches) {
313   //    throw new UnsupportedOperationException("implement me");
314   //  }
315   //
316 
317   @HiddenApi
318   @Implementation(maxSdk = M)
addAssetPathNative(String path)319   protected final int addAssetPathNative(String path) {
320     return addAssetPathNative(path, false);
321   }
322 
323   @HiddenApi
324   @Implementation(minSdk = VERSION_CODES.N)
addAssetPathNative(String path, boolean appAsLib)325   protected int addAssetPathNative(String path, boolean appAsLib) {
326     if (Strings.isNullOrEmpty(path)) {
327       return 0;
328     }
329 
330     CppAssetManager am = assetManagerForJavaObject();
331     if (am == null) {
332       return 0;
333     }
334     final Ref<Integer> cookie = new Ref<>(null);
335     boolean res = am.addAssetPath(new String8(path), cookie, appAsLib);
336     return (res) ? cookie.get() : 0;
337   }
338 
339   @HiddenApi
340   @Implementation
getResourceIdentifier(String name, String defType, String defPackage)341   public int getResourceIdentifier(String name, String defType, String defPackage) {
342     if (Strings.isNullOrEmpty(name)) {
343       return 0;
344     }
345     CppAssetManager am = assetManagerForJavaObject();
346     if (am == null) {
347       return 0;
348     }
349 
350     int ident = am.getResources().identifierForName(name, defType, defPackage);
351 
352     return ident;
353   }
354 
355   @HiddenApi
356   @Implementation
openAsset(String fileName, int mode)357   protected long openAsset(String fileName, int mode) throws FileNotFoundException {
358     CppAssetManager am = assetManagerForJavaObject();
359 
360     ALOGV("openAsset in %s", am);
361 
362     String fileName8 = fileName;
363     if (fileName8 == null) {
364       throw new IllegalArgumentException("Empty file name");
365     }
366 
367     if (mode != AccessMode.ACCESS_UNKNOWN.mode()
368         && mode != AccessMode.ACCESS_RANDOM.mode()
369         && mode != AccessMode.ACCESS_STREAMING.mode()
370         && mode != AccessMode.ACCESS_BUFFER.mode()) {
371       throw new IllegalArgumentException("Bad access mode");
372     }
373 
374     Asset a = am.open(fileName8, AccessMode.fromInt(mode));
375 
376     if (a == null) {
377       throw new FileNotFoundException(fileName8);
378     }
379 
380     // printf("Created Asset Stream: %p\n", a);
381 
382     return Registries.NATIVE_ASSET_REGISTRY.register(a);
383   }
384 
385   @HiddenApi
386   @Implementation
openAssetFd(String fileName, long[] outOffsets)387   protected ParcelFileDescriptor openAssetFd(String fileName, long[] outOffsets)
388       throws IOException {
389     CppAssetManager am = assetManagerForJavaObject();
390 
391     ALOGV("openAssetFd in %s", am);
392 
393     String fileName8 = fileName;
394     if (fileName8 == null) {
395       return null;
396     }
397 
398     Asset a = am.open(fileName8, Asset.AccessMode.ACCESS_RANDOM);
399 
400     if (a == null) {
401       throw new FileNotFoundException(fileName8);
402     }
403 
404     return returnParcelFileDescriptor(a, outOffsets);
405   }
406 
407   @HiddenApi
408   @Implementation
openNonAssetNative(int cookie, String fileName, int accessMode)409   protected long openNonAssetNative(int cookie, String fileName, int accessMode)
410       throws FileNotFoundException {
411     CppAssetManager am = assetManagerForJavaObject();
412     if (am == null) {
413       return 0;
414     }
415     ALOGV("openNonAssetNative in %s (Java object %s)\n", am, AssetManager.class);
416     String fileName8 = fileName;
417     if (fileName8 == null) {
418       return -1;
419     }
420     AccessMode mode = AccessMode.fromInt(accessMode);
421     if (mode != Asset.AccessMode.ACCESS_UNKNOWN
422         && mode != Asset.AccessMode.ACCESS_RANDOM
423         && mode != Asset.AccessMode.ACCESS_STREAMING
424         && mode != Asset.AccessMode.ACCESS_BUFFER) {
425       throw new IllegalArgumentException("Bad access mode");
426     }
427     Asset a =
428         isTruthy(cookie)
429             ? am.openNonAsset(cookie, fileName8, mode)
430             : am.openNonAsset(fileName8, mode, null);
431     if (a == null) {
432       throw new FileNotFoundException(fileName8);
433     }
434     long assetId = Registries.NATIVE_ASSET_REGISTRY.register(a);
435     // todo: something better than this [xw]
436     a.onClose = () -> destroyAsset(assetId);
437     // printf("Created Asset Stream: %p\n", a);
438     return assetId;
439   }
440 
441   @HiddenApi
442   @Implementation
openNonAssetFdNative( int cookie, String fileName, long[] outOffsets)443   protected ParcelFileDescriptor openNonAssetFdNative(
444       int cookie, String fileName, long[] outOffsets) throws IOException {
445     CppAssetManager am = assetManagerForJavaObject();
446 
447     ALOGV("openNonAssetFd in %s (Java object %s)", am, this);
448 
449     if (fileName == null) {
450       return null;
451     }
452 
453     Asset a =
454         isTruthy(cookie)
455             ? am.openNonAsset(cookie, fileName, Asset.AccessMode.ACCESS_RANDOM)
456             : am.openNonAsset(fileName, Asset.AccessMode.ACCESS_RANDOM, null);
457 
458     if (a == null) {
459       throw new FileNotFoundException(fileName);
460     }
461 
462     // printf("Created Asset Stream: %p\n", a);
463 
464     return returnParcelFileDescriptor(a, outOffsets);
465   }
466 
467   @HiddenApi
468   @Implementation
destroyAsset(long asset)469   protected void destroyAsset(long asset) {
470     Registries.NATIVE_ASSET_REGISTRY.unregister(asset);
471   }
472 
473   @HiddenApi
474   @Implementation
readAssetChar(long asset)475   protected int readAssetChar(long asset) {
476     Asset a = getAsset(asset);
477     byte[] b = new byte[1];
478     int res = a.read(b, 1);
479     return res == 1 ? b[0] & 0xff : -1;
480   }
481 
482   @HiddenApi
483   @Implementation
readAsset(long asset, byte[] bArray, int off, int len)484   protected int readAsset(long asset, byte[] bArray, int off, int len) throws IOException {
485     Asset a = getAsset(asset);
486 
487     if (a == null || bArray == null) {
488       throw new NullPointerException("asset");
489     }
490 
491     if (len == 0) {
492       return 0;
493     }
494 
495     int bLen = bArray.length;
496     if (off < 0 || off >= bLen || len < 0 || len > bLen || (off + len) > bLen) {
497       throw new IndexOutOfBoundsException();
498     }
499 
500     byte[] b = bArray;
501     int res = a.read(b, off, len);
502 
503     if (res > 0) return res;
504 
505     if (res < 0) {
506       throw new IOException();
507     }
508     return -1;
509   }
510 
511   @HiddenApi
512   @Implementation
seekAsset(long asset, long offset, int whence)513   protected long seekAsset(long asset, long offset, int whence) {
514     Asset a = getAsset(asset);
515     return a.seek(offset, whence < 0 ? SEEK_SET : SEEK_CUR);
516   }
517 
518   @HiddenApi
519   @Implementation
getAssetLength(long asset)520   protected long getAssetLength(long asset) {
521     Asset a = getAsset(asset);
522     return a.getLength();
523   }
524 
525   @HiddenApi
526   @Implementation
getAssetRemainingLength(long assetHandle)527   protected long getAssetRemainingLength(long assetHandle) {
528     Asset a = getAsset(assetHandle);
529 
530     if (a == null) {
531       throw new NullPointerException("asset");
532     }
533 
534     return a.getRemainingLength();
535   }
536 
getAsset(long asset)537   private Asset getAsset(long asset) {
538     return Registries.NATIVE_ASSET_REGISTRY.getNativeObject(asset);
539   }
540 
541   @HiddenApi
542   @Implementation
loadResourceValue(int ident, short density, TypedValue outValue, boolean resolve)543   protected int loadResourceValue(int ident, short density, TypedValue outValue, boolean resolve) {
544     if (outValue == null) {
545       throw new NullPointerException("outValue");
546       // return 0;
547     }
548     CppAssetManager am = assetManagerForJavaObject();
549     if (am == null) {
550       return 0;
551     }
552     final ResTable res = am.getResources();
553 
554     final Ref<Res_value> value = new Ref<>(null);
555     final Ref<ResTable_config> config = new Ref<>(null);
556     final Ref<Integer> typeSpecFlags = new Ref<>(null);
557     int block = res.getResource(ident, value, false, density, typeSpecFlags, config);
558     if (kThrowOnBadId) {
559       if (block == BAD_INDEX) {
560         throw new IllegalStateException("Bad resource!");
561         // return 0;
562       }
563     }
564     final Ref<Integer> ref = new Ref<>(ident);
565     if (resolve) {
566       block = res.resolveReference(value, block, ref, typeSpecFlags, config);
567       if (kThrowOnBadId) {
568         if (block == BAD_INDEX) {
569           throw new IllegalStateException("Bad resource!");
570           // return 0;
571         }
572       }
573     }
574     if (block >= 0) {
575       // return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config);
576       return copyValue(
577           outValue, res, value.get(), ref.get(), block, typeSpecFlags.get(), config.get());
578     }
579     return block;
580   }
581 
copyValue( TypedValue outValue, ResTable table, Res_value value, int ref, int block, int typeSpecFlags)582   private static int copyValue(
583       TypedValue outValue, ResTable table, Res_value value, int ref, int block, int typeSpecFlags) {
584     return copyValue(outValue, table, value, ref, block, typeSpecFlags, null);
585   }
586 
copyValue( TypedValue outValue, ResTable table, Res_value value, int ref, int block, int typeSpecFlags, ResTable_config config)587   private static int copyValue(
588       TypedValue outValue,
589       ResTable table,
590       Res_value value,
591       int ref,
592       int block,
593       int typeSpecFlags,
594       ResTable_config config) {
595     outValue.type = value.dataType;
596     outValue.assetCookie = table.getTableCookie(block);
597     outValue.data = value.data;
598     outValue.string = null;
599     outValue.resourceId = ref;
600     outValue.changingConfigurations = typeSpecFlags;
601 
602     if (config != null) {
603       outValue.density = config.density;
604     }
605     return block;
606   }
607 
getResourceBagValues(int ident, ResTable res)608   public static Map<String, Integer> getResourceBagValues(int ident, ResTable res) {
609     // Now lock down the resource object and start pulling stuff from it.
610     res.lock();
611 
612     HashMap<String, Integer> map;
613     try {
614       final Ref<bag_entry[]> entryRef = new Ref<>(null);
615       final Ref<Integer> typeSpecFlags = new Ref<>(0);
616       int entryCount = res.getBagLocked(ident, entryRef, typeSpecFlags);
617 
618       map = new HashMap<>();
619       bag_entry[] bag_entries = entryRef.get();
620       for (int i = 0; i < entryCount; i++) {
621         bag_entry entry = bag_entries[i];
622         ResourceName resourceName = new ResourceName();
623         if (res.getResourceName(entry.map.name.ident, true, resourceName)) {
624           map.put(resourceName.name, entry.map.value.data);
625         }
626       }
627     } finally {
628       res.unlock();
629     }
630 
631     return map;
632   }
633 
634   /** Returns true if the resource was found, filling in mRetStringBlock and mRetData. */
635   @Implementation
636   @HiddenApi
loadResourceBagValue( int ident, int bagEntryId, TypedValue outValue, boolean resolve)637   protected int loadResourceBagValue(
638       int ident, int bagEntryId, TypedValue outValue, boolean resolve) {
639     CppAssetManager am = assetManagerForJavaObject();
640     if (am == null) {
641       return 0;
642     }
643     final ResTable res = am.getResources();
644     return loadResourceBagValueInternal(ident, bagEntryId, outValue, resolve, res);
645   }
646 
getResourceBagValue(int ident, int bagEntryId, ResTable resTable)647   public static String getResourceBagValue(int ident, int bagEntryId, ResTable resTable) {
648     TypedValue outValue = new TypedValue();
649     int blockId =
650         ShadowArscAssetManager.loadResourceBagValueInternal(
651             ident, bagEntryId, outValue, true, resTable);
652     if (outValue.type == TypedValue.TYPE_STRING) {
653       return resTable.getTableStringBlock(blockId).stringAt(outValue.data);
654     } else {
655       return outValue.coerceToString().toString();
656     }
657   }
658 
loadResourceBagValueInternal( int ident, int bagEntryId, TypedValue outValue, boolean resolve, ResTable res)659   private static int loadResourceBagValueInternal(
660       int ident, int bagEntryId, TypedValue outValue, boolean resolve, ResTable res) {
661     // Now lock down the resource object and start pulling stuff from it.
662     res.lock();
663 
664     int block = -1;
665     final Ref<Res_value> valueRef = new Ref<>(null);
666     final Ref<bag_entry[]> entryRef = new Ref<>(null);
667     final Ref<Integer> typeSpecFlags = new Ref<>(0);
668     int entryCount = res.getBagLocked(ident, entryRef, typeSpecFlags);
669 
670     bag_entry[] bag_entries = entryRef.get();
671     for (int i = 0; i < entryCount; i++) {
672       bag_entry entry = bag_entries[i];
673       if (bagEntryId == entry.map.name.ident) {
674         block = entry.stringBlock;
675         valueRef.set(entry.map.value);
676       }
677     }
678 
679     res.unlock();
680 
681     if (block < 0) {
682       return block;
683     }
684 
685     final Ref<Integer> ref = new Ref<>(ident);
686     if (resolve) {
687       block = res.resolveReference(valueRef, block, ref, typeSpecFlags);
688       if (kThrowOnBadId) {
689         if (block == BAD_INDEX) {
690           throw new IllegalStateException("Bad resource!");
691         }
692       }
693     }
694     if (block >= 0) {
695       return copyValue(outValue, res, valueRef.get(), ref.get(), block, typeSpecFlags.get());
696     }
697 
698     return block;
699   }
700 
701   // /*package*/ static final int STYLE_NUM_ENTRIES = 6;
702   // /*package*/ static final int STYLE_TYPE = 0;
703   // /*package*/ static final int STYLE_DATA = 1;
704   // /*package*/ static final int STYLE_ASSET_COOKIE = 2;
705   // /*package*/ static final int STYLE_RESOURCE_ID = 3;
706   //
707   // /* Offset within typed data array for native changingConfigurations. */
708   // static final int STYLE_CHANGING_CONFIGURATIONS = 4;
709 
710   // /*package*/ static final int STYLE_DENSITY = 5;
711 
712   /* lowercase hexadecimal notation.  */
713   // # define PRIx8		"x"
714   //      # define PRIx16		"x"
715   //      # define PRIx32		"x"
716 
717   @HiddenApi
718   @Implementation(minSdk = O, maxSdk = O_MR1)
applyStyle( long themeToken, int defStyleAttr, int defStyleRes, long xmlParserToken, int[] inAttrs, int length, long outValuesAddress, long outIndicesAddress)719   protected static void applyStyle(
720       long themeToken,
721       int defStyleAttr,
722       int defStyleRes,
723       long xmlParserToken,
724       int[] inAttrs,
725       int length,
726       long outValuesAddress,
727       long outIndicesAddress) {
728     ShadowVMRuntime shadowVMRuntime = Shadow.extract(VMRuntime.getRuntime());
729     int[] outValues = (int[]) shadowVMRuntime.getObjectForAddress(outValuesAddress);
730     int[] outIndices = (int[]) shadowVMRuntime.getObjectForAddress(outIndicesAddress);
731     applyStyle(
732         themeToken, defStyleAttr, defStyleRes, xmlParserToken, inAttrs, outValues, outIndices);
733   }
734 
735   @HiddenApi
736   @Implementation(maxSdk = N_MR1)
applyStyle( long themeToken, int defStyleAttr, int defStyleRes, long xmlParserToken, int[] attrs, int[] outValues, int[] outIndices)737   protected static boolean applyStyle(
738       long themeToken,
739       int defStyleAttr,
740       int defStyleRes,
741       long xmlParserToken,
742       int[] attrs,
743       int[] outValues,
744       int[] outIndices) {
745     ResTableTheme theme = Registries.NATIVE_THEME_REGISTRY.getNativeObject(themeToken);
746     ResXMLParser xmlParser =
747         xmlParserToken == 0
748             ? null
749             : Registries.NATIVE_RES_XML_PARSERS.getNativeObject(xmlParserToken);
750     AttributeResolution.ApplyStyle(
751         theme, xmlParser, defStyleAttr, defStyleRes, attrs, attrs.length, outValues, outIndices);
752     return true;
753   }
754 
755   @Implementation
756   @HiddenApi
resolveAttrs( long themeToken, int defStyleAttr, int defStyleRes, int[] inValues, int[] attrs, int[] outValues, int[] outIndices)757   protected static boolean resolveAttrs(
758       long themeToken,
759       int defStyleAttr,
760       int defStyleRes,
761       int[] inValues,
762       int[] attrs,
763       int[] outValues,
764       int[] outIndices) {
765     if (themeToken == 0) {
766       throw new NullPointerException("theme token");
767     }
768     if (attrs == null) {
769       throw new NullPointerException("attrs");
770     }
771     if (outValues == null) {
772       throw new NullPointerException("out values");
773     }
774 
775     final int NI = attrs.length;
776     final int NV = outValues.length;
777     if (NV < (NI * STYLE_NUM_ENTRIES)) {
778       throw new IndexOutOfBoundsException("out values too small");
779     }
780 
781     int[] src = attrs;
782     //    if (src == null) {
783     //      return JNI_FALSE;
784     //    }
785 
786     int[] srcValues = inValues;
787     final int NSV = srcValues == null ? 0 : inValues.length;
788 
789     int[] baseDest = outValues;
790     int destOffset = 0;
791     if (baseDest == null) {
792       return false;
793     }
794 
795     int[] indices = null;
796     if (outIndices != null) {
797       if (outIndices.length > NI) {
798         indices = outIndices;
799       }
800     }
801 
802     ResTableTheme theme = Registries.NATIVE_THEME_REGISTRY.getNativeObject(themeToken);
803 
804     boolean result =
805         AttributeResolution.ResolveAttrs(
806             theme, defStyleAttr, defStyleRes, srcValues, NSV, src, NI, baseDest, indices);
807 
808     if (indices != null) {
809       //      env.ReleasePrimitiveArrayCritical(outIndices, indices, 0);
810     }
811     //    env.ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
812     //    env.ReleasePrimitiveArrayCritical(inValues, srcValues, 0);
813     //    env.ReleasePrimitiveArrayCritical(attrs, src, 0);
814 
815     return result;
816   }
817 
818   @HiddenApi
819   @Implementation
retrieveAttributes( long xmlParserToken, int[] attrs, int[] outValues, int[] outIndices)820   protected boolean retrieveAttributes(
821       long xmlParserToken, int[] attrs, int[] outValues, int[] outIndices) {
822     if (xmlParserToken == 0) {
823       throw new NullPointerException("xmlParserToken");
824       //      return JNI_FALSE;
825     }
826     if (attrs == null) {
827       throw new NullPointerException("attrs");
828       //      return JNI_FALSE;
829     }
830     if (outValues == null) {
831       throw new NullPointerException("out values");
832       //      return JNI_FALSE;
833     }
834 
835     CppAssetManager am = assetManagerForJavaObject();
836     //    if (am == null) {
837     //      return JNI_FALSE;
838     //    }
839     ResTable res = am.getResources();
840     //    ResXMLParser xmlParser = (ResXMLParser*)xmlParserToken;
841     ResXMLParser xmlParser = Registries.NATIVE_RES_XML_PARSERS.getNativeObject(xmlParserToken);
842 
843     //    const int NI = env.GetArrayLength(attrs);
844     //    const int NV = env.GetArrayLength(outValues);
845     final int NI = attrs.length;
846     final int NV = outValues.length;
847     if (NV < (NI * STYLE_NUM_ENTRIES)) {
848       throw new IndexOutOfBoundsException("out values too small");
849       //      return JNI_FALSE;
850     }
851 
852     //    int[] src = (int[])env.GetPrimitiveArrayCritical(attrs, 0);
853     //    if (src == null) {
854     //      return JNI_FALSE;
855     //    }
856     int[] src = attrs;
857 
858     //    int[] baseDest = (int[])env.GetPrimitiveArrayCritical(outValues, 0);
859     int[] baseDest = outValues;
860     if (baseDest == null) {
861       //      env.ReleasePrimitiveArrayCritical(attrs, src, 0);
862       //      return JNI_FALSE;
863       return false;
864     }
865 
866     int[] indices = null;
867     if (outIndices != null) {
868       if (outIndices.length > NI) {
869         //        indices = (int[])env.GetPrimitiveArrayCritical(outIndices, 0);
870         indices = outIndices;
871       }
872     }
873     boolean result =
874         AttributeResolution.RetrieveAttributes(res, xmlParser, src, NI, baseDest, indices);
875 
876     if (indices != null) {
877       //      indices[0] = indicesIdx;
878       //      env.ReleasePrimitiveArrayCritical(outIndices, indices, 0);
879     }
880 
881     //    env.ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
882     //    env.ReleasePrimitiveArrayCritical(attrs, src, 0);
883 
884     return result;
885   }
886 
887   @HiddenApi
888   @Implementation
getArraySize(int id)889   protected int getArraySize(int id) {
890     CppAssetManager am = assetManagerForJavaObject();
891     if (am == null) {
892       return 0;
893     }
894     final ResTable res = am.getResources();
895 
896     res.lock();
897     final Ref<bag_entry[]> defStyleEnt = new Ref<>(null);
898     int bagOff = res.getBagLocked(id, defStyleEnt, null);
899     res.unlock();
900 
901     return bagOff;
902   }
903 
904   @Implementation
905   @HiddenApi
retrieveArray(int id, int[] outValues)906   protected int retrieveArray(int id, int[] outValues) {
907     if (outValues == null) {
908       throw new NullPointerException("out values");
909     }
910 
911     CppAssetManager am = assetManagerForJavaObject();
912     if (am == null) {
913       return 0 /*JNI_FALSE */;
914     }
915     ResTable res = am.getResources();
916     final Ref<ResTable_config> config = new Ref<>(new ResTable_config());
917     Res_value value;
918     int block;
919 
920     int NV = outValues.length;
921 
922     //    int[] baseDest = (int[])env->GetPrimitiveArrayCritical(outValues, 0);
923     int[] baseDest = outValues;
924     int[] dest = baseDest;
925     //    if (dest == null) {
926     //      throw new NullPointerException(env, "java/lang/OutOfMemoryError", "");
927     //      return JNI_FALSE;
928     //    }
929 
930     // Now lock down the resource object and start pulling stuff from it.
931     res.lock();
932 
933     final Ref<bag_entry[]> arrayEnt = new Ref<>(null);
934     final Ref<Integer> arrayTypeSetFlags = new Ref<>(0);
935     int bagOff = res.getBagLocked(id, arrayEnt, arrayTypeSetFlags);
936     //    final ResTable::bag_entry* endArrayEnt = arrayEnt +
937     //        (bagOff >= 0 ? bagOff : 0);
938 
939     int destOffset = 0;
940     final Ref<Integer> typeSetFlags = new Ref<>(0);
941     while (destOffset < NV
942         && destOffset < bagOff * STYLE_NUM_ENTRIES /*&& arrayEnt < endArrayEnt*/) {
943       bag_entry curArrayEnt = arrayEnt.get()[destOffset / STYLE_NUM_ENTRIES];
944 
945       block = curArrayEnt.stringBlock;
946       typeSetFlags.set(arrayTypeSetFlags.get());
947       config.get().density = 0;
948       value = curArrayEnt.map.value;
949 
950       final Ref<Integer> resid = new Ref<>(0);
951       if (value.dataType != DataType.NULL.code()) {
952         // Take care of resolving the found resource to its final value.
953         // printf("Resolving attribute reference\n");
954         final Ref<Res_value> resValueRef = new Ref<>(value);
955         int newBlock = res.resolveReference(resValueRef, block, resid, typeSetFlags, config);
956         value = resValueRef.get();
957         if (kThrowOnBadId) {
958           if (newBlock == BAD_INDEX) {
959             throw new IllegalStateException("Bad resource!");
960           }
961         }
962         if (newBlock >= 0) block = newBlock;
963       }
964 
965       // Deal with the special @null value -- it turns back to TYPE_NULL.
966       if (value.dataType == DataType.REFERENCE.code() && value.data == 0) {
967         value = Res_value.NULL_VALUE;
968       }
969 
970       // printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType,
971       // value.data);
972 
973       // Write the final value back to Java.
974       dest[destOffset + STYLE_TYPE] = value.dataType;
975       dest[destOffset + STYLE_DATA] = value.data;
976       dest[destOffset + STYLE_ASSET_COOKIE] = res.getTableCookie(block);
977       dest[destOffset + STYLE_RESOURCE_ID] = resid.get();
978       dest[destOffset + STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags.get();
979       dest[destOffset + STYLE_DENSITY] = config.get().density;
980       //      dest += STYLE_NUM_ENTRIES;
981       destOffset += STYLE_NUM_ENTRIES;
982       //      arrayEnt++;
983     }
984 
985     destOffset /= STYLE_NUM_ENTRIES;
986 
987     res.unlock();
988 
989     //    env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
990 
991     return destOffset;
992   }
993 
994   @HiddenApi
995   @Implementation
getNativeStringBlock(int block)996   protected long getNativeStringBlock(int block) {
997     CppAssetManager am = assetManagerForJavaObject();
998     if (am == null) {
999       return 0;
1000     }
1001 
1002     return am.getResources().getTableStringBlock(block).getNativePtr();
1003   }
1004 
1005   @Implementation
getAssignedPackageIdentifiers()1006   protected SparseArray<String> getAssignedPackageIdentifiers() {
1007     CppAssetManager am = assetManagerForJavaObject();
1008     final ResTable res = am.getResources();
1009 
1010     SparseArray<String> sparseArray = new SparseArray<>();
1011     final int N = res.getBasePackageCount();
1012     for (int i = 0; i < N; i++) {
1013       final String name = res.getBasePackageName(i);
1014       sparseArray.put(res.getBasePackageId(i), name);
1015     }
1016     return sparseArray;
1017   }
1018 
1019   @HiddenApi
1020   @Implementation
newTheme()1021   protected long newTheme() {
1022     CppAssetManager am = assetManagerForJavaObject();
1023     if (am == null) {
1024       return 0;
1025     }
1026     ResTableTheme theme = new ResTableTheme(am.getResources());
1027     return Registries.NATIVE_THEME_REGISTRY.register(theme);
1028   }
1029 
1030   @HiddenApi
1031   @Implementation
deleteTheme(long theme)1032   protected void deleteTheme(long theme) {
1033     Registries.NATIVE_THEME_REGISTRY.unregister(theme);
1034   }
1035 
1036   @HiddenApi
1037   @Implementation(maxSdk = O_MR1)
applyThemeStyle(long themePtr, int styleRes, boolean force)1038   public static void applyThemeStyle(long themePtr, int styleRes, boolean force) {
1039     Registries.NATIVE_THEME_REGISTRY.getNativeObject(themePtr).applyStyle(styleRes, force);
1040   }
1041 
1042   @HiddenApi
1043   @Implementation(maxSdk = O_MR1)
copyTheme(long destPtr, long sourcePtr)1044   public static void copyTheme(long destPtr, long sourcePtr) {
1045     ResTableTheme dest = Registries.NATIVE_THEME_REGISTRY.getNativeObject(destPtr);
1046     ResTableTheme src = Registries.NATIVE_THEME_REGISTRY.getNativeObject(sourcePtr);
1047     dest.setTo(src);
1048   }
1049 
1050   @HiddenApi
1051   @Implementation
loadThemeAttributeValue( long themeHandle, int ident, TypedValue outValue, boolean resolve)1052   protected static int loadThemeAttributeValue(
1053       long themeHandle, int ident, TypedValue outValue, boolean resolve) {
1054     ResTableTheme theme =
1055         Preconditions.checkNotNull(Registries.NATIVE_THEME_REGISTRY.getNativeObject(themeHandle));
1056     ResTable res = theme.getResTable();
1057 
1058     final Ref<Res_value> value = new Ref<>(null);
1059     // XXX value could be different in different configs!
1060     final Ref<Integer> typeSpecFlags = new Ref<>(0);
1061     int block = theme.GetAttribute(ident, value, typeSpecFlags);
1062     final Ref<Integer> ref = new Ref<>(0);
1063     if (resolve) {
1064       block = res.resolveReference(value, block, ref, typeSpecFlags);
1065       if (kThrowOnBadId) {
1066         if (block == BAD_INDEX) {
1067           throw new IllegalStateException("Bad resource!");
1068         }
1069       }
1070     }
1071     return block >= 0
1072         ? copyValue(outValue, res, value.get(), ref.get(), block, typeSpecFlags.get(), null)
1073         : block;
1074   }
1075 
1076   //  /*package*/@HiddenApi @Implementation public static final @NativeConfig
1077   //  int getThemeChangingConfigurations(long theme);
1078 
1079   @HiddenApi
1080   @Implementation
openXmlAssetNative(int cookie, String fileName)1081   protected long openXmlAssetNative(int cookie, String fileName) throws FileNotFoundException {
1082     CppAssetManager am = assetManagerForJavaObject();
1083     if (am == null) {
1084       return 0;
1085     }
1086 
1087     ALOGV("openXmlAsset in %s (Java object %s)\n", am, ShadowArscAssetManager.class);
1088 
1089     String fileName8 = fileName;
1090     if (fileName8 == null) {
1091       return 0;
1092     }
1093 
1094     int assetCookie = cookie;
1095     Asset a;
1096     if (isTruthy(assetCookie)) {
1097       a = am.openNonAsset(assetCookie, fileName8, AccessMode.ACCESS_BUFFER);
1098     } else {
1099       final Ref<Integer> assetCookieRef = new Ref<>(assetCookie);
1100       a = am.openNonAsset(fileName8, AccessMode.ACCESS_BUFFER, assetCookieRef);
1101       assetCookie = assetCookieRef.get();
1102     }
1103 
1104     if (a == null) {
1105       throw new FileNotFoundException(fileName8);
1106     }
1107 
1108     final DynamicRefTable dynamicRefTable =
1109         am.getResources().getDynamicRefTableForCookie(assetCookie);
1110     ResXMLTree block = new ResXMLTree(dynamicRefTable);
1111     int err = block.setTo(a.getBuffer(true), (int) a.getLength(), true);
1112     a.close();
1113     //    delete a;
1114 
1115     if (err != NO_ERROR) {
1116       throw new FileNotFoundException("Corrupt XML binary file");
1117     }
1118 
1119     return Registries.NATIVE_RES_XML_TREES.register(block);
1120   }
1121 
1122   @HiddenApi
1123   @Implementation
getArrayStringResource(int arrayResId)1124   protected String[] getArrayStringResource(int arrayResId) {
1125     CppAssetManager am = assetManagerForJavaObject();
1126     if (am == null) {
1127       return null;
1128     }
1129     final ResTable res = am.getResources();
1130 
1131     final Ref<bag_entry[]> startOfBag = new Ref<>(null);
1132     final int N = res.lockBag(arrayResId, startOfBag);
1133     if (N < 0) {
1134       return null;
1135     }
1136 
1137     String[] array = new String[N];
1138 
1139     final Ref<Res_value> valueRef = new Ref<>(null);
1140     final bag_entry[] bag = startOfBag.get();
1141     int strLen = 0;
1142     for (int i = 0; ((int) i) < N; i++) {
1143       valueRef.set(bag[i].map.value);
1144       String str = null;
1145 
1146       // Take care of resolving the found resource to its final value.
1147       int block = res.resolveReference(valueRef, bag[i].stringBlock, null);
1148       if (kThrowOnBadId) {
1149         if (block == BAD_INDEX) {
1150           throw new IllegalStateException("Bad resource!");
1151         }
1152       }
1153       if (valueRef.get().dataType == DataType.STRING.code()) {
1154         final ResStringPool pool = res.getTableStringBlock(block);
1155         str = pool.stringAt(valueRef.get().data);
1156 
1157         // assume we can skip utf8 vs utf 16 handling
1158 
1159         //            final char* str8 = pool.string8At(value.data, &strLen);
1160         //        if (str8 != NULL) {
1161         //          str = env.NewStringUTF(str8);
1162         //        } else {
1163         //                final char16_t* str16 = pool.stringAt(value.data, &strLen);
1164         //          str = env.NewString(reinterpret_cast<final jchar*>(str16),
1165         //              strLen);
1166         //        }
1167         //
1168         //        // If one of our NewString{UTF} calls failed due to memory, an
1169         //        // exception will be pending.
1170         //        if (env.ExceptionCheck()) {
1171         //          res.unlockBag(startOfBag);
1172         //          return NULL;
1173         //        }
1174         if (str == null) {
1175           res.unlockBag(startOfBag);
1176           return null;
1177         }
1178 
1179         array[i] = str;
1180 
1181         // str is not NULL at that point, otherwise ExceptionCheck would have been true.
1182         // If we have a large amount of strings in our array, we might
1183         // overflow the local reference table of the VM.
1184         // env.DeleteLocalRef(str);
1185       }
1186     }
1187     res.unlockBag(startOfBag);
1188     return array;
1189   }
1190 
1191   @HiddenApi
1192   @Implementation
getArrayStringInfo(int arrayResId)1193   protected int[] getArrayStringInfo(int arrayResId) {
1194     CppAssetManager am = assetManagerForJavaObject();
1195     ResTable res = am.getResources();
1196 
1197     final Ref<bag_entry[]> startOfBag = new Ref<>(null);
1198     final int N = res.lockBag(arrayResId, startOfBag);
1199     if (N < 0) {
1200       return null;
1201     }
1202 
1203     int[] array = new int[N * 2];
1204 
1205     final Ref<Res_value> value = new Ref<>(null);
1206     bag_entry[] bag = startOfBag.get();
1207     for (int i = 0, j = 0; i < N; i++) {
1208       int stringIndex = -1;
1209       int stringBlock = 0;
1210       value.set(bag[i].map.value);
1211 
1212       // Take care of resolving the found resource to its final value.
1213       stringBlock = res.resolveReference(value, bag[i].stringBlock, null);
1214       if (value.get().dataType == DataType.STRING.code()) {
1215         stringIndex = value.get().data;
1216       }
1217 
1218       if (kThrowOnBadId) {
1219         if (stringBlock == BAD_INDEX) {
1220           throw new IllegalStateException("Bad resource!");
1221         }
1222       }
1223 
1224       // todo: It might be faster to allocate a C array to contain
1225       //      the blocknums and indices, put them in there and then
1226       //      do just one SetIntArrayRegion()
1227       // env->SetIntArrayRegion(array, j, 1, &stringBlock);
1228       array[j] = stringBlock;
1229       // env->SetIntArrayRegion(array, j + 1, 1, &stringIndex);
1230       array[j + 1] = stringIndex;
1231       j += 2;
1232     }
1233     res.unlockBag(startOfBag);
1234     return array;
1235   }
1236 
1237   @HiddenApi
1238   @Implementation
getArrayIntResource(int arrayResId)1239   public int[] getArrayIntResource(int arrayResId) {
1240     CppAssetManager am = assetManagerForJavaObject();
1241     if (am == null) {
1242       return null;
1243     }
1244     final ResTable res = am.getResources();
1245 
1246     //    final ResTable::bag_entry* startOfBag;
1247     final Ref<bag_entry[]> startOfBag = new Ref<>(null);
1248     final int N = res.lockBag(arrayResId, startOfBag);
1249     if (N < 0) {
1250       return null;
1251     }
1252 
1253     int[] array = new int[N];
1254     if (array == null) {
1255       res.unlockBag(startOfBag);
1256       return null;
1257     }
1258 
1259     final Ref<Res_value> valueRef = new Ref<>(null);
1260     bag_entry[] bag = startOfBag.get();
1261     for (int i = 0; i < N; i++) {
1262       valueRef.set(bag[i].map.value);
1263 
1264       // Take care of resolving the found resource to its final value.
1265       int block = res.resolveReference(valueRef, bag[i].stringBlock, null, null, null);
1266       if (kThrowOnBadId) {
1267         if (block == BAD_INDEX) {
1268           res.unlockBag(
1269               startOfBag); // seems like this is missing from android_util_AssetManager.cpp?
1270           throw new IllegalStateException("Bad resource!");
1271           //          return array;
1272         }
1273       }
1274       Res_value value = valueRef.get();
1275       if (value.dataType >= DataType.TYPE_FIRST_INT && value.dataType <= DataType.TYPE_LAST_INT) {
1276         int intVal = value.data;
1277         //        env->SetIntArrayRegion(array, i, 1, &intVal);
1278         array[i] = intVal;
1279       }
1280     }
1281     res.unlockBag(startOfBag);
1282     return array;
1283   }
1284 
1285   private static CppAssetManager systemCppAssetManager;
1286 
1287   @HiddenApi
1288   @Implementation
init(boolean isSystem)1289   protected void init(boolean isSystem) {
1290     //  if (isSystem) {
1291     //    verifySystemIdmaps();
1292     //  }
1293 
1294     Path androidFrameworkJarPath = RuntimeEnvironment.getAndroidFrameworkJarPath();
1295     Preconditions.checkNotNull(androidFrameworkJarPath);
1296 
1297     if (isSystem) {
1298       synchronized (ShadowArscAssetManager.class) {
1299         if (systemCppAssetManager == null) {
1300           systemCppAssetManager = new CppAssetManager();
1301           systemCppAssetManager.addDefaultAssets(androidFrameworkJarPath);
1302         }
1303       }
1304       this.cppAssetManager = systemCppAssetManager;
1305     } else {
1306       this.cppAssetManager = new CppAssetManager();
1307       cppAssetManager.addDefaultAssets(androidFrameworkJarPath);
1308     }
1309 
1310     ALOGV(
1311         "Created AssetManager %s for Java object %s\n",
1312         cppAssetManager, ShadowArscAssetManager.class);
1313   }
1314 
1315   @VisibleForTesting
getConfiguration()1316   ResTable_config getConfiguration() {
1317     final Ref<ResTable_config> config = new Ref<>(new ResTable_config());
1318     assetManagerForJavaObject().getConfiguration(config);
1319     return config.get();
1320   }
1321 
1322   //  private native final void destroy();
1323 
1324   //  @HiddenApi
1325   //  @Implementation
1326   //  public int addOverlayPathNative(String idmapPath) {
1327   //    if (Strings.isNullOrEmpty(idmapPath)) {
1328   //      return 0;
1329   //    }
1330   //
1331   //    CppAssetManager am = assetManagerForJavaObject();
1332   //    if (am == null) {
1333   //      return 0;
1334   //    }
1335   //    final Ref<Integer> cookie = new Ref<>(null);
1336   //    boolean res = am.addOverlayPath(new String8(idmapPath), cookie);
1337   //    return (res) ? cookie.get() : 0;
1338   //  }
1339 
1340   @HiddenApi
1341   @Implementation
getStringBlockCount()1342   protected int getStringBlockCount() {
1343     CppAssetManager am = assetManagerForJavaObject();
1344     if (am == null) {
1345       return 0;
1346     }
1347     return am.getResources().getTableCount();
1348   }
1349 
assetManagerForJavaObject()1350   private synchronized CppAssetManager assetManagerForJavaObject() {
1351     if (cppAssetManager == null) {
1352       throw new NullPointerException();
1353     }
1354     return cppAssetManager;
1355   }
1356 
returnParcelFileDescriptor(Asset a, long[] outOffsets)1357   static ParcelFileDescriptor returnParcelFileDescriptor(Asset a, long[] outOffsets)
1358       throws FileNotFoundException {
1359     final Ref<Long> startOffset = new Ref<Long>(-1L);
1360     final Ref<Long> length = new Ref<Long>(-1L);
1361     ;
1362     FileDescriptor fd = a.openFileDescriptor(startOffset, length);
1363 
1364     if (fd == null) {
1365       throw new FileNotFoundException(
1366           "This file can not be opened as a file descriptor; it is probably compressed");
1367     }
1368 
1369     long[] offsets = outOffsets;
1370     if (offsets == null) {
1371       // fd.close();
1372       return null;
1373     }
1374 
1375     offsets[0] = startOffset.get();
1376     offsets[1] = length.get();
1377 
1378     // FileDescriptor fileDesc = jniCreateFileDescriptor(fd);
1379     // if (fileDesc == null) {
1380     // close(fd);
1381     // return null;
1382     // }
1383 
1384     // TODO: consider doing this
1385     // return new ParcelFileDescriptor(fileDesc);
1386     return ParcelFileDescriptor.open(a.getFile(), ParcelFileDescriptor.MODE_READ_ONLY);
1387   }
1388 
1389   @Override
getAllAssetDirs()1390   Collection<Path> getAllAssetDirs() {
1391     ArrayList<Path> paths = new ArrayList<>();
1392     for (AssetPath assetPath : cppAssetManager.getAssetPaths()) {
1393       if (Files.isRegularFile(assetPath.file)) {
1394         paths.add(Fs.forJar(assetPath.file).getPath("assets"));
1395       } else {
1396         paths.add(assetPath.file);
1397       }
1398     }
1399     return paths;
1400   }
1401 
1402   @VisibleForTesting
1403   @Override
getNativePtr()1404   long getNativePtr() {
1405     return reflector(_AssetManager_.class, realObject).getNativePtr();
1406   }
1407 
1408   @Override
getAssetPaths()1409   List<AssetPath> getAssetPaths() {
1410     return assetManagerForJavaObject().getAssetPaths();
1411   }
1412 }
1413