content: /*
   * Copyright (C) 2008 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.android.layoutlib.bridge.android;
  
  import com.android.SdkConstants;
  import com.android.ide.common.rendering.api.AssetRepository;
  import com.android.ide.common.rendering.api.ILayoutPullParser;
  import com.android.ide.common.rendering.api.LayoutLog;
  import com.android.ide.common.rendering.api.LayoutlibCallback;
  import com.android.ide.common.rendering.api.RenderResources;
  import com.android.ide.common.rendering.api.ResourceNamespace;
  import com.android.ide.common.rendering.api.ResourceNamespace.Resolver;
  import com.android.ide.common.rendering.api.ResourceReference;
  import com.android.ide.common.rendering.api.ResourceValue;
  import com.android.ide.common.rendering.api.ResourceValueImpl;
  import com.android.ide.common.rendering.api.StyleResourceValue;
  import com.android.layoutlib.bridge.Bridge;
  import com.android.layoutlib.bridge.BridgeConstants;
  import com.android.layoutlib.bridge.android.view.WindowManagerImpl;
  import com.android.layoutlib.bridge.impl.ParserFactory;
  import com.android.layoutlib.bridge.impl.Stack;
  import com.android.resources.ResourceType;
  import com.android.util.Pair;
  
  import org.xmlpull.v1.XmlPullParser;
  import org.xmlpull.v1.XmlPullParserException;
  
  import android.annotation.NonNull;
  import android.annotation.Nullable;
  import android.app.SystemServiceRegistry_Accessor;
  import android.content.BroadcastReceiver;
  import android.content.ComponentName;
  import android.content.ContentResolver;
  import android.content.Context;
  import android.content.ContextWrapper;
  import android.content.Intent;
  import android.content.IntentFilter;
  import android.content.IntentSender;
  import android.content.ServiceConnection;
  import android.content.SharedPreferences;
  import android.content.pm.ApplicationInfo;
  import android.content.pm.PackageManager;
  import android.content.res.AssetManager;
  import android.content.res.BridgeAssetManager;
  import android.content.res.BridgeTypedArray;
  import android.content.res.Configuration;
  import android.content.res.Resources;
  import android.content.res.Resources.Theme;
  import android.content.res.Resources_Delegate;
  import android.database.DatabaseErrorHandler;
  import android.database.sqlite.SQLiteDatabase;
  import android.database.sqlite.SQLiteDatabase.CursorFactory;
  import android.graphics.Bitmap;
  import android.graphics.Color;
  import android.graphics.drawable.Drawable;
  import android.hardware.display.DisplayManager;
  import android.net.Uri;
  import android.os.Bundle;
  import android.os.Handler;
  import android.os.IBinder;
  import android.os.IInterface;
  import android.os.Looper;
  import android.os.Parcel;
  import android.os.PowerManager;
  import android.os.RemoteException;
  import android.os.ResultReceiver;
  import android.os.ShellCallback;
  import android.os.UserHandle;
  import android.util.AttributeSet;
  import android.util.DisplayMetrics;
  import android.util.TypedValue;
  import android.view.BridgeInflater;
  import android.view.Display;
  import android.view.DisplayAdjustments;
  import android.view.View;
  import android.view.ViewGroup;
  import android.view.WindowManager;
  import android.view.accessibility.AccessibilityManager;
  import android.view.textservice.TextServicesManager;
  
  import java.io.File;
  import java.io.FileDescriptor;
  import java.io.FileInputStream;
  import java.io.FileNotFoundException;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.IdentityHashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.concurrent.Executor;
  
  import static android.os._Original_Build.VERSION_CODES.JELLY_BEAN_MR1;
  import static com.android.layoutlib.bridge.android.RenderParamsFlags.FLAG_KEY_APPLICATION_PACKAGE;
  
  /**
   * Custom implementation of Context/Activity to handle non compiled resources.
   */
  @SuppressWarnings("deprecation")  // For use of Pair.
  public class BridgeContext extends Context {
      private static final String PREFIX_THEME_APPCOMPAT = "Theme.AppCompat";
  
      private static final Map<String, ResourceValue> FRAMEWORK_PATCHED_VALUES = new HashMap<>(2);
      private static final Map<String, ResourceValue> FRAMEWORK_REPLACE_VALUES = new HashMap<>(3);
  
      static {
          FRAMEWORK_PATCHED_VALUES.put("animateFirstView",
                  new ResourceValueImpl(ResourceNamespace.ANDROID, ResourceType.BOOL,
                          "animateFirstView", "false"));
          FRAMEWORK_PATCHED_VALUES.put("animateLayoutChanges",
                  new ResourceValueImpl(ResourceNamespace.ANDROID, ResourceType.BOOL,
                          "animateLayoutChanges", "false"));
  
  
          FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionItemLayout",
                  new ResourceValueImpl(ResourceNamespace.ANDROID, ResourceType.LAYOUT,
                          "textEditSuggestionItemLayout", "text_edit_suggestion_item"));
          FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionContainerLayout",
                  new ResourceValueImpl(ResourceNamespace.ANDROID, ResourceType.LAYOUT,
                          "textEditSuggestionContainerLayout", "text_edit_suggestion_container"));
          FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionHighlightStyle",
                  new ResourceValueImpl(ResourceNamespace.ANDROID, ResourceType.STYLE,
                          "textEditSuggestionHighlightStyle",
                          "TextAppearance.Holo.SuggestionHighlight"));
      }
  
      /** The map adds cookies to each view so that IDE can link xml tags to views. */
      private final HashMap<View, Object> mViewKeyMap = new HashMap<>();
      /**
       * In some cases, when inflating an xml, some objects are created. Then later, the objects are
       * converted to views. This map stores the mapping from objects to cookies which can then be
       * used to populate the mViewKeyMap.
       */
      private final HashMap<Object, Object> mViewKeyHelpMap = new HashMap<>();
      private final BridgeAssetManager mAssets;
      private Resources mSystemResources;
      private final Object mProjectKey;
      private final DisplayMetrics mMetrics;
      private final RenderResources mRenderResources;
      private final Configuration mConfig;
      private final ApplicationInfo mApplicationInfo;
      private final LayoutlibCallback mLayoutlibCallback;
      private final WindowManager mWindowManager;
      private final DisplayManager mDisplayManager;
      private final HashMap<View, Integer> mScrollYPos = new HashMap<>();
      private final HashMap<View, Integer> mScrollXPos = new HashMap<>();
  
      private Resources.Theme mTheme;
  
      private final Map<Object, Map<ResourceReference, ResourceValue>> mDefaultPropMaps =
              new IdentityHashMap<>();
      private final Map<Object, ResourceReference> mDefaultStyleMap = new IdentityHashMap<>();
  
      // cache for TypedArray generated from StyleResourceValue object
      private TypedArrayCache mTypedArrayCache;
      private BridgeInflater mBridgeInflater;
  
      private BridgeContentResolver mContentResolver;
  
      private final Stack<BridgeXmlBlockParser> mParserStack = new Stack<>();
      private SharedPreferences mSharedPreferences;
      private ClassLoader mClassLoader;
      private IBinder mBinder;
      private PackageManager mPackageManager;
      private Boolean mIsThemeAppCompat;
      private final ResourceNamespace mAppCompatNamespace;
      private final Map<Key<?>, Object> mUserData = new HashMap<>();
  
      /**
       * Some applications that target both pre API 17 and post API 17, set the newer attrs to
       * reference the older ones. For example, android:paddingStart will resolve to
       * android:paddingLeft. This way the apps need to only define paddingLeft at any other place.
       * This a map from value to attribute name. Warning for missing references shouldn't be logged
       * if value and attr name pair is the same as an entry in this map.
       */
      private static Map<String, String> RTL_ATTRS = new HashMap<>(10);
  
      static {
          RTL_ATTRS.put("?android:attr/paddingLeft", "paddingStart");
          RTL_ATTRS.put("?android:attr/paddingRight", "paddingEnd");
          RTL_ATTRS.put("?android:attr/layout_marginLeft", "layout_marginStart");
          RTL_ATTRS.put("?android:attr/layout_marginRight", "layout_marginEnd");
          RTL_ATTRS.put("?android:attr/layout_toLeftOf", "layout_toStartOf");
          RTL_ATTRS.put("?android:attr/layout_toRightOf", "layout_toEndOf");
          RTL_ATTRS.put("?android:attr/layout_alignParentLeft", "layout_alignParentStart");
          RTL_ATTRS.put("?android:attr/layout_alignParentRight", "layout_alignParentEnd");
          RTL_ATTRS.put("?android:attr/drawableLeft", "drawableStart");
          RTL_ATTRS.put("?android:attr/drawableRight", "drawableEnd");
      }
  
      /**
       * @param projectKey An Object identifying the project. This is used for the cache mechanism.
       * @param metrics the {@link DisplayMetrics}.
       * @param renderResources the configured resources (both framework and projects) for this
       * render.
       * @param config the Configuration object for this render.
       * @param targetSdkVersion the targetSdkVersion of the application.
       */
      public BridgeContext(Object projectKey, @NonNull DisplayMetrics metrics,
              @NonNull RenderResources renderResources,
              @NonNull AssetRepository assets,
              @NonNull LayoutlibCallback layoutlibCallback,
              @NonNull Configuration config,
              int targetSdkVersion,
              boolean hasRtlSupport) {
          mProjectKey = projectKey;
          mMetrics = metrics;
          mLayoutlibCallback = layoutlibCallback;
  
          mRenderResources = renderResources;
          mConfig = config;
          AssetManager systemAssetManager = AssetManager.getSystem();
          if (systemAssetManager instanceof BridgeAssetManager) {
              mAssets = (BridgeAssetManager) systemAssetManager;
          } else {
              throw new AssertionError("Creating BridgeContext without initializing Bridge");
          }
          mAssets.setAssetRepository(assets);
  
          mApplicationInfo = new ApplicationInfo();
          mApplicationInfo.targetSdkVersion = targetSdkVersion;
          if (hasRtlSupport) {
              mApplicationInfo.flags = mApplicationInfo.flags | ApplicationInfo.FLAG_SUPPORTS_RTL;
          }
  
          mWindowManager = new WindowManagerImpl(mMetrics);
          mDisplayManager = new DisplayManager(this);
  
          if (mLayoutlibCallback.isResourceNamespacingRequired()) {
              if (mLayoutlibCallback.hasAndroidXAppCompat()) {
                  mAppCompatNamespace = ResourceNamespace.APPCOMPAT;
              } else {
                  mAppCompatNamespace = ResourceNamespace.APPCOMPAT_LEGACY;
              }
          } else {
              mAppCompatNamespace = ResourceNamespace.RES_AUTO;
          }
      }
  
      /**
       * Initializes the {@link Resources} singleton to be linked to this {@link Context}, its
       * {@link DisplayMetrics}, {@link Configuration}, and {@link LayoutlibCallback}.
       *
       * @see #disposeResources()
       */
      public void initResources() {
          AssetManager assetManager = AssetManager.getSystem();
  
          mSystemResources = Resources_Delegate.initSystem(
                  this,
                  assetManager,
                  mMetrics,
                  mConfig,
                  mLayoutlibCallback);
          mTheme = mSystemResources.newTheme();
      }
  
      /**
       * Disposes the {@link Resources} singleton and the AssetRepository inside BridgeAssetManager.
       */
      public void disposeResources() {
          Resources_Delegate.disposeSystem();
  
          // The BridgeAssetManager pointed to by the mAssets field is a long-lived object, but
          // the AssetRepository is not. To prevent it from leaking clear a reference to it from
          // the BridgeAssetManager.
          mAssets.releaseAssetRepository();
      }
  
      public void setBridgeInflater(BridgeInflater inflater) {
          mBridgeInflater = inflater;
      }
  
      public void addViewKey(View view, Object viewKey) {
          mViewKeyMap.put(view, viewKey);
      }
  
      public Object getViewKey(View view) {
          return mViewKeyMap.get(view);
      }
  
      public void addCookie(Object o, Object cookie) {
          mViewKeyHelpMap.put(o, cookie);
      }
  
      public Object getCookie(Object o) {
          return mViewKeyHelpMap.get(o);
      }
  
      public Object getProjectKey() {
          return mProjectKey;
      }
  
      public DisplayMetrics getMetrics() {
          return mMetrics;
      }
  
      public LayoutlibCallback getLayoutlibCallback() {
          return mLayoutlibCallback;
      }
  
      public RenderResources getRenderResources() {
          return mRenderResources;
      }
  
      public Map<Object, Map<ResourceReference, ResourceValue>> getDefaultProperties() {
          return mDefaultPropMaps;
      }
  
      public Map<Object, ResourceReference> getDefaultNamespacedStyles() {
          return mDefaultStyleMap;
      }
  
      public Configuration getConfiguration() {
          return mConfig;
      }
  
      /**
       * Adds a parser to the stack.
       * @param parser the parser to add.
       */
      public void pushParser(BridgeXmlBlockParser parser) {
          if (ParserFactory.LOG_PARSER) {
              System.out.println("PUSH " + parser.getParser().toString());
          }
          mParserStack.push(parser);
      }
  
      /**
       * Removes the parser at the top of the stack
       */
      public void popParser() {
          BridgeXmlBlockParser parser = mParserStack.pop();
          if (ParserFactory.LOG_PARSER) {
              System.out.println("POPD " + parser.getParser().toString());
          }
      }
  
      /**
       * Returns the current parser at the top the of the stack.
       * @return a parser or null.
       */
      private BridgeXmlBlockParser getCurrentParser() {
          return mParserStack.peek();
      }
  
      /**
       * Returns the previous parser.
       * @return a parser or null if there isn't any previous parser
       */
      public BridgeXmlBlockParser getPreviousParser() {
          if (mParserStack.size() < 2) {
              return null;
          }
          return mParserStack.get(mParserStack.size() - 2);
      }
  
      public boolean resolveThemeAttribute(int resId, TypedValue outValue, boolean resolveRefs) {
          ResourceReference resourceInfo = Bridge.resolveResourceId(resId);
          if (resourceInfo == null) {
              resourceInfo = mLayoutlibCallback.resolveResourceId(resId);
          }
  
          if (resourceInfo == null || resourceInfo.getResourceType() != ResourceType.ATTR) {
              return false;
          }
  
          ResourceValue value = mRenderResources.findItemInTheme(resourceInfo);
          if (resolveRefs) {
              value = mRenderResources.resolveResValue(value);
          }
  
          if (value == null) {
              // unable to find the attribute.
              return false;
          }
  
          // check if this is a style resource
          if (value instanceof StyleResourceValue) {
              // get the id that will represent this style.
              outValue.resourceId = getDynamicIdByStyle((StyleResourceValue) value);
              return true;
          }
  
          String stringValue = value.getValue();
          if (!stringValue.isEmpty()) {
              if (stringValue.charAt(0) == '#') {
                  outValue.type = TypedValue.TYPE_INT_COLOR_ARGB8;
                  outValue.data = Color.parseColor(value.getValue());
              }
              else if (stringValue.charAt(0) == '@') {
                  outValue.type = TypedValue.TYPE_REFERENCE;
              }
          }
  
          int a = getResourceId(value.asReference(), 0 /*defValue*/);
  
          if (a != 0) {
              outValue.resourceId = a;
              return true;
          }
  
          // If the value is not a valid reference, fallback to pass the value as a string.
          outValue.string = stringValue;
          return true;
      }
  
  
      public ResourceReference resolveId(int id) {
          // first get the String related to this id in the framework
          ResourceReference resourceInfo = Bridge.resolveResourceId(id);
  
          if (resourceInfo != null) {
              return resourceInfo;
          }
  
          // didn't find a match in the framework? look in the project.
          if (mLayoutlibCallback != null) {
              resourceInfo = mLayoutlibCallback.resolveResourceId(id);
  
              if (resourceInfo != null) {
                  return resourceInfo;
              }
          }
  
          return null;
      }
  
      public Pair<View, Boolean> inflateView(ResourceReference layout, ViewGroup parent,
              @SuppressWarnings("SameParameterValue") boolean attachToRoot,
              boolean skipCallbackParser) {
          boolean isPlatformLayout = layout.getNamespace().equals(ResourceNamespace.ANDROID);
  
          if (!isPlatformLayout && !skipCallbackParser) {
              // check if the project callback can provide us with a custom parser.
              ILayoutPullParser parser = null;
              ResourceValue layoutValue = mRenderResources.getResolvedResource(layout);
              if (layoutValue != null) {
                  parser = getLayoutlibCallback().getParser(layoutValue);
              }
  
              if (parser != null) {
                  BridgeXmlBlockParser blockParser =
                          new BridgeXmlBlockParser(parser, this, layout.getNamespace());
                  try {
                      pushParser(blockParser);
                      return Pair.of(
                              mBridgeInflater.inflate(blockParser, parent, attachToRoot),
                              Boolean.TRUE);
                  } finally {
                      popParser();
                  }
              }
          }
  
          ResourceValue resValue = mRenderResources.getResolvedResource(layout);
  
          if (resValue != null) {
              String path = resValue.getValue();
              // We need to create a pull parser around the layout XML file, and then
              // give that to our XmlBlockParser.
              try {
                  XmlPullParser parser = ParserFactory.create(path, true);
                  if (parser != null) {
                      // Set the layout ref to have correct view cookies.
                      mBridgeInflater.setResourceReference(layout);
  
                      BridgeXmlBlockParser blockParser =
                              new BridgeXmlBlockParser(parser, this, layout.getNamespace());
                      try {
                          pushParser(blockParser);
                          return Pair.of(mBridgeInflater.inflate(blockParser, parent, attachToRoot),
                                  Boolean.FALSE);
                      } finally {
                          popParser();
                      }
                  } else {
                      Bridge.getLog().error(LayoutLog.TAG_BROKEN,
                              String.format("File %s is missing!", path), null);
                  }
              } catch (XmlPullParserException e) {
                  Bridge.getLog().error(LayoutLog.TAG_BROKEN,
                          "Failed to parse file " + path, e, null /*data*/);
                  // we'll return null below.
              } finally {
                  mBridgeInflater.setResourceReference(null);
              }
          } else {
              Bridge.getLog().error(LayoutLog.TAG_BROKEN,
                      String.format("Layout %s%s does not exist.", isPlatformLayout ? "android:" : "",
                              layout.getName()), null);
          }
  
          return Pair.of(null, Boolean.FALSE);
      }
  
      /**
       * Returns whether the current selected theme is based on AppCompat
       */
      public boolean isAppCompatTheme() {
          // If a cached value exists, return it.
          if (mIsThemeAppCompat != null) {
              return mIsThemeAppCompat;
          }
          // Ideally, we should check if the corresponding activity extends
          // android.support.v7.app.ActionBarActivity, and not care about the theme name at all.
          StyleResourceValue defaultTheme = mRenderResources.getDefaultTheme();
          // We can't simply check for parent using resources.themeIsParentOf() since the
          // inheritance structure isn't really what one would expect. The first common parent
          // between Theme.AppCompat.Light and Theme.AppCompat is Theme.Material (for v21).
          boolean isThemeAppCompat = false;
          for (int i = 0; i < 50; i++) {
              if (defaultTheme == null) {
                  break;
              }
              // for loop ensures that we don't run into cyclic theme inheritance.
              if (defaultTheme.getName().startsWith(PREFIX_THEME_APPCOMPAT)) {
                  isThemeAppCompat = true;
                  break;
              }
              defaultTheme = mRenderResources.getParent(defaultTheme);
          }
          mIsThemeAppCompat = isThemeAppCompat;
          return isThemeAppCompat;
      }
  
      // ------------ Context methods
  
      @Override
      public Resources getResources() {
          return mSystemResources;
      }
  
      @Override
      public Theme getTheme() {
          return mTheme;
      }
  
      @Override
      public ClassLoader getClassLoader() {
          // The documentation for this method states that it should return a class loader one can
          // use to retrieve classes in this package. However, when called by LayoutInflater, we do
          // not want the class loader to return app's custom views.
          // This is so that the IDE can instantiate the custom views and also generate proper error
          // messages in case of failure. This also enables the IDE to fallback to MockView in case
          // there's an exception thrown when trying to inflate the custom view.
          // To work around this issue, LayoutInflater is modified via LayoutLib Create tool to
          // replace invocations of this method to a new method: getFrameworkClassLoader(). Also,
          // the method is injected into Context. The implementation of getFrameworkClassLoader() is:
          // "return getClass().getClassLoader();". This means that when LayoutInflater asks for
          // the context ClassLoader, it gets only LayoutLib's ClassLoader which doesn't have
          // access to the apps's custom views.
          // This method can now return the right ClassLoader, which CustomViews can use to do the
          // right thing.
          if (mClassLoader == null) {
              mClassLoader = new ClassLoader(getClass().getClassLoader()) {
                  @Override
                  protected Class<?> findClass(String name) throws ClassNotFoundException {
                      for (String prefix : BridgeInflater.getClassPrefixList()) {
                          if (name.startsWith(prefix)) {
                              // These are framework classes and should not be loaded from the app.
                              throw new ClassNotFoundException(name + " not found");
                          }
                      }
                      return BridgeContext.this.mLayoutlibCallback.findClass(name);
                  }
              };
          }
          return mClassLoader;
      }
  
      @Override
      public Object getSystemService(String service) {
          switch (service) {
              case LAYOUT_INFLATER_SERVICE:
                  return mBridgeInflater;
  
              case TEXT_SERVICES_MANAGER_SERVICE:
                  // we need to return a valid service to avoid NPE
                  return TextServicesManager.getInstance();
  
              case WINDOW_SERVICE:
                  return mWindowManager;
  
              case POWER_SERVICE:
                  return new PowerManager(this, new BridgePowerManager(), new Handler());
  
              case DISPLAY_SERVICE:
                  return mDisplayManager;
  
              case ACCESSIBILITY_SERVICE:
                  return AccessibilityManager.getInstance(this);
  
              case INPUT_METHOD_SERVICE:  // needed by SearchView
              case AUTOFILL_MANAGER_SERVICE:
              case AUDIO_SERVICE:
              case TEXT_CLASSIFICATION_SERVICE:
              case CONTENT_CAPTURE_MANAGER_SERVICE:
                  return null;
              default:
                  assert false : "Unsupported Service: " + service;
          }
  
          return null;
      }
  
      @Override
      public String getSystemServiceName(Class<?> serviceClass) {
          return SystemServiceRegistry_Accessor.getSystemServiceName(serviceClass);
      }
  
      /**
       * Same as Context#obtainStyledAttributes. We do not override the base method to give the
       * original Context the chance to override the theme when needed.
       */
      @Nullable
      public final BridgeTypedArray internalObtainStyledAttributes(int resId, int[] attrs)
              throws Resources.NotFoundException {
          StyleResourceValue style = null;
          // get the StyleResourceValue based on the resId;
          if (resId != 0) {
              style = getStyleByDynamicId(resId);
  
              if (style == null) {
                  // In some cases, style may not be a dynamic id, so we do a full search.
                  ResourceReference ref = resolveId(resId);
                  if (ref != null) {
                      style = mRenderResources.getStyle(ref);
                  }
              }
  
              if (style == null) {
                  Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE,
                          "Failed to find style with " + resId, null);
                  return null;
              }
          }
  
          if (mTypedArrayCache == null) {
              mTypedArrayCache = new TypedArrayCache();
          }
  
          List<StyleResourceValue> currentThemes = mRenderResources.getAllThemes();
  
          Pair<BridgeTypedArray, Map<ResourceReference, ResourceValue>> typeArrayAndPropertiesPair =
                  mTypedArrayCache.get(attrs, currentThemes, resId);
  
          if (typeArrayAndPropertiesPair == null) {
              typeArrayAndPropertiesPair = createStyleBasedTypedArray(style, attrs);
              mTypedArrayCache.put(attrs, currentThemes, resId, typeArrayAndPropertiesPair);
          }
          // Add value to defaultPropsMap if needed
          if (typeArrayAndPropertiesPair.getSecond() != null) {
              BridgeXmlBlockParser parser = getCurrentParser();
              Object key = parser != null ? parser.getViewCookie() : null;
              if (key != null) {
                  Map<ResourceReference, ResourceValue> defaultPropMap = mDefaultPropMaps.get(key);
                  if (defaultPropMap == null) {
                      defaultPropMap = typeArrayAndPropertiesPair.getSecond();
                      mDefaultPropMaps.put(key, defaultPropMap);
                  } else {
                      defaultPropMap.putAll(typeArrayAndPropertiesPair.getSecond());
                  }
              }
          }
          return typeArrayAndPropertiesPair.getFirst();
      }
  
      /**
       * Same as Context#obtainStyledAttributes. We do not override the base method to give the
       * original Context the chance to override the theme when needed.
       */
      @Nullable
      public BridgeTypedArray internalObtainStyledAttributes(@Nullable AttributeSet set, int[] attrs,
              int defStyleAttr, int defStyleRes) {
  
          Map<ResourceReference, ResourceValue> defaultPropMap = null;
          Object key = null;
  
          ResourceNamespace currentFileNamespace;
          ResourceNamespace.Resolver resolver;
  
          // Hint: for XmlPullParser, attach source //DEVICE_SRC/dalvik/libcore/xml/src/java
          if (set instanceof BridgeXmlBlockParser) {
              BridgeXmlBlockParser parser;
              parser = (BridgeXmlBlockParser)set;
  
              key = parser.getViewCookie();
              if (key != null) {
                  defaultPropMap = mDefaultPropMaps.computeIfAbsent(key, k -> new HashMap<>());
              }
  
              currentFileNamespace = parser.getFileResourceNamespace();
              resolver = new XmlPullParserResolver(parser, mLayoutlibCallback.getImplicitNamespaces());
          } else if (set instanceof BridgeLayoutParamsMapAttributes) {
              // This is for temp layout params generated dynamically in MockView. The set contains
              // hardcoded values and we don't need to worry about resolving them.
              currentFileNamespace = ResourceNamespace.RES_AUTO;
              resolver = Resolver.EMPTY_RESOLVER;
          } else if (set != null) {
              // really this should not be happening since its instantiated in Bridge
              Bridge.getLog().error(LayoutLog.TAG_BROKEN,
                      "Parser is not a BridgeXmlBlockParser!", null);
              return null;
          } else {
              // `set` is null, so there will be no values to resolve.
              currentFileNamespace = ResourceNamespace.RES_AUTO;
              resolver = Resolver.EMPTY_RESOLVER;
          }
  
          List<AttributeHolder> attributeList = searchAttrs(attrs);
  
          BridgeTypedArray ta =
                  Resources_Delegate.newTypeArray(mSystemResources, attrs.length);
  
          // Look for a custom style.
          StyleResourceValue customStyleValues = null;
          if (set != null) {
              String customStyle = set.getAttributeValue(null, "style");
              if (customStyle != null) {
                  ResourceValue resolved = mRenderResources.resolveResValue(
                          new UnresolvedResourceValue(customStyle, currentFileNamespace, resolver));
  
                  if (resolved instanceof StyleResourceValue) {
                      customStyleValues = (StyleResourceValue) resolved;
                  }
              }
          }
  
          // resolve the defStyleAttr value into a StyleResourceValue
          StyleResourceValue defStyleValues = null;
  
          if (defStyleAttr != 0) {
              // get the name from the int.
              ResourceReference defStyleAttribute = searchAttr(defStyleAttr);
  
              if (defStyleAttribute == null) {
                  // This should be rare. Happens trying to map R.style.foo to @style/foo fails.
                  // This will happen if the user explicitly used a non existing int value for
                  // defStyleAttr or there's something wrong with the project structure/build.
                  Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE,
                          "Failed to find the style corresponding to the id " + defStyleAttr, null);
              } else {
                  // look for the style in the current theme, and its parent:
                  ResourceValue item = mRenderResources.findItemInTheme(defStyleAttribute);
  
                  if (item != null) {
                      if (key != null) {
                          mDefaultStyleMap.put(key, defStyleAttribute);
                      }
                      // item is a reference to a style entry. Search for it.
                      item = mRenderResources.resolveResValue(item);
                      if (item instanceof StyleResourceValue) {
                          defStyleValues = (StyleResourceValue) item;
                      }
                  }
              }
          }
  
          if (defStyleValues == null && defStyleRes != 0) {
              StyleResourceValue item = getStyleByDynamicId(defStyleRes);
              if (item != null) {
                  defStyleValues = item;
              } else {
                  ResourceReference value = Bridge.resolveResourceId(defStyleRes);
                  if (value == null) {
                      value = mLayoutlibCallback.resolveResourceId(defStyleRes);
                  }
  
                  if (value != null) {
                      if ((value.getResourceType() == ResourceType.STYLE)) {
                          // look for the style in all resources:
                          item = mRenderResources.getStyle(value);
                          if (item != null) {
                              if (key != null) {
                                  mDefaultStyleMap.put(key, item.asReference());
                              }
  
                              defStyleValues = item;
                          } else {
                              Bridge.getLog().error(null,
                                      String.format(
                                              "Style with id 0x%x (resolved to '%s') does not exist.",
                                              defStyleRes, value.getName()),
                                      null);
                          }
                      } else {
                          Bridge.getLog().error(null,
                                  String.format(
                                          "Resource id 0x%x is not of type STYLE (instead %s)",
                                          defStyleRes, value.getResourceType().name()),
                                  null);
                      }
                  } else {
                      Bridge.getLog().error(null,
                              String.format(
                                      "Failed to find style with id 0x%x in current theme",
                                      defStyleRes),
                              null);
                  }
              }
          }
  
          if (attributeList != null) {
              for (int index = 0 ; index < attributeList.size() ; index++) {
                  AttributeHolder attributeHolder = attributeList.get(index);
  
                  if (attributeHolder == null) {
                      continue;
                  }
  
                  String attrName = attributeHolder.getName();
                  String value = null;
                  if (set != null) {
                      value = set.getAttributeValue(
                              attributeHolder.getNamespace().getXmlNamespaceUri(), attrName);
  
                      // if this is an app attribute, and the first get fails, try with the
                      // new res-auto namespace as well
                      if (attributeHolder.getNamespace() != ResourceNamespace.ANDROID && value == null) {
                          value = set.getAttributeValue(BridgeConstants.NS_APP_RES_AUTO, attrName);
                      }
                  }
  
                  // Calculate the default value from the Theme in two cases:
                  //   - If defaultPropMap is not null, get the default value to add it to the list
                  //   of default values of properties.
                  //   - If value is null, it means that the attribute is not directly set as an
                  //   attribute in the XML so try to get the default value.
                  ResourceValue defaultValue = null;
                  if (defaultPropMap != null || value == null) {
                      // look for the value in the custom style first (and its parent if needed)
                      ResourceReference attrRef = attributeHolder.asReference();
                      if (customStyleValues != null) {
                          defaultValue =
                                  mRenderResources.findItemInStyle(customStyleValues, attrRef);
                      }
  
                      // then look for the value in the default Style (and its parent if needed)
                      if (defaultValue == null && defStyleValues != null) {
                          defaultValue =
                                  mRenderResources.findItemInStyle(defStyleValues, attrRef);
                      }
  
                      // if the item is not present in the defStyle, we look in the main theme (and
                      // its parent themes)
                      if (defaultValue == null) {
                          defaultValue =
                                  mRenderResources.findItemInTheme(attrRef);
                      }
  
                      // if we found a value, we make sure this doesn't reference another value.
                      // So we resolve it.
                      if (defaultValue != null) {
                          if (defaultPropMap != null) {
                              defaultPropMap.put(attrRef, defaultValue);
                          }
  
                          defaultValue = mRenderResources.resolveResValue(defaultValue);
                      }
                  }
                  // Done calculating the defaultValue.
  
                  // If there's no direct value for this attribute in the XML, we look for default
                  // values in the widget defStyle, and then in the theme.
                  if (value == null) {
                      if (attributeHolder.getNamespace() == ResourceNamespace.ANDROID) {
                          // For some framework values, layoutlib patches the actual value in the
                          // theme when it helps to improve the final preview. In most cases
                          // we just disable animations.
                          ResourceValue patchedValue = FRAMEWORK_PATCHED_VALUES.get(attrName);
                          if (patchedValue != null) {
                              defaultValue = patchedValue;
                          }
                      }
  
                      // If we found a value, we make sure this doesn't reference another value.
                      // So we resolve it.
                      if (defaultValue != null) {
                          // If the value is a reference to another theme attribute that doesn't
                          // exist, we should log a warning and omit it.
                          String val = defaultValue.getValue();
                          if (val != null && val.startsWith(SdkConstants.PREFIX_THEME_REF)) {
                              // Because we always use the latest framework code, some resources might
                              // fail to resolve when using old themes (they haven't been backported).
                              // Since this is an artifact caused by us using always the latest
                              // code, we check for some of those values and replace them here.
                              ResourceReference reference = defaultValue.getReference();
                              defaultValue = FRAMEWORK_REPLACE_VALUES.get(attrName);
  
                              // Only log a warning if the referenced value isn't one of the RTL
                              // attributes, or the app targets old API.
                              if (defaultValue == null &&
                                      (getApplicationInfo().targetSdkVersion < JELLY_BEAN_MR1 ||
                                      !attrName.equals(RTL_ATTRS.get(val)))) {
                                  if (reference != null) {
                                      val = reference.getResourceUrl().toString();
                                  }
                                  Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE_THEME_ATTR,
                                          String.format("Failed to find '%s' in current theme.", val),
                                          val);
                              }
                          }
                      }
  
                      ta.bridgeSetValue(
                              index,
                              attrName, attributeHolder.getNamespace(),
                              attributeHolder.getResourceId(),
                              defaultValue);
                  } else {
                      // There is a value in the XML, but we need to resolve it in case it's
                      // referencing another resource or a theme value.
                      ta.bridgeSetValue(
                              index,
                              attrName, attributeHolder.getNamespace(),
                              attributeHolder.getResourceId(),
                              mRenderResources.resolveResValue(
                                      new UnresolvedResourceValue(
                                              value, currentFileNamespace, resolver)));
                  }
              }
          }
  
          ta.sealArray();
  
          return ta;
      }
  
      @Override
      public Looper getMainLooper() {
          return Looper.myLooper();
      }
  
  
      @Override
      public String getPackageName() {
          if (mApplicationInfo.packageName == null) {
              mApplicationInfo.packageName = mLayoutlibCallback.getFlag(FLAG_KEY_APPLICATION_PACKAGE);
          }
          return mApplicationInfo.packageName;
      }
  
      @Override
      public PackageManager getPackageManager() {
          if (mPackageManager == null) {
              mPackageManager = new BridgePackageManager();
          }
          return mPackageManager;
      }
  
      // ------------- private new methods
  
      /**
       * Creates a {@link BridgeTypedArray} by filling the values defined by the int[] with the
       * values found in the given style. If no style is specified, the default theme, along with the
       * styles applied to it are used.
       *
       * @see #obtainStyledAttributes(int, int[])
       */
      private Pair<BridgeTypedArray, Map<ResourceReference, ResourceValue>> createStyleBasedTypedArray(
              @Nullable StyleResourceValue style, int[] attrs) throws Resources.NotFoundException {
          List<AttributeHolder> attributes = searchAttrs(attrs);
  
          BridgeTypedArray ta =
                  Resources_Delegate.newTypeArray(mSystemResources, attrs.length);
  
          Map<ResourceReference, ResourceValue> defaultPropMap = new HashMap<>();
          // for each attribute, get its name so that we can search it in the style
          for (int i = 0; i < attrs.length; i++) {
              AttributeHolder attrHolder = attributes.get(i);
  
              if (attrHolder != null) {
                  // look for the value in the given style
                  ResourceValue resValue;
                  if (style != null) {
                      resValue = mRenderResources.findItemInStyle(style, attrHolder.asReference());
                  } else {
                      resValue = mRenderResources.findItemInTheme(attrHolder.asReference());
                  }
  
                  if (resValue != null) {
                      defaultPropMap.put(attrHolder.asReference(), resValue);
                      // resolve it to make sure there are no references left.
                      resValue = mRenderResources.resolveResValue(resValue);
                      ta.bridgeSetValue(
                              i, attrHolder.getName(), attrHolder.getNamespace(),
                              attrHolder.getResourceId(),
                              resValue);
                  }
              }
          }
  
          ta.sealArray();
  
          return Pair.of(ta, defaultPropMap);
      }
  
      /**
       * The input int[] attributeIds is a list of attributes. The returns a list of information about
       * each attributes. The information is (name, isFramework)
       * <p/>
       *
       * @param attributeIds An attribute array reference given to obtainStyledAttributes.
       * @return List of attribute information.
       */
      private List<AttributeHolder> searchAttrs(int[] attributeIds) {
          List<AttributeHolder> results = new ArrayList<>(attributeIds.length);
  
          // for each attribute, get its name so that we can search it in the style
          for (int id : attributeIds) {
              ResourceReference refForId = Bridge.resolveResourceId(id);
              if (refForId == null) {
                  refForId = mLayoutlibCallback.resolveResourceId(id);
              }
  
              if (refForId != null) {
                  results.add(new AttributeHolder(id, refForId));
              } else {
                  results.add(null);
              }
          }
  
          return results;
      }
  
      /**
       * Searches for the attribute referenced by its internal id.
       */
      private ResourceReference searchAttr(int attrId) {
          ResourceReference attr = Bridge.resolveResourceId(attrId);
          if (attr == null) {
              attr = mLayoutlibCallback.resolveResourceId(attrId);
          }
  
          return attr;
      }
  
      /**
       * Maps a given style to a numeric id.
       *
       * <p>For now Bridge handles numeric ids (both fixed and dynamic) for framework and the callback
       * for non-framework. TODO(namespaces): teach the IDE about fixed framework ids and handle this
       * all in the callback.
       */
      public int getDynamicIdByStyle(StyleResourceValue resValue) {
          if (resValue.isFramework()) {
              return Bridge.getResourceId(resValue.getResourceType(), resValue.getName());
          } else {
              return mLayoutlibCallback.getOrGenerateResourceId(resValue.asReference());
          }
      }
  
      /**
       * Maps a numeric id back to {@link StyleResourceValue}.
       *
       * <p>For now framework numeric ids are handled by Bridge, so try there first and fall back to
       * the callback, which manages ids for non-framework resources. TODO(namespaces): manage all
       * ids in the IDE.
       *
       * <p>Once we the resource for the given id, we ask the IDE to get the
       * {@link StyleResourceValue} for it.
       */
      @Nullable
      private StyleResourceValue getStyleByDynamicId(int id) {
          ResourceReference reference = Bridge.resolveResourceId(id);
          if (reference == null) {
              reference = mLayoutlibCallback.resolveResourceId(id);
          }
  
          if (reference == null) {
              return null;
          }
  
          return mRenderResources.getStyle(reference);
      }
  
      public int getResourceId(@NonNull ResourceReference resource, int defValue) {
          if (getRenderResources().getUnresolvedResource(resource) != null) {
              if (resource.getNamespace().equals(ResourceNamespace.ANDROID)) {
                  return Bridge.getResourceId(resource.getResourceType(), resource.getName());
              } else if (mLayoutlibCallback != null) {
                  return mLayoutlibCallback.getOrGenerateResourceId(resource);
              }
          }
  
          return defValue;
      }
  
      public static Context getBaseContext(Context context) {
          while (context instanceof ContextWrapper) {
              context = ((ContextWrapper) context).getBaseContext();
          }
          return context;
      }
  
      /**
       * Returns the Framework attr resource reference with the given name.
       */
      @NonNull
      public static ResourceReference createFrameworkAttrReference(@NonNull String name) {
          return createFrameworkResourceReference(ResourceType.ATTR, name);
      }
  
      /**
       * Returns the Framework resource reference with the given type and name.
       */
      @NonNull
      public static ResourceReference createFrameworkResourceReference(@NonNull ResourceType type,
              @NonNull String name) {
          return new ResourceReference(ResourceNamespace.ANDROID, type, name);
      }
  
      /**
       * Returns the AppCompat attr resource reference with the given name.
       */
      @NonNull
      public ResourceReference createAppCompatAttrReference(@NonNull String name) {
          return createAppCompatResourceReference(ResourceType.ATTR, name);
      }
  
      /**
       * Returns the AppCompat resource reference with the given type and name.
       */
      @NonNull
      public ResourceReference createAppCompatResourceReference(@NonNull ResourceType type,
              @NonNull String name) {
          return new ResourceReference(mAppCompatNamespace, type, name);
      }
  
      public IBinder getBinder() {
          if (mBinder == null) {
              // create a dummy binder. We only need it be not null.
              mBinder = new IBinder() {
                  @Override
                  public String getInterfaceDescriptor() throws RemoteException {
                      return null;
                  }
  
                  @Override
                  public boolean pingBinder() {
                      return false;
                  }
  
                  @Override
                  public boolean isBinderAlive() {
                      return false;
                  }
  
                  @Override
                  public IInterface queryLocalInterface(String descriptor) {
                      return null;
                  }
  
                  @Override
                  public void dump(FileDescriptor fd, String[] args) throws RemoteException {
  
                  }
  
                  @Override
                  public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
  
                  }
  
                  @Override
                  public boolean transact(int code, Parcel data, Parcel reply, int flags)
                          throws RemoteException {
                      return false;
                  }
  
                  @Override
                  public void linkToDeath(DeathRecipient recipient, int flags)
                          throws RemoteException {
  
                  }
  
                  @Override
                  public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
                      return false;
                  }
  
                  @Override
                  public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
                    String[] args, ShellCallback shellCallback, ResultReceiver resultReceiver) {
                  }
              };
          }
          return mBinder;
      }
  
      //------------ NOT OVERRIDEN --------------------
  
      @Override
      public boolean bindService(Intent arg0, ServiceConnection arg1, int arg2) {
          // pass
          return false;
      }
  
      @Override
      public boolean bindService(Intent arg0, int arg1, Executor arg2, ServiceConnection arg3) {
          return false;
      }
  
      @Override
      public boolean bindIsolatedService(Intent arg0,
              int arg1, String arg2, Executor arg3, ServiceConnection arg4) {
          return false;
      }
  
      @Override
      public int checkCallingOrSelfPermission(String arg0) {
          // pass
          return 0;
      }
  
      @Override
      public int checkCallingOrSelfUriPermission(Uri arg0, int arg1) {
          // pass
          return 0;
      }
  
      @Override
      public int checkCallingPermission(String arg0) {
          // pass
          return 0;
      }
  
      @Override
      public int checkCallingUriPermission(Uri arg0, int arg1) {
          // pass
          return 0;
      }
  
      @Override
      public int checkPermission(String arg0, int arg1, int arg2) {
          // pass
          return 0;
      }
  
      @Override
      public int checkSelfPermission(String arg0) {
          // pass
          return 0;
      }
  
      @Override
      public int checkPermission(String arg0, int arg1, int arg2, IBinder arg3) {
          // pass
          return 0;
      }
  
      @Override
      public int checkUriPermission(Uri arg0, int arg1, int arg2, int arg3) {
          // pass
          return 0;
      }
  
      @Override
      public int checkUriPermission(Uri arg0, int arg1, int arg2, int arg3, IBinder arg4) {
          // pass
          return 0;
      }
  
      @Override
      public int checkUriPermission(Uri arg0, String arg1, String arg2, int arg3,
              int arg4, int arg5) {
          // pass
          return 0;
      }
  
      @Override
      public void clearWallpaper() {
          // pass
  
      }
  
      @Override
      public Context createPackageContext(String arg0, int arg1) {
          // pass
          return null;
      }
  
      @Override
      public Context createPackageContextAsUser(String arg0, int arg1, UserHandle user) {
          // pass
          return null;
      }
  
      @Override
      public Context createConfigurationContext(Configuration overrideConfiguration) {
          // pass
          return null;
      }
  
      @Override
      public Context createDisplayContext(Display display) {
          // pass
          return null;
      }
  
      @Override
      public Context createContextForSplit(String splitName) {
          // pass
          return null;
      }
  
      @Override
      public String[] databaseList() {
          // pass
          return null;
      }
  
      @Override
      public Context createApplicationContext(ApplicationInfo application, int flags)
              throws PackageManager.NameNotFoundException {
          return null;
      }
  
      @Override
      public boolean moveDatabaseFrom(Context sourceContext, String name) {
          // pass
          return false;
      }
  
      @Override
      public boolean deleteDatabase(String arg0) {
          // pass
          return false;
      }
  
      @Override
      public boolean deleteFile(String arg0) {
          // pass
          return false;
      }
  
      @Override
      public void enforceCallingOrSelfPermission(String arg0, String arg1) {
          // pass
  
      }
  
      @Override
      public void enforceCallingOrSelfUriPermission(Uri arg0, int arg1,
              String arg2) {
          // pass
  
      }
  
      @Override
      public void enforceCallingPermission(String arg0, String arg1) {
          // pass
  
      }
  
      @Override
      public void enforceCallingUriPermission(Uri arg0, int arg1, String arg2) {
          // pass
  
      }
  
      @Override
      public void enforcePermission(String arg0, int arg1, int arg2, String arg3) {
          // pass
  
      }
  
      @Override
      public void enforceUriPermission(Uri arg0, int arg1, int arg2, int arg3,
              String arg4) {
          // pass
  
      }
  
      @Override
      public void enforceUriPermission(Uri arg0, String arg1, String arg2,
              int arg3, int arg4, int arg5, String arg6) {
          // pass
  
      }
  
      @Override
      public String[] fileList() {
          // pass
          return null;
      }
  
      @Override
      public BridgeAssetManager getAssets() {
          return mAssets;
      }
  
      @Override
      public File getCacheDir() {
          // pass
          return null;
      }
  
      @Override
      public File getCodeCacheDir() {
          // pass
          return null;
      }
  
      @Override
      public File getExternalCacheDir() {
          // pass
          return null;
      }
  
      @Override
      public File getPreloadsFileCache() {
          // pass
          return null;
      }
  
      @Override
      public ContentResolver getContentResolver() {
          if (mContentResolver == null) {
              mContentResolver = new BridgeContentResolver(this);
          }
          return mContentResolver;
      }
  
      @Override
      public File getDatabasePath(String arg0) {
          // pass
          return null;
      }
  
      @Override
      public File getDir(String arg0, int arg1) {
          // pass
          return null;
      }
  
      @Override
      public File getFileStreamPath(String arg0) {
          // pass
          return null;
      }
  
      @Override
      public File getSharedPreferencesPath(String name) {
          // pass
          return null;
      }
  
      @Override
      public File getDataDir() {
          // pass
          return null;
      }
  
      @Override
      public File getFilesDir() {
          // pass
          return null;
      }
  
      @Override
      public File getNoBackupFilesDir() {
          // pass
          return null;
      }
  
      @Override
      public File getExternalFilesDir(String type) {
          // pass
          return null;
      }
  
      @Override
      public String getPackageCodePath() {
          // pass
          return null;
      }
  
      @Override
      public String getBasePackageName() {
          // pass
          return null;
      }
  
      @Override
      public String getOpPackageName() {
          // pass
          return null;
      }
  
      @Override
      public ApplicationInfo getApplicationInfo() {
          return mApplicationInfo;
      }
  
      @Override
      public String getPackageResourcePath() {
          // pass
          return null;
      }
  
      @Override
      public SharedPreferences getSharedPreferences(String arg0, int arg1) {
          if (mSharedPreferences == null) {
              mSharedPreferences = new BridgeSharedPreferences();
          }
          return mSharedPreferences;
      }
  
      @Override
      public SharedPreferences getSharedPreferences(File arg0, int arg1) {
          if (mSharedPreferences == null) {
              mSharedPreferences = new BridgeSharedPreferences();
          }
          return mSharedPreferences;
      }
  
      @Override
      public void reloadSharedPreferences() {
          // intentional noop
      }
  
      @Override
      public boolean moveSharedPreferencesFrom(Context sourceContext, String name) {
          // pass
          return false;
      }
  
      @Override
      public boolean deleteSharedPreferences(String name) {
          // pass
          return false;
      }
  
      @Override
      public Drawable getWallpaper() {
          // pass
          return null;
      }
  
      @Override
      public int getWallpaperDesiredMinimumWidth() {
          return -1;
      }
  
      @Override
      public int getWallpaperDesiredMinimumHeight() {
          return -1;
      }
  
      @Override
      public void grantUriPermission(String arg0, Uri arg1, int arg2) {
          // pass
  
      }
  
      @Override
      public FileInputStream openFileInput(String arg0) throws FileNotFoundException {
          // pass
          return null;
      }
  
      @Override
      public FileOutputStream openFileOutput(String arg0, int arg1) throws FileNotFoundException {
          // pass
          return null;
      }
  
      @Override
      public SQLiteDatabase openOrCreateDatabase(String arg0, int arg1, CursorFactory arg2) {
          // pass
          return null;
      }
  
      @Override
      public SQLiteDatabase openOrCreateDatabase(String arg0, int arg1,
              CursorFactory arg2, DatabaseErrorHandler arg3) {
          // pass
          return null;
      }
  
      @Override
      public Drawable peekWallpaper() {
          // pass
          return null;
      }
  
      @Override
      public Intent registerReceiver(BroadcastReceiver arg0, IntentFilter arg1) {
          // pass
          return null;
      }
  
      @Override
      public Intent registerReceiver(BroadcastReceiver arg0, IntentFilter arg1, int arg2) {
          // pass
          return null;
      }
  
      @Override
      public Intent registerReceiver(BroadcastReceiver arg0, IntentFilter arg1,
              String arg2, Handler arg3) {
          // pass
          return null;
      }
  
      @Override
      public Intent registerReceiver(BroadcastReceiver arg0, IntentFilter arg1,
              String arg2, Handler arg3, int arg4) {
          // pass
          return null;
      }
  
      @Override
      public Intent registerReceiverAsUser(BroadcastReceiver arg0, UserHandle arg0p5,
              IntentFilter arg1, String arg2, Handler arg3) {
          // pass
          return null;
      }
  
      @Override
      public void removeStickyBroadcast(Intent arg0) {
          // pass
  
      }
  
      @Override
      public void revokeUriPermission(Uri arg0, int arg1) {
          // pass
  
      }
  
      @Override
      public void revokeUriPermission(String arg0, Uri arg1, int arg2) {
          // pass
  
      }
  
      @Override
      public void sendBroadcast(Intent arg0) {
          // pass
  
      }
  
      @Override
      public void sendBroadcast(Intent arg0, String arg1) {
          // pass
  
      }
  
      @Override
      public void sendBroadcastMultiplePermissions(Intent intent, String[] receiverPermissions) {
          // pass
  
      }
  
      @Override
      public void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user,
              String[] receiverPermissions) {
          // pass
  
      }
  
      @Override
      public void sendBroadcast(Intent arg0, String arg1, Bundle arg2) {
          // pass
  
      }
  
      @Override
      public void sendBroadcast(Intent intent, String receiverPermission, int appOp) {
          // pass
      }
  
      @Override
      public void sendOrderedBroadcast(Intent arg0, String arg1) {
          // pass
  
      }
  
      @Override
      public void sendOrderedBroadcast(Intent arg0, String arg1,
              BroadcastReceiver arg2, Handler arg3, int arg4, String arg5,
              Bundle arg6) {
          // pass
  
      }
  
      @Override
      public void sendOrderedBroadcast(Intent arg0, String arg1,
              Bundle arg7, BroadcastReceiver arg2, Handler arg3, int arg4, String arg5,
              Bundle arg6) {
          // pass
  
      }
  
      @Override
      public void sendOrderedBroadcast(Intent intent, String receiverPermission, int appOp,
              BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
              String initialData, Bundle initialExtras) {
          // pass
      }
  
      @Override
      public void sendBroadcastAsUser(Intent intent, UserHandle user) {
          // pass
      }
  
      @Override
      public void sendBroadcastAsUser(Intent intent, UserHandle user,
              String receiverPermission) {
          // pass
      }
  
      @Override
      public void sendBroadcastAsUser(Intent intent, UserHandle user,
              String receiverPermission, Bundle options) {
          // pass
      }
  
      public void sendBroadcastAsUser(Intent intent, UserHandle user,
              String receiverPermission, int appOp) {
          // pass
      }
  
      @Override
      public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
              String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler,
              int initialCode, String initialData, Bundle initialExtras) {
          // pass
      }
  
      @Override
      public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
              String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
              Handler scheduler,
              int initialCode, String initialData, Bundle initialExtras) {
          // pass
      }
  
      @Override
      public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
              String receiverPermission, int appOp, Bundle options, BroadcastReceiver resultReceiver,
              Handler scheduler,
              int initialCode, String initialData, Bundle initialExtras) {
          // pass
      }
  
      @Override
      public void sendStickyBroadcast(Intent arg0) {
          // pass
  
      }
  
      @Override
      public void sendStickyOrderedBroadcast(Intent intent,
              BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData,
             Bundle initialExtras) {
          // pass
      }
  
      @Override
      public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) {
          // pass
      }
  
      @Override
      public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) {
          // pass
      }
  
      @Override
      public void sendStickyOrderedBroadcastAsUser(Intent intent,
              UserHandle user, BroadcastReceiver resultReceiver,
              Handler scheduler, int initialCode, String initialData,
              Bundle initialExtras) {
          // pass
      }
  
      @Override
      public void removeStickyBroadcastAsUser(Intent intent, UserHandle user) {
          // pass
      }
  
      @Override
      public void setTheme(int arg0) {
          // pass
  
      }
  
      @Override
      public void setWallpaper(Bitmap arg0) throws IOException {
          // pass
  
      }
  
      @Override
      public void setWallpaper(InputStream arg0) throws IOException {
          // pass
  
      }
  
      @Override
      public void startActivity(Intent arg0) {
          // pass
      }
  
      @Override
      public void startActivity(Intent arg0, Bundle arg1) {
          // pass
      }
  
      @Override
      public void startIntentSender(IntentSender intent,
              Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags)
              throws IntentSender.SendIntentException {
          // pass
      }
  
      @Override
      public void startIntentSender(IntentSender intent,
              Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags,
              Bundle options) throws IntentSender.SendIntentException {
          // pass
      }
  
      @Override
      public boolean startInstrumentation(ComponentName arg0, String arg1,
              Bundle arg2) {
          // pass
          return false;
      }
  
      @Override
      public ComponentName startService(Intent arg0) {
          // pass
          return null;
      }
  
      @Override
      public ComponentName startForegroundService(Intent service) {
          // pass
          return null;
      }
  
      @Override
      public ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) {
          // pass
          return null;
      }
  
      @Override
      public boolean stopService(Intent arg0) {
          // pass
          return false;
      }
  
      @Override
      public ComponentName startServiceAsUser(Intent arg0, UserHandle arg1) {
          // pass
          return null;
      }
  
      @Override
      public boolean stopServiceAsUser(Intent arg0, UserHandle arg1) {
          // pass
          return false;
      }
  
      @Override
      public void updateServiceGroup(@NonNull ServiceConnection conn, int group,
              int importance) {
          // pass
      }
  
      @Override
      public void unbindService(ServiceConnection arg0) {
          // pass
  
      }
  
      @Override
      public void unregisterReceiver(BroadcastReceiver arg0) {
          // pass
  
      }
  
      @Override
      public Context getApplicationContext() {
          return this;
      }
  
      @Override
      public void startActivities(Intent[] arg0) {
          // pass
  
      }
  
      @Override
      public void startActivities(Intent[] arg0, Bundle arg1) {
          // pass
  
      }
  
      @Override
      public boolean isRestricted() {
          return false;
      }
  
      @Override
      public File getObbDir() {
          Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, "OBB not supported", null);
          return null;
      }
  
      @Override
      public DisplayAdjustments getDisplayAdjustments(int displayId) {
          // pass
          return null;
      }
  
      @Override
      public Display getDisplay() {
          // pass
          return null;
      }
  
      @Override
      public int getDisplayId() {
          // pass
          return 0;
      }
  
      @Override
      public void updateDisplay(int displayId) {
          // pass
      }
  
      @Override
      public int getUserId() {
          return 0; // not used
      }
  
      @Override
      public File[] getExternalFilesDirs(String type) {
          // pass
          return new File[0];
      }
  
      @Override
      public File[] getObbDirs() {
          // pass
          return new File[0];
      }
  
      @Override
      public File[] getExternalCacheDirs() {
          // pass
          return new File[0];
      }
  
      @Override
      public File[] getExternalMediaDirs() {
          // pass
          return new File[0];
      }
  
      public void setScrollYPos(@NonNull View view, int scrollPos) {
          mScrollYPos.put(view, scrollPos);
      }
  
      public int getScrollYPos(@NonNull View view) {
          Integer pos = mScrollYPos.get(view);
          return pos != null ? pos : 0;
      }
  
      public void setScrollXPos(@NonNull View view, int scrollPos) {
          mScrollXPos.put(view, scrollPos);
      }
  
      public int getScrollXPos(@NonNull View view) {
          Integer pos = mScrollXPos.get(view);
          return pos != null ? pos : 0;
      }
  
      @Override
      public Context createDeviceProtectedStorageContext() {
          // pass
          return null;
      }
  
      @Override
      public Context createCredentialProtectedStorageContext() {
          // pass
          return null;
      }
  
      @Override
      public boolean isDeviceProtectedStorage() {
          return false;
      }
  
      @Override
      public boolean isCredentialProtectedStorage() {
          return false;
      }
  
      @Override
      public boolean canLoadUnsafeResources() {
          return true;
      }
  
      public <T> void putUserData(@NonNull Key<T> key, @Nullable T data) {
          mUserData.put(key, data);
      }
  
      @SuppressWarnings("unchecked")
      @Nullable
      public <T> T getUserData(@NonNull Key<T> key) {
          return (T) mUserData.get(key);
      }
  
      /**
       * No two Key instances are considered equal.
       *
       * @param <T> the type of values associated with the key
       */
      public static final class Key<T> {
          private final String name;
  
          @NonNull
          public static <T> Key<T> create(@NonNull String name) {
              return new Key<T>(name);
          }
  
          private Key(@NonNull String name) {
              this.name = name;
          }
  
          /** For debugging only. */
          @Override
          public String toString() {
              return name;
          }
      }
  
      private class AttributeHolder {
          private final int resourceId;
          @NonNull private final ResourceReference reference;
  
          private AttributeHolder(int resourceId, @NonNull ResourceReference reference) {
              this.resourceId = resourceId;
              this.reference = reference;
          }
  
          @NonNull
          private ResourceReference asReference() {
              return reference;
          }
  
          private int getResourceId() {
              return resourceId;
          }
  
          @NonNull
          private String getName() {
              return reference.getName();
          }
  
          @NonNull
          private ResourceNamespace getNamespace() {
              return reference.getNamespace();
          }
      }
  
      /**
       * The cached value depends on
       * <ol>
       * <li>{@code int[]}: the attributes for which TypedArray is created </li>
       * <li>{@code List<StyleResourceValue>}: the themes set on the context at the time of
       * creation of the TypedArray</li>
       * <li>{@code Integer}: the default style used at the time of creation</li>
       * </ol>
       *
       * The class is created by using nested maps resolving one dependency at a time.
       * <p/>
       * The final value of the nested maps is a pair of the typed array and a map of properties
       * that should be added to {@link #mDefaultPropMaps}, if needed.
       */
      private static class TypedArrayCache {
  
          private Map<int[],
                  Map<List<StyleResourceValue>,
                          Map<Integer, Pair<BridgeTypedArray,
                                  Map<ResourceReference, ResourceValue>>>>> mCache;
  
          private TypedArrayCache() {
              mCache = new IdentityHashMap<>();
          }
  
          public Pair<BridgeTypedArray, Map<ResourceReference, ResourceValue>> get(int[] attrs,
                  List<StyleResourceValue> themes, int resId) {
              Map<List<StyleResourceValue>, Map<Integer, Pair<BridgeTypedArray, Map<ResourceReference,
                      ResourceValue>>>>
                      cacheFromThemes = mCache.get(attrs);
              if (cacheFromThemes != null) {
                  Map<Integer, Pair<BridgeTypedArray, Map<ResourceReference, ResourceValue>>> cacheFromResId =
                          cacheFromThemes.get(themes);
                  if (cacheFromResId != null) {
                      return cacheFromResId.get(resId);
                  }
              }
              return null;
          }
  
          public void put(int[] attrs, List<StyleResourceValue> themes, int resId,
                  Pair<BridgeTypedArray, Map<ResourceReference, ResourceValue>> value) {
              Map<List<StyleResourceValue>, Map<Integer, Pair<BridgeTypedArray, Map<ResourceReference,
                      ResourceValue>>>>
                      cacheFromThemes = mCache.computeIfAbsent(attrs, k -> new HashMap<>());
              Map<Integer, Pair<BridgeTypedArray, Map<ResourceReference, ResourceValue>>> cacheFromResId =
                      cacheFromThemes.computeIfAbsent(themes, k -> new HashMap<>());
              cacheFromResId.put(resId, value);
          }
  
      }
  }
  