1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.layoutlib.bridge.android;
18
19import com.android.SdkConstants;
20import com.android.ide.common.rendering.api.AssetRepository;
21import com.android.ide.common.rendering.api.ILayoutPullParser;
22import com.android.ide.common.rendering.api.LayoutLog;
23import com.android.ide.common.rendering.api.LayoutlibCallback;
24import com.android.ide.common.rendering.api.RenderResources;
25import com.android.ide.common.rendering.api.ResourceNamespace;
26import com.android.ide.common.rendering.api.ResourceNamespace.Resolver;
27import com.android.ide.common.rendering.api.ResourceReference;
28import com.android.ide.common.rendering.api.ResourceValue;
29import com.android.ide.common.rendering.api.StyleResourceValue;
30import com.android.layoutlib.bridge.Bridge;
31import com.android.layoutlib.bridge.BridgeConstants;
32import com.android.layoutlib.bridge.android.view.WindowManagerImpl;
33import com.android.layoutlib.bridge.impl.ParserFactory;
34import com.android.layoutlib.bridge.impl.Stack;
35import com.android.resources.ResourceType;
36import com.android.util.Pair;
37import com.android.util.PropertiesMap;
38import com.android.util.PropertiesMap.Property;
39
40import org.xmlpull.v1.XmlPullParser;
41import org.xmlpull.v1.XmlPullParserException;
42
43import android.annotation.NonNull;
44import android.annotation.Nullable;
45import android.app.SystemServiceRegistry_Accessor;
46import android.content.BroadcastReceiver;
47import android.content.ComponentName;
48import android.content.ContentResolver;
49import android.content.Context;
50import android.content.ContextWrapper;
51import android.content.Intent;
52import android.content.IntentFilter;
53import android.content.IntentSender;
54import android.content.ServiceConnection;
55import android.content.SharedPreferences;
56import android.content.pm.ApplicationInfo;
57import android.content.pm.PackageManager;
58import android.content.res.AssetManager;
59import android.content.res.BridgeAssetManager;
60import android.content.res.BridgeTypedArray;
61import android.content.res.Configuration;
62import android.content.res.Resources;
63import android.content.res.Resources.Theme;
64import android.content.res.Resources_Delegate;
65import android.database.DatabaseErrorHandler;
66import android.database.sqlite.SQLiteDatabase;
67import android.database.sqlite.SQLiteDatabase.CursorFactory;
68import android.graphics.Bitmap;
69import android.graphics.Color;
70import android.graphics.drawable.Drawable;
71import android.hardware.display.DisplayManager;
72import android.net.Uri;
73import android.os.Bundle;
74import android.os.Handler;
75import android.os.IBinder;
76import android.os.IInterface;
77import android.os.Looper;
78import android.os.Parcel;
79import android.os.PowerManager;
80import android.os.RemoteException;
81import android.os.ResultReceiver;
82import android.os.ShellCallback;
83import android.os.UserHandle;
84import android.util.AttributeSet;
85import android.util.DisplayMetrics;
86import android.util.TypedValue;
87import android.view.BridgeInflater;
88import android.view.Display;
89import android.view.DisplayAdjustments;
90import android.view.View;
91import android.view.ViewGroup;
92import android.view.WindowManager;
93import android.view.accessibility.AccessibilityManager;
94import android.view.textservice.TextServicesManager;
95
96import java.io.File;
97import java.io.FileDescriptor;
98import java.io.FileInputStream;
99import java.io.FileNotFoundException;
100import java.io.FileOutputStream;
101import java.io.IOException;
102import java.io.InputStream;
103import java.util.ArrayList;
104import java.util.Collections;
105import java.util.HashMap;
106import java.util.IdentityHashMap;
107import java.util.List;
108import java.util.Map;
109
110import static android.os._Original_Build.VERSION_CODES.JELLY_BEAN_MR1;
111import static com.android.layoutlib.bridge.android.RenderParamsFlags.FLAG_KEY_APPLICATION_PACKAGE;
112
113/**
114 * Custom implementation of Context/Activity to handle non compiled resources.
115 */
116@SuppressWarnings("deprecation")  // For use of Pair.
117public class BridgeContext extends Context {
118    private static final String PREFIX_THEME_APPCOMPAT = "Theme.AppCompat";
119
120    private static final Map<String, ResourceValue> FRAMEWORK_PATCHED_VALUES = new HashMap<>(2);
121    private static final Map<String, ResourceValue> FRAMEWORK_REPLACE_VALUES = new HashMap<>(3);
122
123    private static final Resolver LEGACY_NAMESPACE_RESOLVER =
124            Collections.singletonMap(SdkConstants.TOOLS_PREFIX, SdkConstants.TOOLS_URI)::get;
125
126    static {
127        FRAMEWORK_PATCHED_VALUES.put("animateFirstView", new ResourceValue(
128                ResourceType.BOOL, "animateFirstView", "false", false));
129        FRAMEWORK_PATCHED_VALUES.put("animateLayoutChanges",
130                new ResourceValue(ResourceType.BOOL, "animateLayoutChanges", "false", false));
131
132
133        FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionItemLayout",
134                new ResourceValue(ResourceType.LAYOUT, "textEditSuggestionItemLayout",
135                        "text_edit_suggestion_item", true));
136        FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionContainerLayout",
137                new ResourceValue(ResourceType.LAYOUT, "textEditSuggestionContainerLayout",
138                        "text_edit_suggestion_container", true));
139        FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionHighlightStyle",
140                new ResourceValue(ResourceType.STYLE, "textEditSuggestionHighlightStyle",
141                        "TextAppearance.Holo.SuggestionHighlight", true));
142
143    }
144
145    /** The map adds cookies to each view so that IDE can link xml tags to views. */
146    private final HashMap<View, Object> mViewKeyMap = new HashMap<>();
147    /**
148     * In some cases, when inflating an xml, some objects are created. Then later, the objects are
149     * converted to views. This map stores the mapping from objects to cookies which can then be
150     * used to populate the mViewKeyMap.
151     */
152    private final HashMap<Object, Object> mViewKeyHelpMap = new HashMap<>();
153    private final BridgeAssetManager mAssets;
154    private Resources mSystemResources;
155    private final Object mProjectKey;
156    private final DisplayMetrics mMetrics;
157    private final RenderResources mRenderResources;
158    private final Configuration mConfig;
159    private final ApplicationInfo mApplicationInfo;
160    private final LayoutlibCallback mLayoutlibCallback;
161    private final WindowManager mWindowManager;
162    private final DisplayManager mDisplayManager;
163    private final HashMap<View, Integer> mScrollYPos = new HashMap<>();
164    private final HashMap<View, Integer> mScrollXPos = new HashMap<>();
165
166    private Resources.Theme mTheme;
167
168    private final Map<Object, PropertiesMap> mDefaultPropMaps = new IdentityHashMap<>();
169
170    // maps for dynamically generated id representing style objects (StyleResourceValue)
171    @Nullable
172    private Map<Integer, StyleResourceValue> mDynamicIdToStyleMap;
173    private Map<StyleResourceValue, Integer> mStyleToDynamicIdMap;
174    private int mDynamicIdGenerator = 0x02030000; // Base id for R.style in custom namespace
175
176    // cache for TypedArray generated from StyleResourceValue object
177    private TypedArrayCache mTypedArrayCache;
178    private BridgeInflater mBridgeInflater;
179
180    private BridgeContentResolver mContentResolver;
181
182    private final Stack<BridgeXmlBlockParser> mParserStack = new Stack<>();
183    private SharedPreferences mSharedPreferences;
184    private ClassLoader mClassLoader;
185    private IBinder mBinder;
186    private PackageManager mPackageManager;
187    private Boolean mIsThemeAppCompat;
188
189    /**
190     * Some applications that target both pre API 17 and post API 17, set the newer attrs to
191     * reference the older ones. For example, android:paddingStart will resolve to
192     * android:paddingLeft. This way the apps need to only define paddingLeft at any other place.
193     * This a map from value to attribute name. Warning for missing references shouldn't be logged
194     * if value and attr name pair is the same as an entry in this map.
195     */
196    private static Map<String, String> RTL_ATTRS = new HashMap<>(10);
197
198    static {
199        RTL_ATTRS.put("?android:attr/paddingLeft", "paddingStart");
200        RTL_ATTRS.put("?android:attr/paddingRight", "paddingEnd");
201        RTL_ATTRS.put("?android:attr/layout_marginLeft", "layout_marginStart");
202        RTL_ATTRS.put("?android:attr/layout_marginRight", "layout_marginEnd");
203        RTL_ATTRS.put("?android:attr/layout_toLeftOf", "layout_toStartOf");
204        RTL_ATTRS.put("?android:attr/layout_toRightOf", "layout_toEndOf");
205        RTL_ATTRS.put("?android:attr/layout_alignParentLeft", "layout_alignParentStart");
206        RTL_ATTRS.put("?android:attr/layout_alignParentRight", "layout_alignParentEnd");
207        RTL_ATTRS.put("?android:attr/drawableLeft", "drawableStart");
208        RTL_ATTRS.put("?android:attr/drawableRight", "drawableEnd");
209    }
210
211    /**
212     * @param projectKey An Object identifying the project. This is used for the cache mechanism.
213     * @param metrics the {@link DisplayMetrics}.
214     * @param renderResources the configured resources (both framework and projects) for this
215     * render.
216     * @param config the Configuration object for this render.
217     * @param targetSdkVersion the targetSdkVersion of the application.
218     */
219    public BridgeContext(Object projectKey, @NonNull DisplayMetrics metrics,
220            @NonNull RenderResources renderResources,
221            @NonNull AssetRepository assets,
222            @NonNull LayoutlibCallback layoutlibCallback,
223            @NonNull Configuration config,
224            int targetSdkVersion,
225            boolean hasRtlSupport) {
226        mProjectKey = projectKey;
227        mMetrics = metrics;
228        mLayoutlibCallback = layoutlibCallback;
229
230        mRenderResources = renderResources;
231        mConfig = config;
232        AssetManager systemAssetManager = AssetManager.getSystem();
233        if (systemAssetManager instanceof BridgeAssetManager) {
234            mAssets = (BridgeAssetManager) systemAssetManager;
235        } else {
236            throw new AssertionError("Creating BridgeContext without initializing Bridge");
237        }
238        mAssets.setAssetRepository(assets);
239
240        mApplicationInfo = new ApplicationInfo();
241        mApplicationInfo.targetSdkVersion = targetSdkVersion;
242        if (hasRtlSupport) {
243            mApplicationInfo.flags = mApplicationInfo.flags | ApplicationInfo.FLAG_SUPPORTS_RTL;
244        }
245
246        mWindowManager = new WindowManagerImpl(mMetrics);
247        mDisplayManager = new DisplayManager(this);
248    }
249
250    /**
251     * Initializes the {@link Resources} singleton to be linked to this {@link Context}, its
252     * {@link DisplayMetrics}, {@link Configuration}, and {@link LayoutlibCallback}.
253     *
254     * @see #disposeResources()
255     */
256    public void initResources() {
257        AssetManager assetManager = AssetManager.getSystem();
258
259        mSystemResources = Resources_Delegate.initSystem(
260                this,
261                assetManager,
262                mMetrics,
263                mConfig,
264                mLayoutlibCallback);
265        mTheme = mSystemResources.newTheme();
266    }
267
268    /**
269     * Disposes the {@link Resources} singleton.
270     */
271    public void disposeResources() {
272        Resources_Delegate.disposeSystem();
273    }
274
275    public void setBridgeInflater(BridgeInflater inflater) {
276        mBridgeInflater = inflater;
277    }
278
279    public void addViewKey(View view, Object viewKey) {
280        mViewKeyMap.put(view, viewKey);
281    }
282
283    public Object getViewKey(View view) {
284        return mViewKeyMap.get(view);
285    }
286
287    public void addCookie(Object o, Object cookie) {
288        mViewKeyHelpMap.put(o, cookie);
289    }
290
291    public Object getCookie(Object o) {
292        return mViewKeyHelpMap.get(o);
293    }
294
295    public Object getProjectKey() {
296        return mProjectKey;
297    }
298
299    public DisplayMetrics getMetrics() {
300        return mMetrics;
301    }
302
303    public LayoutlibCallback getLayoutlibCallback() {
304        return mLayoutlibCallback;
305    }
306
307    public RenderResources getRenderResources() {
308        return mRenderResources;
309    }
310
311    public Map<Object, PropertiesMap> getDefaultProperties() {
312        return mDefaultPropMaps;
313    }
314
315    public Configuration getConfiguration() {
316        return mConfig;
317    }
318
319    /**
320     * Adds a parser to the stack.
321     * @param parser the parser to add.
322     */
323    public void pushParser(BridgeXmlBlockParser parser) {
324        if (ParserFactory.LOG_PARSER) {
325            System.out.println("PUSH " + parser.getParser().toString());
326        }
327        mParserStack.push(parser);
328    }
329
330    /**
331     * Removes the parser at the top of the stack
332     */
333    public void popParser() {
334        BridgeXmlBlockParser parser = mParserStack.pop();
335        if (ParserFactory.LOG_PARSER) {
336            System.out.println("POPD " + parser.getParser().toString());
337        }
338    }
339
340    /**
341     * Returns the current parser at the top the of the stack.
342     * @return a parser or null.
343     */
344    private BridgeXmlBlockParser getCurrentParser() {
345        return mParserStack.peek();
346    }
347
348    /**
349     * Returns the previous parser.
350     * @return a parser or null if there isn't any previous parser
351     */
352    public BridgeXmlBlockParser getPreviousParser() {
353        if (mParserStack.size() < 2) {
354            return null;
355        }
356        return mParserStack.get(mParserStack.size() - 2);
357    }
358
359    public boolean resolveThemeAttribute(int resId, TypedValue outValue, boolean resolveRefs) {
360        Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(resId);
361        boolean isFrameworkRes = true;
362        if (resourceInfo == null) {
363            resourceInfo = mLayoutlibCallback.resolveResourceId(resId);
364            isFrameworkRes = false;
365        }
366
367        if (resourceInfo == null) {
368            return false;
369        }
370
371        ResourceValue value = mRenderResources.findItemInTheme(resourceInfo.getSecond(),
372                isFrameworkRes);
373        if (resolveRefs) {
374            value = mRenderResources.resolveResValue(value);
375        }
376
377        if (value == null) {
378            // unable to find the attribute.
379            return false;
380        }
381
382        // check if this is a style resource
383        if (value instanceof StyleResourceValue) {
384            // get the id that will represent this style.
385            outValue.resourceId = getDynamicIdByStyle((StyleResourceValue) value);
386            return true;
387        }
388
389        String stringValue = value.getValue();
390        if (!stringValue.isEmpty()) {
391            if (stringValue.charAt(0) == '#') {
392                outValue.type = TypedValue.TYPE_INT_COLOR_ARGB8;
393                outValue.data = Color.parseColor(value.getValue());
394            }
395            else if (stringValue.charAt(0) == '@') {
396                outValue.type = TypedValue.TYPE_REFERENCE;
397            }
398
399        }
400
401        int a;
402        // if this is a framework value.
403        if (value.isFramework()) {
404            // look for idName in the android R classes.
405            // use 0 a default res value as it's not a valid id value.
406            a = getFrameworkResourceValue(value.getResourceType(), value.getName(), 0 /*defValue*/);
407        } else {
408            // look for idName in the project R class.
409            // use 0 a default res value as it's not a valid id value.
410            a = getProjectResourceValue(value.getResourceType(), value.getName(), 0 /*defValue*/);
411        }
412
413        if (a != 0) {
414            outValue.resourceId = a;
415            return true;
416        }
417
418        // If the value is not a valid reference, fallback to pass the value as a string.
419        outValue.string = stringValue;
420        return true;
421    }
422
423
424    public ResourceReference resolveId(int id) {
425        // first get the String related to this id in the framework
426        Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(id);
427
428        if (resourceInfo != null) {
429            return new ResourceReference(resourceInfo.getSecond(), true);
430        }
431
432        // didn't find a match in the framework? look in the project.
433        if (mLayoutlibCallback != null) {
434            resourceInfo = mLayoutlibCallback.resolveResourceId(id);
435
436            if (resourceInfo != null) {
437                return new ResourceReference(resourceInfo.getSecond(), false);
438            }
439        }
440
441        // The base value for R.style is 0x01030000 and the custom style is 0x02030000.
442        // So, if the second byte is 03, it's probably a style.
443        if ((id >> 16 & 0xFF) == 0x03) {
444            return getStyleByDynamicId(id);
445        }
446        return null;
447    }
448
449    public Pair<View, Boolean> inflateView(ResourceReference resource, ViewGroup parent,
450            @SuppressWarnings("SameParameterValue") boolean attachToRoot,
451            boolean skipCallbackParser) {
452        boolean isPlatformLayout = resource.isFramework();
453
454        if (!isPlatformLayout && !skipCallbackParser) {
455            // check if the project callback can provide us with a custom parser.
456            ILayoutPullParser parser = getParser(resource);
457
458            if (parser != null) {
459                BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(parser,
460                        this, resource.isFramework());
461                try {
462                    pushParser(blockParser);
463                    return Pair.of(
464                            mBridgeInflater.inflate(blockParser, parent, attachToRoot),
465                            Boolean.TRUE);
466                } finally {
467                    popParser();
468                }
469            }
470        }
471
472        ResourceValue resValue;
473        if (resource instanceof ResourceValue) {
474            resValue = (ResourceValue) resource;
475        } else {
476            if (isPlatformLayout) {
477                resValue = mRenderResources.getFrameworkResource(ResourceType.LAYOUT,
478                        resource.getName());
479            } else {
480                resValue = mRenderResources.getProjectResource(ResourceType.LAYOUT,
481                        resource.getName());
482            }
483        }
484
485        if (resValue != null) {
486
487            File xml = new File(resValue.getValue());
488            if (xml.isFile()) {
489                // we need to create a pull parser around the layout XML file, and then
490                // give that to our XmlBlockParser
491                try {
492                    XmlPullParser parser = ParserFactory.create(xml, true);
493
494                    // set the resource ref to have correct view cookies
495                    mBridgeInflater.setResourceReference(resource);
496
497                    BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(parser,
498                            this, resource.isFramework());
499                    try {
500                        pushParser(blockParser);
501                        return Pair.of(
502                                mBridgeInflater.inflate(blockParser, parent, attachToRoot),
503                                Boolean.FALSE);
504                    } finally {
505                        popParser();
506                    }
507                } catch (XmlPullParserException e) {
508                    Bridge.getLog().error(LayoutLog.TAG_BROKEN,
509                            "Failed to configure parser for " + xml, e, null /*data*/);
510                    // we'll return null below.
511                } catch (FileNotFoundException e) {
512                    // this shouldn't happen since we check above.
513                } finally {
514                    mBridgeInflater.setResourceReference(null);
515                }
516            } else {
517                Bridge.getLog().error(LayoutLog.TAG_BROKEN,
518                        String.format("File %s is missing!", xml), null);
519            }
520        } else {
521            Bridge.getLog().error(LayoutLog.TAG_BROKEN,
522                    String.format("Layout %s%s does not exist.", isPlatformLayout ? "android:" : "",
523                            resource.getName()), null);
524        }
525
526        return Pair.of(null, Boolean.FALSE);
527    }
528
529    /**
530     * Returns whether the current selected theme is based on AppCompat
531     */
532    public boolean isAppCompatTheme() {
533        // If a cached value exists, return it.
534        if (mIsThemeAppCompat != null) {
535            return mIsThemeAppCompat;
536        }
537        // Ideally, we should check if the corresponding activity extends
538        // android.support.v7.app.ActionBarActivity, and not care about the theme name at all.
539        StyleResourceValue defaultTheme = mRenderResources.getDefaultTheme();
540        // We can't simply check for parent using resources.themeIsParentOf() since the
541        // inheritance structure isn't really what one would expect. The first common parent
542        // between Theme.AppCompat.Light and Theme.AppCompat is Theme.Material (for v21).
543        boolean isThemeAppCompat = false;
544        for (int i = 0; i < 50; i++) {
545            if (defaultTheme == null) {
546                break;
547            }
548            // for loop ensures that we don't run into cyclic theme inheritance.
549            if (defaultTheme.getName().startsWith(PREFIX_THEME_APPCOMPAT)) {
550                isThemeAppCompat = true;
551                break;
552            }
553            defaultTheme = mRenderResources.getParent(defaultTheme);
554        }
555        mIsThemeAppCompat = isThemeAppCompat;
556        return isThemeAppCompat;
557    }
558
559    @SuppressWarnings("deprecation")
560    private ILayoutPullParser getParser(ResourceReference resource) {
561        ILayoutPullParser parser;
562        if (resource instanceof ResourceValue) {
563            parser = mLayoutlibCallback.getParser((ResourceValue) resource);
564        } else {
565            parser = mLayoutlibCallback.getParser(resource.getName());
566        }
567        return parser;
568    }
569
570    // ------------ Context methods
571
572    @Override
573    public Resources getResources() {
574        return mSystemResources;
575    }
576
577    @Override
578    public Theme getTheme() {
579        return mTheme;
580    }
581
582    @Override
583    public ClassLoader getClassLoader() {
584        // The documentation for this method states that it should return a class loader one can
585        // use to retrieve classes in this package. However, when called by LayoutInflater, we do
586        // not want the class loader to return app's custom views.
587        // This is so that the IDE can instantiate the custom views and also generate proper error
588        // messages in case of failure. This also enables the IDE to fallback to MockView in case
589        // there's an exception thrown when trying to inflate the custom view.
590        // To work around this issue, LayoutInflater is modified via LayoutLib Create tool to
591        // replace invocations of this method to a new method: getFrameworkClassLoader(). Also,
592        // the method is injected into Context. The implementation of getFrameworkClassLoader() is:
593        // "return getClass().getClassLoader();". This means that when LayoutInflater asks for
594        // the context ClassLoader, it gets only LayoutLib's ClassLoader which doesn't have
595        // access to the apps's custom views.
596        // This method can now return the right ClassLoader, which CustomViews can use to do the
597        // right thing.
598        if (mClassLoader == null) {
599            mClassLoader = new ClassLoader(getClass().getClassLoader()) {
600                @Override
601                protected Class<?> findClass(String name) throws ClassNotFoundException {
602                    for (String prefix : BridgeInflater.getClassPrefixList()) {
603                        if (name.startsWith(prefix)) {
604                            // These are framework classes and should not be loaded from the app.
605                            throw new ClassNotFoundException(name + " not found");
606                        }
607                    }
608                    return BridgeContext.this.mLayoutlibCallback.findClass(name);
609                }
610            };
611        }
612        return mClassLoader;
613    }
614
615    @Override
616    public Object getSystemService(String service) {
617        switch (service) {
618            case LAYOUT_INFLATER_SERVICE:
619                return mBridgeInflater;
620
621            case TEXT_SERVICES_MANAGER_SERVICE:
622                // we need to return a valid service to avoid NPE
623                return TextServicesManager.getInstance();
624
625            case WINDOW_SERVICE:
626                return mWindowManager;
627
628            case POWER_SERVICE:
629                return new PowerManager(this, new BridgePowerManager(), new Handler());
630
631            case DISPLAY_SERVICE:
632                return mDisplayManager;
633
634            case ACCESSIBILITY_SERVICE:
635                return AccessibilityManager.getInstance(this);
636
637            case INPUT_METHOD_SERVICE:  // needed by SearchView
638            case AUTOFILL_MANAGER_SERVICE:
639            case AUDIO_SERVICE:
640            case TEXT_CLASSIFICATION_SERVICE:
641                return null;
642            default:
643                assert false : "Unsupported Service: " + service;
644        }
645
646        return null;
647    }
648
649    @Override
650    public String getSystemServiceName(Class<?> serviceClass) {
651        return SystemServiceRegistry_Accessor.getSystemServiceName(serviceClass);
652    }
653
654
655    /**
656     * Same as Context#obtainStyledAttributes. We do not override the base method to give the
657     * original Context the chance to override the theme when needed.
658     */
659    @Nullable
660    public final BridgeTypedArray internalObtainStyledAttributes(int resId, int[] attrs)
661            throws Resources.NotFoundException {
662        StyleResourceValue style = null;
663        // get the StyleResourceValue based on the resId;
664        if (resId != 0) {
665            style = getStyleByDynamicId(resId);
666
667            if (style == null) {
668                // In some cases, style may not be a dynamic id, so we do a full search.
669                ResourceReference ref = resolveId(resId);
670                if (ref != null) {
671                    style = mRenderResources.getStyle(ref.getName(), ref.isFramework());
672                }
673            }
674
675            if (style == null) {
676                Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE,
677                        "Failed to find style with " + resId, null);
678                return null;
679            }
680        }
681
682        if (mTypedArrayCache == null) {
683            mTypedArrayCache = new TypedArrayCache();
684        }
685
686        List<StyleResourceValue> currentThemes = mRenderResources.getAllThemes();
687
688        Pair<BridgeTypedArray, PropertiesMap> typeArrayAndPropertiesPair =
689                mTypedArrayCache.get(attrs, currentThemes, resId);
690
691        if (typeArrayAndPropertiesPair == null) {
692            typeArrayAndPropertiesPair = createStyleBasedTypedArray(style, attrs);
693            mTypedArrayCache.put(attrs, currentThemes, resId, typeArrayAndPropertiesPair);
694        }
695        // Add value to defaultPropsMap if needed
696        if (typeArrayAndPropertiesPair.getSecond() != null) {
697            BridgeXmlBlockParser parser = getCurrentParser();
698            Object key = parser != null ? parser.getViewCookie() : null;
699            if (key != null) {
700                PropertiesMap defaultPropMap = mDefaultPropMaps.get(key);
701                if (defaultPropMap == null) {
702                    defaultPropMap = typeArrayAndPropertiesPair.getSecond();
703                    mDefaultPropMaps.put(key, defaultPropMap);
704                } else {
705                    defaultPropMap.putAll(typeArrayAndPropertiesPair.getSecond());
706                }
707            }
708        }
709        return typeArrayAndPropertiesPair.getFirst();
710    }
711
712    /**
713     * Same as Context#obtainStyledAttributes. We do not override the base method to give the
714     * original Context the chance to override the theme when needed.
715     */
716    @Nullable
717    public BridgeTypedArray internalObtainStyledAttributes(@Nullable AttributeSet set, int[] attrs,
718            int defStyleAttr, int defStyleRes) {
719
720        PropertiesMap defaultPropMap = null;
721        boolean isPlatformFile = true;
722
723        // TODO(namespaces): We need to figure out how to keep track of the namespace of the current
724        // layout file.
725        ResourceNamespace currentFileNamespace = ResourceNamespace.TODO;
726
727        // TODO(namespaces): get this through the callback, only in non-namespaced projects.
728        ResourceNamespace.Resolver resolver = LEGACY_NAMESPACE_RESOLVER;
729
730        // Hint: for XmlPullParser, attach source //DEVICE_SRC/dalvik/libcore/xml/src/java
731        if (set instanceof BridgeXmlBlockParser) {
732            BridgeXmlBlockParser parser;
733            parser = (BridgeXmlBlockParser)set;
734
735            isPlatformFile = parser.isPlatformFile();
736
737            Object key = parser.getViewCookie();
738            if (key != null) {
739                defaultPropMap = mDefaultPropMaps.computeIfAbsent(key, k -> new PropertiesMap());
740            }
741
742            resolver = parser::getNamespace;
743            currentFileNamespace = ResourceNamespace.fromBoolean(parser.isPlatformFile());
744        } else if (set instanceof BridgeLayoutParamsMapAttributes) {
745            // this is only for temp layout params generated dynamically, so this is never
746            // platform content.
747            isPlatformFile = false;
748        } else if (set != null) { // null parser is ok
749            // really this should not be happening since its instantiated in Bridge
750            Bridge.getLog().error(LayoutLog.TAG_BROKEN,
751                    "Parser is not a BridgeXmlBlockParser!", null);
752            return null;
753        }
754
755        List<AttributeHolder> attributeList = searchAttrs(attrs);
756
757        BridgeTypedArray ta =
758                Resources_Delegate.newTypeArray(mSystemResources, attrs.length, isPlatformFile);
759
760        // look for a custom style.
761        String customStyle = null;
762        if (set != null) {
763            customStyle = set.getAttributeValue(null, "style");
764        }
765
766        StyleResourceValue customStyleValues = null;
767        if (customStyle != null) {
768            ResourceValue item = mRenderResources.findResValue(customStyle,
769                    isPlatformFile /*forceFrameworkOnly*/);
770
771            // resolve it in case it links to something else
772            item = mRenderResources.resolveResValue(item);
773
774            if (item instanceof StyleResourceValue) {
775                customStyleValues = (StyleResourceValue)item;
776            }
777        }
778
779        // resolve the defStyleAttr value into a IStyleResourceValue
780        StyleResourceValue defStyleValues = null;
781
782        if (defStyleAttr != 0) {
783            // get the name from the int.
784            Pair<String, Boolean> defStyleAttribute = searchAttr(defStyleAttr);
785
786            if (defStyleAttribute == null) {
787                // This should be rare. Happens trying to map R.style.foo to @style/foo fails.
788                // This will happen if the user explicitly used a non existing int value for
789                // defStyleAttr or there's something wrong with the project structure/build.
790                Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE,
791                        "Failed to find the style corresponding to the id " + defStyleAttr, null);
792            } else {
793                String defStyleName = defStyleAttribute.getFirst();
794
795                // look for the style in the current theme, and its parent:
796                ResourceValue item = mRenderResources.findItemInTheme(defStyleName,
797                        defStyleAttribute.getSecond());
798
799                if (item != null) {
800                    // item is a reference to a style entry. Search for it.
801                    item = mRenderResources.findResValue(item.getValue(), item.isFramework());
802                    item = mRenderResources.resolveResValue(item);
803                    if (item instanceof StyleResourceValue) {
804                        defStyleValues = (StyleResourceValue) item;
805                    }
806                    if (defaultPropMap != null) {
807                        if (defStyleAttribute.getSecond()) {
808                            defStyleName = "android:" + defStyleName;
809                        }
810                        defaultPropMap.put("style", new Property(defStyleName, item.getValue()));
811                    }
812                }
813            }
814        }
815
816        if (defStyleValues == null && defStyleRes != 0) {
817            StyleResourceValue item = getStyleByDynamicId(defStyleRes);
818            if (item != null) {
819                defStyleValues = item;
820            } else {
821                boolean isFrameworkRes = true;
822                Pair<ResourceType, String> value = Bridge.resolveResourceId(defStyleRes);
823                if (value == null) {
824                    value = mLayoutlibCallback.resolveResourceId(defStyleRes);
825                    isFrameworkRes = false;
826                }
827
828                if (value != null) {
829                    if ((value.getFirst() == ResourceType.STYLE)) {
830                        // look for the style in all resources:
831                        item = mRenderResources.getStyle(value.getSecond(), isFrameworkRes);
832                        if (item != null) {
833                            if (defaultPropMap != null) {
834                                String name = item.getName();
835                                defaultPropMap.put("style", new Property(name, name));
836                            }
837
838                            defStyleValues = item;
839                        } else {
840                            Bridge.getLog().error(null,
841                                    String.format(
842                                            "Style with id 0x%x (resolved to '%s') does not exist.",
843                                            defStyleRes, value.getSecond()),
844                                    null);
845                        }
846                    } else {
847                        Bridge.getLog().error(null,
848                                String.format(
849                                        "Resource id 0x%x is not of type STYLE (instead %s)",
850                                        defStyleRes, value.getFirst().toString()),
851                                null);
852                    }
853                } else {
854                    Bridge.getLog().error(null,
855                            String.format(
856                                    "Failed to find style with id 0x%x in current theme",
857                                    defStyleRes),
858                            null);
859                }
860            }
861        }
862
863        String appNamespace = mLayoutlibCallback.getNamespace();
864
865        if (attributeList != null) {
866            for (int index = 0 ; index < attributeList.size() ; index++) {
867                AttributeHolder attributeHolder = attributeList.get(index);
868
869                if (attributeHolder == null) {
870                    continue;
871                }
872
873                String attrName = attributeHolder.name;
874                boolean frameworkAttr = attributeHolder.isFramework;
875                String value = null;
876                if (set != null) {
877                    value = set.getAttributeValue(
878                            frameworkAttr ? BridgeConstants.NS_RESOURCES : appNamespace,
879                                    attrName);
880
881                    // if this is an app attribute, and the first get fails, try with the
882                    // new res-auto namespace as well
883                    if (!frameworkAttr && value == null) {
884                        value = set.getAttributeValue(BridgeConstants.NS_APP_RES_AUTO, attrName);
885                    }
886                }
887
888                // Calculate the default value from the Theme in two cases:
889                //   - If defaultPropMap is not null, get the default value to add it to the list
890                //   of default values of properties.
891                //   - If value is null, it means that the attribute is not directly set as an
892                //   attribute in the XML so try to get the default value.
893                ResourceValue defaultValue = null;
894                if (defaultPropMap != null || value == null) {
895                    // look for the value in the custom style first (and its parent if needed)
896                    if (customStyleValues != null) {
897                        defaultValue = mRenderResources.findItemInStyle(customStyleValues, attrName,
898                                frameworkAttr);
899                    }
900
901                    // then look for the value in the default Style (and its parent if needed)
902                    if (defaultValue == null && defStyleValues != null) {
903                        defaultValue = mRenderResources.findItemInStyle(defStyleValues, attrName,
904                                frameworkAttr);
905                    }
906
907                    // if the item is not present in the defStyle, we look in the main theme (and
908                    // its parent themes)
909                    if (defaultValue == null) {
910                        defaultValue = mRenderResources.findItemInTheme(attrName, frameworkAttr);
911                    }
912
913                    // if we found a value, we make sure this doesn't reference another value.
914                    // So we resolve it.
915                    if (defaultValue != null) {
916                        String preResolve = defaultValue.getValue();
917                        defaultValue = mRenderResources.resolveResValue(defaultValue);
918
919                        if (defaultPropMap != null) {
920                            defaultPropMap.put(
921                                    frameworkAttr ? SdkConstants.PREFIX_ANDROID + attrName :
922                                            attrName, new Property(preResolve, defaultValue.getValue()));
923                        }
924                    }
925                }
926                // Done calculating the defaultValue
927
928                // if there's no direct value for this attribute in the XML, we look for default
929                // values in the widget defStyle, and then in the theme.
930                if (value == null) {
931                    if (frameworkAttr) {
932                        // For some framework values, layoutlib patches the actual value in the
933                        // theme when it helps to improve the final preview. In most cases
934                        // we just disable animations.
935                        ResourceValue patchedValue = FRAMEWORK_PATCHED_VALUES.get(attrName);
936                        if (patchedValue != null) {
937                            defaultValue = patchedValue;
938                        }
939                    }
940
941                    // if we found a value, we make sure this doesn't reference another value.
942                    // So we resolve it.
943                    if (defaultValue != null) {
944                        // If the value is a reference to another theme attribute that doesn't
945                        // exist, we should log a warning and omit it.
946                        String val = defaultValue.getValue();
947                        if (val != null && val.startsWith(SdkConstants.PREFIX_THEME_REF)) {
948                            // Because we always use the latest framework code, some resources might
949                            // fail to resolve when using old themes (they haven't been backported).
950                            // Since this is an artifact caused by us using always the latest
951                            // code, we check for some of those values and replace them here.
952                            defaultValue = FRAMEWORK_REPLACE_VALUES.get(attrName);
953
954                            if (defaultValue == null &&
955                                    (getApplicationInfo().targetSdkVersion < JELLY_BEAN_MR1 ||
956                                    !attrName.equals(RTL_ATTRS.get(val)))) {
957                                // Only log a warning if the referenced value isn't one of the RTL
958                                // attributes, or the app targets old API.
959                                Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE_THEME_ATTR,
960                                        String.format("Failed to find '%s' in current theme.", val),
961                                        val);
962                            }
963                        }
964                    }
965
966                    ta.bridgeSetValue(index, attrName, frameworkAttr, attributeHolder.resourceId,
967                            defaultValue);
968                } else {
969                    // There is a value in the XML, but we need to resolve it in case it's
970                    // referencing another resource or a theme value.
971                    ResourceValue dummy =
972                            new ResourceValue(
973                                    currentFileNamespace,
974                                    null,
975                                    attrName,
976                                    value);
977                    dummy.setNamespaceLookup(resolver);
978
979                    ta.bridgeSetValue(
980                            index, attrName, frameworkAttr, attributeHolder.resourceId,
981                            mRenderResources.resolveResValue(dummy));
982                }
983            }
984        }
985
986        ta.sealArray();
987
988        return ta;
989    }
990
991    @Override
992    public Looper getMainLooper() {
993        return Looper.myLooper();
994    }
995
996
997    @Override
998    public String getPackageName() {
999        if (mApplicationInfo.packageName == null) {
1000            mApplicationInfo.packageName = mLayoutlibCallback.getFlag(FLAG_KEY_APPLICATION_PACKAGE);
1001        }
1002        return mApplicationInfo.packageName;
1003    }
1004
1005    @Override
1006    public PackageManager getPackageManager() {
1007        if (mPackageManager == null) {
1008            mPackageManager = new BridgePackageManager();
1009        }
1010        return mPackageManager;
1011    }
1012
1013    // ------------- private new methods
1014
1015    /**
1016     * Creates a {@link BridgeTypedArray} by filling the values defined by the int[] with the
1017     * values found in the given style. If no style is specified, the default theme, along with the
1018     * styles applied to it are used.
1019     *
1020     * @see #obtainStyledAttributes(int, int[])
1021     */
1022    private Pair<BridgeTypedArray, PropertiesMap> createStyleBasedTypedArray(
1023            @Nullable StyleResourceValue style, int[] attrs) throws Resources.NotFoundException {
1024        List<AttributeHolder> attributes = searchAttrs(attrs);
1025
1026        BridgeTypedArray ta =
1027                Resources_Delegate.newTypeArray(mSystemResources, attrs.length, false);
1028
1029        PropertiesMap defaultPropMap = new PropertiesMap();
1030        // for each attribute, get its name so that we can search it in the style
1031        for (int i = 0; i < attrs.length; i++) {
1032            AttributeHolder attrHolder = attributes.get(i);
1033
1034            if (attrHolder != null) {
1035                // look for the value in the given style
1036                ResourceValue resValue;
1037                String attrName = attrHolder.name;
1038                boolean frameworkAttr = attrHolder.isFramework;
1039                if (style != null) {
1040                    resValue = mRenderResources.findItemInStyle(style, attrName, frameworkAttr);
1041                } else {
1042                    resValue = mRenderResources.findItemInTheme(attrName, frameworkAttr);
1043                }
1044
1045                if (resValue != null) {
1046                    // Add it to defaultPropMap before resolving
1047                    String preResolve = resValue.getValue();
1048                    // resolve it to make sure there are no references left.
1049                    resValue = mRenderResources.resolveResValue(resValue);
1050                    ta.bridgeSetValue(i, attrName, frameworkAttr, attrHolder.resourceId,
1051                            resValue);
1052                    defaultPropMap.put(
1053                            frameworkAttr ? SdkConstants.ANDROID_PREFIX + attrName : attrName,
1054                            new Property(preResolve, resValue.getValue()));
1055                }
1056            }
1057        }
1058
1059        ta.sealArray();
1060
1061        return Pair.of(ta, defaultPropMap);
1062    }
1063
1064    /**
1065     * The input int[] attributeIds is a list of attributes. The returns a list of information about
1066     * each attributes. The information is (name, isFramework)
1067     * <p/>
1068     *
1069     * @param attributeIds An attribute array reference given to obtainStyledAttributes.
1070     * @return List of attribute information.
1071     */
1072    private List<AttributeHolder> searchAttrs(int[] attributeIds) {
1073        List<AttributeHolder> results = new ArrayList<>(attributeIds.length);
1074
1075        // for each attribute, get its name so that we can search it in the style
1076        for (int id : attributeIds) {
1077            Pair<ResourceType, String> resolvedResource = Bridge.resolveResourceId(id);
1078            boolean isFramework = false;
1079            if (resolvedResource != null) {
1080                isFramework = true;
1081            } else {
1082                resolvedResource = mLayoutlibCallback.resolveResourceId(id);
1083            }
1084
1085            if (resolvedResource != null) {
1086                results.add(new AttributeHolder(id, resolvedResource.getSecond(), isFramework));
1087            } else {
1088                results.add(null);
1089            }
1090        }
1091
1092        return results;
1093    }
1094
1095    /**
1096     * Searches for the attribute referenced by its internal id.
1097     *
1098     * @param attr An attribute reference given to obtainStyledAttributes such as defStyle.
1099     * @return A (name, isFramework) pair describing the attribute if found. Returns null
1100     *         if nothing is found.
1101     */
1102    private Pair<String, Boolean> searchAttr(int attr) {
1103        Pair<ResourceType, String> info = Bridge.resolveResourceId(attr);
1104        if (info != null) {
1105            return Pair.of(info.getSecond(), Boolean.TRUE);
1106        }
1107
1108        info = mLayoutlibCallback.resolveResourceId(attr);
1109        if (info != null) {
1110            return Pair.of(info.getSecond(), Boolean.FALSE);
1111        }
1112
1113        return null;
1114    }
1115
1116    public int getDynamicIdByStyle(StyleResourceValue resValue) {
1117        if (mDynamicIdToStyleMap == null) {
1118            // create the maps.
1119            mDynamicIdToStyleMap = new HashMap<>();
1120            mStyleToDynamicIdMap = new HashMap<>();
1121        }
1122
1123        // look for an existing id
1124        Integer id = mStyleToDynamicIdMap.get(resValue);
1125
1126        if (id == null) {
1127            // generate a new id
1128            id = ++mDynamicIdGenerator;
1129
1130            // and add it to the maps.
1131            mDynamicIdToStyleMap.put(id, resValue);
1132            mStyleToDynamicIdMap.put(resValue, id);
1133        }
1134
1135        return id;
1136    }
1137
1138    private StyleResourceValue getStyleByDynamicId(int i) {
1139        if (mDynamicIdToStyleMap != null) {
1140            return mDynamicIdToStyleMap.get(i);
1141        }
1142
1143        return null;
1144    }
1145
1146    public int getFrameworkResourceValue(ResourceType resType, String resName, int defValue) {
1147        if (getRenderResources().getFrameworkResource(resType, resName) != null) {
1148            // Bridge.getResourceId creates a new resource id if an existing one isn't found. So,
1149            // we check for the existence of the resource before calling it.
1150            return Bridge.getResourceId(resType, resName);
1151        }
1152
1153        return defValue;
1154    }
1155
1156    public int getProjectResourceValue(ResourceType resType, String resName, int defValue) {
1157        // getResourceId creates a new resource id if an existing resource id isn't found. So, we
1158        // check for the existence of the resource before calling it.
1159        if (getRenderResources().getProjectResource(resType, resName) != null) {
1160            if (mLayoutlibCallback != null) {
1161                Integer value = mLayoutlibCallback.getResourceId(resType, resName);
1162                if (value != null) {
1163                    return value;
1164                }
1165            }
1166        }
1167
1168        return defValue;
1169    }
1170
1171    public static Context getBaseContext(Context context) {
1172        while (context instanceof ContextWrapper) {
1173            context = ((ContextWrapper) context).getBaseContext();
1174        }
1175        return context;
1176    }
1177
1178    public IBinder getBinder() {
1179        if (mBinder == null) {
1180            // create a dummy binder. We only need it be not null.
1181            mBinder = new IBinder() {
1182                @Override
1183                public String getInterfaceDescriptor() throws RemoteException {
1184                    return null;
1185                }
1186
1187                @Override
1188                public boolean pingBinder() {
1189                    return false;
1190                }
1191
1192                @Override
1193                public boolean isBinderAlive() {
1194                    return false;
1195                }
1196
1197                @Override
1198                public IInterface queryLocalInterface(String descriptor) {
1199                    return null;
1200                }
1201
1202                @Override
1203                public void dump(FileDescriptor fd, String[] args) throws RemoteException {
1204
1205                }
1206
1207                @Override
1208                public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
1209
1210                }
1211
1212                @Override
1213                public boolean transact(int code, Parcel data, Parcel reply, int flags)
1214                        throws RemoteException {
1215                    return false;
1216                }
1217
1218                @Override
1219                public void linkToDeath(DeathRecipient recipient, int flags)
1220                        throws RemoteException {
1221
1222                }
1223
1224                @Override
1225                public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
1226                    return false;
1227                }
1228
1229                @Override
1230                public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
1231                  String[] args, ShellCallback shellCallback, ResultReceiver resultReceiver) {
1232                }
1233            };
1234        }
1235        return mBinder;
1236    }
1237
1238    //------------ NOT OVERRIDEN --------------------
1239
1240    @Override
1241    public boolean bindService(Intent arg0, ServiceConnection arg1, int arg2) {
1242        // pass
1243        return false;
1244    }
1245
1246    @Override
1247    public int checkCallingOrSelfPermission(String arg0) {
1248        // pass
1249        return 0;
1250    }
1251
1252    @Override
1253    public int checkCallingOrSelfUriPermission(Uri arg0, int arg1) {
1254        // pass
1255        return 0;
1256    }
1257
1258    @Override
1259    public int checkCallingPermission(String arg0) {
1260        // pass
1261        return 0;
1262    }
1263
1264    @Override
1265    public int checkCallingUriPermission(Uri arg0, int arg1) {
1266        // pass
1267        return 0;
1268    }
1269
1270    @Override
1271    public int checkPermission(String arg0, int arg1, int arg2) {
1272        // pass
1273        return 0;
1274    }
1275
1276    @Override
1277    public int checkSelfPermission(String arg0) {
1278        // pass
1279        return 0;
1280    }
1281
1282    @Override
1283    public int checkPermission(String arg0, int arg1, int arg2, IBinder arg3) {
1284        // pass
1285        return 0;
1286    }
1287
1288    @Override
1289    public int checkUriPermission(Uri arg0, int arg1, int arg2, int arg3) {
1290        // pass
1291        return 0;
1292    }
1293
1294    @Override
1295    public int checkUriPermission(Uri arg0, int arg1, int arg2, int arg3, IBinder arg4) {
1296        // pass
1297        return 0;
1298    }
1299
1300    @Override
1301    public int checkUriPermission(Uri arg0, String arg1, String arg2, int arg3,
1302            int arg4, int arg5) {
1303        // pass
1304        return 0;
1305    }
1306
1307    @Override
1308    public void clearWallpaper() {
1309        // pass
1310
1311    }
1312
1313    @Override
1314    public Context createPackageContext(String arg0, int arg1) {
1315        // pass
1316        return null;
1317    }
1318
1319    @Override
1320    public Context createPackageContextAsUser(String arg0, int arg1, UserHandle user) {
1321        // pass
1322        return null;
1323    }
1324
1325    @Override
1326    public Context createConfigurationContext(Configuration overrideConfiguration) {
1327        // pass
1328        return null;
1329    }
1330
1331    @Override
1332    public Context createDisplayContext(Display display) {
1333        // pass
1334        return null;
1335    }
1336
1337    @Override
1338    public Context createContextForSplit(String splitName) {
1339        // pass
1340        return null;
1341    }
1342
1343    @Override
1344    public String[] databaseList() {
1345        // pass
1346        return null;
1347    }
1348
1349    @Override
1350    public Context createApplicationContext(ApplicationInfo application, int flags)
1351            throws PackageManager.NameNotFoundException {
1352        return null;
1353    }
1354
1355    @Override
1356    public boolean moveDatabaseFrom(Context sourceContext, String name) {
1357        // pass
1358        return false;
1359    }
1360
1361    @Override
1362    public boolean deleteDatabase(String arg0) {
1363        // pass
1364        return false;
1365    }
1366
1367    @Override
1368    public boolean deleteFile(String arg0) {
1369        // pass
1370        return false;
1371    }
1372
1373    @Override
1374    public void enforceCallingOrSelfPermission(String arg0, String arg1) {
1375        // pass
1376
1377    }
1378
1379    @Override
1380    public void enforceCallingOrSelfUriPermission(Uri arg0, int arg1,
1381            String arg2) {
1382        // pass
1383
1384    }
1385
1386    @Override
1387    public void enforceCallingPermission(String arg0, String arg1) {
1388        // pass
1389
1390    }
1391
1392    @Override
1393    public void enforceCallingUriPermission(Uri arg0, int arg1, String arg2) {
1394        // pass
1395
1396    }
1397
1398    @Override
1399    public void enforcePermission(String arg0, int arg1, int arg2, String arg3) {
1400        // pass
1401
1402    }
1403
1404    @Override
1405    public void enforceUriPermission(Uri arg0, int arg1, int arg2, int arg3,
1406            String arg4) {
1407        // pass
1408
1409    }
1410
1411    @Override
1412    public void enforceUriPermission(Uri arg0, String arg1, String arg2,
1413            int arg3, int arg4, int arg5, String arg6) {
1414        // pass
1415
1416    }
1417
1418    @Override
1419    public String[] fileList() {
1420        // pass
1421        return null;
1422    }
1423
1424    @Override
1425    public BridgeAssetManager getAssets() {
1426        return mAssets;
1427    }
1428
1429    @Override
1430    public File getCacheDir() {
1431        // pass
1432        return null;
1433    }
1434
1435    @Override
1436    public File getCodeCacheDir() {
1437        // pass
1438        return null;
1439    }
1440
1441    @Override
1442    public File getExternalCacheDir() {
1443        // pass
1444        return null;
1445    }
1446
1447    @Override
1448    public File getPreloadsFileCache() {
1449        // pass
1450        return null;
1451    }
1452
1453    @Override
1454    public ContentResolver getContentResolver() {
1455        if (mContentResolver == null) {
1456            mContentResolver = new BridgeContentResolver(this);
1457        }
1458        return mContentResolver;
1459    }
1460
1461    @Override
1462    public File getDatabasePath(String arg0) {
1463        // pass
1464        return null;
1465    }
1466
1467    @Override
1468    public File getDir(String arg0, int arg1) {
1469        // pass
1470        return null;
1471    }
1472
1473    @Override
1474    public File getFileStreamPath(String arg0) {
1475        // pass
1476        return null;
1477    }
1478
1479    @Override
1480    public File getSharedPreferencesPath(String name) {
1481        // pass
1482        return null;
1483    }
1484
1485    @Override
1486    public File getDataDir() {
1487        // pass
1488        return null;
1489    }
1490
1491    @Override
1492    public File getFilesDir() {
1493        // pass
1494        return null;
1495    }
1496
1497    @Override
1498    public File getNoBackupFilesDir() {
1499        // pass
1500        return null;
1501    }
1502
1503    @Override
1504    public File getExternalFilesDir(String type) {
1505        // pass
1506        return null;
1507    }
1508
1509    @Override
1510    public String getPackageCodePath() {
1511        // pass
1512        return null;
1513    }
1514
1515    @Override
1516    public String getBasePackageName() {
1517        // pass
1518        return null;
1519    }
1520
1521    @Override
1522    public String getOpPackageName() {
1523        // pass
1524        return null;
1525    }
1526
1527    @Override
1528    public ApplicationInfo getApplicationInfo() {
1529        return mApplicationInfo;
1530    }
1531
1532    @Override
1533    public String getPackageResourcePath() {
1534        // pass
1535        return null;
1536    }
1537
1538    @Override
1539    public SharedPreferences getSharedPreferences(String arg0, int arg1) {
1540        if (mSharedPreferences == null) {
1541            mSharedPreferences = new BridgeSharedPreferences();
1542        }
1543        return mSharedPreferences;
1544    }
1545
1546    @Override
1547    public SharedPreferences getSharedPreferences(File arg0, int arg1) {
1548        if (mSharedPreferences == null) {
1549            mSharedPreferences = new BridgeSharedPreferences();
1550        }
1551        return mSharedPreferences;
1552    }
1553
1554    @Override
1555    public void reloadSharedPreferences() {
1556        // intentional noop
1557    }
1558
1559    @Override
1560    public boolean moveSharedPreferencesFrom(Context sourceContext, String name) {
1561        // pass
1562        return false;
1563    }
1564
1565    @Override
1566    public boolean deleteSharedPreferences(String name) {
1567        // pass
1568        return false;
1569    }
1570
1571    @Override
1572    public Drawable getWallpaper() {
1573        // pass
1574        return null;
1575    }
1576
1577    @Override
1578    public int getWallpaperDesiredMinimumWidth() {
1579        return -1;
1580    }
1581
1582    @Override
1583    public int getWallpaperDesiredMinimumHeight() {
1584        return -1;
1585    }
1586
1587    @Override
1588    public void grantUriPermission(String arg0, Uri arg1, int arg2) {
1589        // pass
1590
1591    }
1592
1593    @Override
1594    public FileInputStream openFileInput(String arg0) throws FileNotFoundException {
1595        // pass
1596        return null;
1597    }
1598
1599    @Override
1600    public FileOutputStream openFileOutput(String arg0, int arg1) throws FileNotFoundException {
1601        // pass
1602        return null;
1603    }
1604
1605    @Override
1606    public SQLiteDatabase openOrCreateDatabase(String arg0, int arg1, CursorFactory arg2) {
1607        // pass
1608        return null;
1609    }
1610
1611    @Override
1612    public SQLiteDatabase openOrCreateDatabase(String arg0, int arg1,
1613            CursorFactory arg2, DatabaseErrorHandler arg3) {
1614        // pass
1615        return null;
1616    }
1617
1618    @Override
1619    public Drawable peekWallpaper() {
1620        // pass
1621        return null;
1622    }
1623
1624    @Override
1625    public Intent registerReceiver(BroadcastReceiver arg0, IntentFilter arg1) {
1626        // pass
1627        return null;
1628    }
1629
1630    @Override
1631    public Intent registerReceiver(BroadcastReceiver arg0, IntentFilter arg1, int arg2) {
1632        // pass
1633        return null;
1634    }
1635
1636    @Override
1637    public Intent registerReceiver(BroadcastReceiver arg0, IntentFilter arg1,
1638            String arg2, Handler arg3) {
1639        // pass
1640        return null;
1641    }
1642
1643    @Override
1644    public Intent registerReceiver(BroadcastReceiver arg0, IntentFilter arg1,
1645            String arg2, Handler arg3, int arg4) {
1646        // pass
1647        return null;
1648    }
1649
1650    @Override
1651    public Intent registerReceiverAsUser(BroadcastReceiver arg0, UserHandle arg0p5,
1652            IntentFilter arg1, String arg2, Handler arg3) {
1653        // pass
1654        return null;
1655    }
1656
1657    @Override
1658    public void removeStickyBroadcast(Intent arg0) {
1659        // pass
1660
1661    }
1662
1663    @Override
1664    public void revokeUriPermission(Uri arg0, int arg1) {
1665        // pass
1666
1667    }
1668
1669    @Override
1670    public void revokeUriPermission(String arg0, Uri arg1, int arg2) {
1671        // pass
1672
1673    }
1674
1675    @Override
1676    public void sendBroadcast(Intent arg0) {
1677        // pass
1678
1679    }
1680
1681    @Override
1682    public void sendBroadcast(Intent arg0, String arg1) {
1683        // pass
1684
1685    }
1686
1687    @Override
1688    public void sendBroadcastMultiplePermissions(Intent intent, String[] receiverPermissions) {
1689        // pass
1690
1691    }
1692
1693    @Override
1694    public void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user,
1695            String[] receiverPermissions) {
1696        // pass
1697
1698    }
1699
1700    @Override
1701    public void sendBroadcast(Intent arg0, String arg1, Bundle arg2) {
1702        // pass
1703
1704    }
1705
1706    @Override
1707    public void sendBroadcast(Intent intent, String receiverPermission, int appOp) {
1708        // pass
1709    }
1710
1711    @Override
1712    public void sendOrderedBroadcast(Intent arg0, String arg1) {
1713        // pass
1714
1715    }
1716
1717    @Override
1718    public void sendOrderedBroadcast(Intent arg0, String arg1,
1719            BroadcastReceiver arg2, Handler arg3, int arg4, String arg5,
1720            Bundle arg6) {
1721        // pass
1722
1723    }
1724
1725    @Override
1726    public void sendOrderedBroadcast(Intent arg0, String arg1,
1727            Bundle arg7, BroadcastReceiver arg2, Handler arg3, int arg4, String arg5,
1728            Bundle arg6) {
1729        // pass
1730
1731    }
1732
1733    @Override
1734    public void sendOrderedBroadcast(Intent intent, String receiverPermission, int appOp,
1735            BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
1736            String initialData, Bundle initialExtras) {
1737        // pass
1738    }
1739
1740    @Override
1741    public void sendBroadcastAsUser(Intent intent, UserHandle user) {
1742        // pass
1743    }
1744
1745    @Override
1746    public void sendBroadcastAsUser(Intent intent, UserHandle user,
1747            String receiverPermission) {
1748        // pass
1749    }
1750
1751    @Override
1752    public void sendBroadcastAsUser(Intent intent, UserHandle user,
1753            String receiverPermission, Bundle options) {
1754        // pass
1755    }
1756
1757    public void sendBroadcastAsUser(Intent intent, UserHandle user,
1758            String receiverPermission, int appOp) {
1759        // pass
1760    }
1761
1762    @Override
1763    public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
1764            String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler,
1765            int initialCode, String initialData, Bundle initialExtras) {
1766        // pass
1767    }
1768
1769    @Override
1770    public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
1771            String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
1772            Handler scheduler,
1773            int initialCode, String initialData, Bundle initialExtras) {
1774        // pass
1775    }
1776
1777    @Override
1778    public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
1779            String receiverPermission, int appOp, Bundle options, BroadcastReceiver resultReceiver,
1780            Handler scheduler,
1781            int initialCode, String initialData, Bundle initialExtras) {
1782        // pass
1783    }
1784
1785    @Override
1786    public void sendStickyBroadcast(Intent arg0) {
1787        // pass
1788
1789    }
1790
1791    @Override
1792    public void sendStickyOrderedBroadcast(Intent intent,
1793            BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData,
1794           Bundle initialExtras) {
1795        // pass
1796    }
1797
1798    @Override
1799    public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) {
1800        // pass
1801    }
1802
1803    @Override
1804    public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) {
1805        // pass
1806    }
1807
1808    @Override
1809    public void sendStickyOrderedBroadcastAsUser(Intent intent,
1810            UserHandle user, BroadcastReceiver resultReceiver,
1811            Handler scheduler, int initialCode, String initialData,
1812            Bundle initialExtras) {
1813        // pass
1814    }
1815
1816    @Override
1817    public void removeStickyBroadcastAsUser(Intent intent, UserHandle user) {
1818        // pass
1819    }
1820
1821    @Override
1822    public void setTheme(int arg0) {
1823        // pass
1824
1825    }
1826
1827    @Override
1828    public void setWallpaper(Bitmap arg0) throws IOException {
1829        // pass
1830
1831    }
1832
1833    @Override
1834    public void setWallpaper(InputStream arg0) throws IOException {
1835        // pass
1836
1837    }
1838
1839    @Override
1840    public void startActivity(Intent arg0) {
1841        // pass
1842    }
1843
1844    @Override
1845    public void startActivity(Intent arg0, Bundle arg1) {
1846        // pass
1847    }
1848
1849    @Override
1850    public void startIntentSender(IntentSender intent,
1851            Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags)
1852            throws IntentSender.SendIntentException {
1853        // pass
1854    }
1855
1856    @Override
1857    public void startIntentSender(IntentSender intent,
1858            Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags,
1859            Bundle options) throws IntentSender.SendIntentException {
1860        // pass
1861    }
1862
1863    @Override
1864    public boolean startInstrumentation(ComponentName arg0, String arg1,
1865            Bundle arg2) {
1866        // pass
1867        return false;
1868    }
1869
1870    @Override
1871    public ComponentName startService(Intent arg0) {
1872        // pass
1873        return null;
1874    }
1875
1876    @Override
1877    public ComponentName startForegroundService(Intent service) {
1878        // pass
1879        return null;
1880    }
1881
1882    @Override
1883    public ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) {
1884        // pass
1885        return null;
1886    }
1887
1888    @Override
1889    public boolean stopService(Intent arg0) {
1890        // pass
1891        return false;
1892    }
1893
1894    @Override
1895    public ComponentName startServiceAsUser(Intent arg0, UserHandle arg1) {
1896        // pass
1897        return null;
1898    }
1899
1900    @Override
1901    public boolean stopServiceAsUser(Intent arg0, UserHandle arg1) {
1902        // pass
1903        return false;
1904    }
1905
1906    @Override
1907    public void unbindService(ServiceConnection arg0) {
1908        // pass
1909
1910    }
1911
1912    @Override
1913    public void unregisterReceiver(BroadcastReceiver arg0) {
1914        // pass
1915
1916    }
1917
1918    @Override
1919    public Context getApplicationContext() {
1920        return this;
1921    }
1922
1923    @Override
1924    public void startActivities(Intent[] arg0) {
1925        // pass
1926
1927    }
1928
1929    @Override
1930    public void startActivities(Intent[] arg0, Bundle arg1) {
1931        // pass
1932
1933    }
1934
1935    @Override
1936    public boolean isRestricted() {
1937        return false;
1938    }
1939
1940    @Override
1941    public File getObbDir() {
1942        Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, "OBB not supported", null);
1943        return null;
1944    }
1945
1946    @Override
1947    public DisplayAdjustments getDisplayAdjustments(int displayId) {
1948        // pass
1949        return null;
1950    }
1951
1952    @Override
1953    public Display getDisplay() {
1954        // pass
1955        return null;
1956    }
1957
1958    @Override
1959    public void updateDisplay(int displayId) {
1960        // pass
1961    }
1962
1963    @Override
1964    public int getUserId() {
1965        return 0; // not used
1966    }
1967
1968    @Override
1969    public File[] getExternalFilesDirs(String type) {
1970        // pass
1971        return new File[0];
1972    }
1973
1974    @Override
1975    public File[] getObbDirs() {
1976        // pass
1977        return new File[0];
1978    }
1979
1980    @Override
1981    public File[] getExternalCacheDirs() {
1982        // pass
1983        return new File[0];
1984    }
1985
1986    @Override
1987    public File[] getExternalMediaDirs() {
1988        // pass
1989        return new File[0];
1990    }
1991
1992    public void setScrollYPos(@NonNull View view, int scrollPos) {
1993        mScrollYPos.put(view, scrollPos);
1994    }
1995
1996    public int getScrollYPos(@NonNull View view) {
1997        Integer pos = mScrollYPos.get(view);
1998        return pos != null ? pos : 0;
1999    }
2000
2001    public void setScrollXPos(@NonNull View view, int scrollPos) {
2002        mScrollXPos.put(view, scrollPos);
2003    }
2004
2005    public int getScrollXPos(@NonNull View view) {
2006        Integer pos = mScrollXPos.get(view);
2007        return pos != null ? pos : 0;
2008    }
2009
2010    @Override
2011    public Context createDeviceProtectedStorageContext() {
2012        // pass
2013        return null;
2014    }
2015
2016    @Override
2017    public Context createCredentialProtectedStorageContext() {
2018        // pass
2019        return null;
2020    }
2021
2022    @Override
2023    public boolean isDeviceProtectedStorage() {
2024        return false;
2025    }
2026
2027    @Override
2028    public boolean isCredentialProtectedStorage() {
2029        return false;
2030    }
2031
2032    @Override
2033    public boolean canLoadUnsafeResources() {
2034        return true;
2035    }
2036
2037    private class AttributeHolder {
2038        private int resourceId;
2039        private String name;
2040        private boolean isFramework;
2041
2042        private AttributeHolder(int resourceId, String name, boolean isFramework) {
2043            this.resourceId = resourceId;
2044            this.name = name;
2045            this.isFramework = isFramework;
2046        }
2047    }
2048
2049    /**
2050     * The cached value depends on
2051     * <ol>
2052     * <li>{@code int[]}: the attributes for which TypedArray is created </li>
2053     * <li>{@code List<StyleResourceValue>}: the themes set on the context at the time of
2054     * creation of the TypedArray</li>
2055     * <li>{@code Integer}: the default style used at the time of creation</li>
2056     * </ol>
2057     *
2058     * The class is created by using nested maps resolving one dependency at a time.
2059     * <p/>
2060     * The final value of the nested maps is a pair of the typed array and a map of properties
2061     * that should be added to {@link #mDefaultPropMaps}, if needed.
2062     */
2063    private static class TypedArrayCache {
2064
2065        private Map<int[],
2066                Map<List<StyleResourceValue>,
2067                        Map<Integer, Pair<BridgeTypedArray, PropertiesMap>>>> mCache;
2068
2069        private TypedArrayCache() {
2070            mCache = new IdentityHashMap<>();
2071        }
2072
2073        public Pair<BridgeTypedArray, PropertiesMap> get(int[] attrs,
2074                List<StyleResourceValue> themes, int resId) {
2075            Map<List<StyleResourceValue>, Map<Integer, Pair<BridgeTypedArray, PropertiesMap>>>
2076                    cacheFromThemes = mCache.get(attrs);
2077            if (cacheFromThemes != null) {
2078                Map<Integer, Pair<BridgeTypedArray, PropertiesMap>> cacheFromResId =
2079                        cacheFromThemes.get(themes);
2080                if (cacheFromResId != null) {
2081                    return cacheFromResId.get(resId);
2082                }
2083            }
2084            return null;
2085        }
2086
2087        public void put(int[] attrs, List<StyleResourceValue> themes, int resId,
2088                Pair<BridgeTypedArray, PropertiesMap> value) {
2089            Map<List<StyleResourceValue>, Map<Integer, Pair<BridgeTypedArray, PropertiesMap>>>
2090                    cacheFromThemes = mCache.computeIfAbsent(attrs, k -> new HashMap<>());
2091            Map<Integer, Pair<BridgeTypedArray, PropertiesMap>> cacheFromResId =
2092                    cacheFromThemes.computeIfAbsent(themes, k -> new HashMap<>());
2093            cacheFromResId.put(resId, value);
2094        }
2095
2096    }
2097}
2098