1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
17 
18 import static com.android.SdkConstants.LAYOUT_RESOURCE_PREFIX;
19 
20 import com.android.annotations.NonNull;
21 import com.android.ide.common.api.IClientRulesEngine;
22 import com.android.ide.common.api.INode;
23 import com.android.ide.common.api.Rect;
24 import com.android.ide.common.rendering.HardwareConfigHelper;
25 import com.android.ide.common.rendering.LayoutLibrary;
26 import com.android.ide.common.rendering.RenderSecurityManager;
27 import com.android.ide.common.rendering.api.Capability;
28 import com.android.ide.common.rendering.api.DrawableParams;
29 import com.android.ide.common.rendering.api.HardwareConfig;
30 import com.android.ide.common.rendering.api.IImageFactory;
31 import com.android.ide.common.rendering.api.ILayoutPullParser;
32 import com.android.ide.common.rendering.api.LayoutLog;
33 import com.android.ide.common.rendering.api.RenderSession;
34 import com.android.ide.common.rendering.api.ResourceValue;
35 import com.android.ide.common.rendering.api.Result;
36 import com.android.ide.common.rendering.api.SessionParams;
37 import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
38 import com.android.ide.common.rendering.api.ViewInfo;
39 import com.android.ide.common.resources.ResourceResolver;
40 import com.android.ide.common.resources.configuration.FolderConfiguration;
41 import com.android.ide.eclipse.adt.AdtPlugin;
42 import com.android.ide.eclipse.adt.AdtUtils;
43 import com.android.ide.eclipse.adt.internal.editors.layout.ContextPullParser;
44 import com.android.ide.eclipse.adt.internal.editors.layout.ProjectCallback;
45 import com.android.ide.eclipse.adt.internal.editors.layout.UiElementPullParser;
46 import com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration;
47 import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationChooser;
48 import com.android.ide.eclipse.adt.internal.editors.layout.configuration.Locale;
49 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.IncludeFinder.Reference;
50 import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeFactory;
51 import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
52 import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
53 import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo;
54 import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo.ActivityAttributes;
55 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
56 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
57 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
58 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
59 import com.android.sdklib.IAndroidTarget;
60 import com.android.sdklib.devices.Device;
61 import com.google.common.base.Charsets;
62 import com.google.common.io.Files;
63 
64 import org.eclipse.core.resources.IProject;
65 import org.xmlpull.v1.XmlPullParser;
66 import org.xmlpull.v1.XmlPullParserException;
67 
68 import java.awt.Toolkit;
69 import java.awt.image.BufferedImage;
70 import java.io.File;
71 import java.io.IOException;
72 import java.io.StringReader;
73 import java.util.Collections;
74 import java.util.HashMap;
75 import java.util.List;
76 import java.util.Map;
77 import java.util.Set;
78 
79 /**
80  * The {@link RenderService} provides rendering and layout information for
81  * Android layouts. This is a wrapper around the layout library.
82  */
83 public class RenderService {
84     private static final Object RENDERING_LOCK = new Object();
85 
86     /** Reference to the file being edited. Can also be used to access the {@link IProject}. */
87     private final GraphicalEditorPart mEditor;
88 
89     // The following fields are inferred from the editor and not customizable by the
90     // client of the render service:
91 
92     private final IProject mProject;
93     private final ProjectCallback mProjectCallback;
94     private final ResourceResolver mResourceResolver;
95     private final int mMinSdkVersion;
96     private final int mTargetSdkVersion;
97     private final LayoutLibrary mLayoutLib;
98     private final IImageFactory mImageFactory;
99     private final HardwareConfigHelper mHardwareConfigHelper;
100     private final Locale mLocale;
101 
102     // The following fields are optional or configurable using the various chained
103     // setters:
104 
105     private UiDocumentNode mModel;
106     private Reference mIncludedWithin;
107     private RenderingMode mRenderingMode = RenderingMode.NORMAL;
108     private LayoutLog mLogger;
109     private Integer mOverrideBgColor;
110     private boolean mShowDecorations = true;
111     private Set<UiElementNode> mExpandNodes = Collections.<UiElementNode>emptySet();
112     private final Object mCredential;
113 
114     /** Use the {@link #create} factory instead */
RenderService(GraphicalEditorPart editor, Object credential)115     private RenderService(GraphicalEditorPart editor, Object credential) {
116         mEditor = editor;
117         mCredential = credential;
118 
119         mProject = editor.getProject();
120         LayoutCanvas canvas = editor.getCanvasControl();
121         mImageFactory = canvas.getImageOverlay();
122         ConfigurationChooser chooser = editor.getConfigurationChooser();
123         Configuration config = chooser.getConfiguration();
124         FolderConfiguration folderConfig = config.getFullConfig();
125 
126         Device device = config.getDevice();
127         assert device != null; // Should only attempt render with configuration that has device
128         mHardwareConfigHelper = new HardwareConfigHelper(device);
129         mHardwareConfigHelper.setOrientation(
130                 folderConfig.getScreenOrientationQualifier().getValue());
131 
132         mLayoutLib = editor.getReadyLayoutLib(true /*displayError*/);
133         mResourceResolver = editor.getResourceResolver();
134         mProjectCallback = editor.getProjectCallback(true /*reset*/, mLayoutLib);
135         mMinSdkVersion = editor.getMinSdkVersion();
136         mTargetSdkVersion = editor.getTargetSdkVersion();
137         mLocale = config.getLocale();
138     }
139 
RenderService(GraphicalEditorPart editor, Configuration configuration, ResourceResolver resourceResolver, Object credential)140     private RenderService(GraphicalEditorPart editor,
141             Configuration configuration, ResourceResolver resourceResolver,
142             Object credential) {
143         mEditor = editor;
144         mCredential = credential;
145 
146         mProject = editor.getProject();
147         LayoutCanvas canvas = editor.getCanvasControl();
148         mImageFactory = canvas.getImageOverlay();
149         FolderConfiguration folderConfig = configuration.getFullConfig();
150 
151         Device device = configuration.getDevice();
152         assert device != null;
153         mHardwareConfigHelper = new HardwareConfigHelper(device);
154         mHardwareConfigHelper.setOrientation(
155                 folderConfig.getScreenOrientationQualifier().getValue());
156 
157         mLayoutLib = editor.getReadyLayoutLib(true /*displayError*/);
158         mResourceResolver =  resourceResolver != null ? resourceResolver : editor.getResourceResolver();
159         mProjectCallback = editor.getProjectCallback(true /*reset*/, mLayoutLib);
160         mMinSdkVersion = editor.getMinSdkVersion();
161         mTargetSdkVersion = editor.getTargetSdkVersion();
162         mLocale = configuration.getLocale();
163     }
164 
createSecurityManager()165     private RenderSecurityManager createSecurityManager() {
166         String projectPath = null;
167         String sdkPath = null;
168         if (RenderSecurityManager.RESTRICT_READS) {
169             projectPath = AdtUtils.getAbsolutePath(mProject).toFile().getPath();
170             Sdk sdk = Sdk.getCurrent();
171             sdkPath = sdk != null ? sdk.getSdkOsLocation() : null;
172         }
173         RenderSecurityManager securityManager = new RenderSecurityManager(sdkPath, projectPath);
174         securityManager.setLogger(AdtPlugin.getDefault());
175 
176         // Make sure this is initialized before we attempt to use it from layoutlib
177         Toolkit.getDefaultToolkit();
178 
179         return securityManager;
180       }
181 
182     /**
183      * Returns true if this configuration supports the given rendering
184      * capability
185      *
186      * @param target the target to look up the layout library for
187      * @param capability the capability to check
188      * @return true if the capability is supported
189      */
supports( @onNull IAndroidTarget target, @NonNull Capability capability)190     public static boolean supports(
191             @NonNull IAndroidTarget target,
192             @NonNull Capability capability) {
193         Sdk sdk = Sdk.getCurrent();
194         if (sdk != null) {
195             AndroidTargetData targetData = sdk.getTargetData(target);
196             if (targetData != null) {
197                 LayoutLibrary layoutLib = targetData.getLayoutLibrary();
198                 if (layoutLib != null) {
199                     return layoutLib.supports(capability);
200                 }
201             }
202         }
203 
204         return false;
205     }
206 
207     /**
208      * Creates a new {@link RenderService} associated with the given editor.
209      *
210      * @param editor the editor to provide configuration data such as the render target
211      * @return a {@link RenderService} which can perform rendering services
212      */
create(GraphicalEditorPart editor)213     public static RenderService create(GraphicalEditorPart editor) {
214         // Delegate to editor such that it can pass its credential to the service
215         return editor.createRenderService();
216     }
217 
218     /**
219      * Creates a new {@link RenderService} associated with the given editor.
220      *
221      * @param editor the editor to provide configuration data such as the render target
222      * @param credential the sandbox credential
223      * @return a {@link RenderService} which can perform rendering services
224      */
225     @NonNull
create(GraphicalEditorPart editor, Object credential)226     public static RenderService create(GraphicalEditorPart editor, Object credential) {
227         return new RenderService(editor, credential);
228     }
229 
230     /**
231      * Creates a new {@link RenderService} associated with the given editor.
232      *
233      * @param editor the editor to provide configuration data such as the render target
234      * @param configuration the configuration to use (and fallback to editor for the rest)
235      * @param resolver a resource resolver to use to look up resources
236      * @return a {@link RenderService} which can perform rendering services
237      */
create(GraphicalEditorPart editor, Configuration configuration, ResourceResolver resolver)238     public static RenderService create(GraphicalEditorPart editor,
239             Configuration configuration, ResourceResolver resolver) {
240         // Delegate to editor such that it can pass its credential to the service
241         return editor.createRenderService(configuration, resolver);
242     }
243 
244     /**
245      * Creates a new {@link RenderService} associated with the given editor.
246      *
247      * @param editor the editor to provide configuration data such as the render target
248      * @param configuration the configuration to use (and fallback to editor for the rest)
249      * @param resolver a resource resolver to use to look up resources
250      * @param credential the sandbox credential
251      * @return a {@link RenderService} which can perform rendering services
252      */
create(GraphicalEditorPart editor, Configuration configuration, ResourceResolver resolver, Object credential)253     public static RenderService create(GraphicalEditorPart editor,
254             Configuration configuration, ResourceResolver resolver, Object credential) {
255         return new RenderService(editor, configuration, resolver, credential);
256     }
257 
258     /**
259      * Renders the given model, using this editor's theme and screen settings, and returns
260      * the result as a {@link RenderSession}.
261      *
262      * @param model the model to be rendered, which can be different than the editor's own
263      *            {@link #getModel()}.
264      * @param width the width to use for the layout, or -1 to use the width of the screen
265      *            associated with this editor
266      * @param height the height to use for the layout, or -1 to use the height of the screen
267      *            associated with this editor
268      * @param explodeNodes a set of nodes to explode, or null for none
269      * @param overrideBgColor If non-null, use the given color as a background to render over
270      *        rather than the normal background requested by the theme
271      * @param noDecor If true, don't draw window decorations like the system bar
272      * @param logger a logger where rendering errors are reported
273      * @param renderingMode the {@link RenderingMode} to use for rendering
274      * @return the resulting rendered image wrapped in an {@link RenderSession}
275      */
276 
277     /**
278      * Sets the {@link LayoutLog} to be used during rendering. If none is specified, a
279      * silent logger will be used.
280      *
281      * @param logger the log to be used
282      * @return this (such that chains of setters can be stringed together)
283      */
setLog(LayoutLog logger)284     public RenderService setLog(LayoutLog logger) {
285         mLogger = logger;
286         return this;
287     }
288 
289     /**
290      * Sets the model to be rendered, which can be different than the editor's own
291      * {@link GraphicalEditorPart#getModel()}.
292      *
293      * @param model the model to be rendered
294      * @return this (such that chains of setters can be stringed together)
295      */
setModel(UiDocumentNode model)296     public RenderService setModel(UiDocumentNode model) {
297         mModel = model;
298         return this;
299     }
300 
301     /**
302      * Overrides the width and height to be used during rendering (which might be adjusted if
303      * the {@link #setRenderingMode(RenderingMode)} is {@link RenderingMode#FULL_EXPAND}.
304      *
305      * A value of -1 will make the rendering use the normal width and height coming from the
306      * {@link Configuration#getDevice()} object.
307      *
308      * @param overrideRenderWidth the width in pixels of the layout to be rendered
309      * @param overrideRenderHeight the height in pixels of the layout to be rendered
310      * @return this (such that chains of setters can be stringed together)
311      */
setOverrideRenderSize(int overrideRenderWidth, int overrideRenderHeight)312     public RenderService setOverrideRenderSize(int overrideRenderWidth, int overrideRenderHeight) {
313         mHardwareConfigHelper.setOverrideRenderSize(overrideRenderWidth, overrideRenderHeight);
314         return this;
315     }
316 
317     /**
318      * Sets the max width and height to be used during rendering (which might be adjusted if
319      * the {@link #setRenderingMode(RenderingMode)} is {@link RenderingMode#FULL_EXPAND}.
320      *
321      * A value of -1 will make the rendering use the normal width and height coming from the
322      * {@link Configuration#getDevice()} object.
323      *
324      * @param maxRenderWidth the max width in pixels of the layout to be rendered
325      * @param maxRenderHeight the max height in pixels of the layout to be rendered
326      * @return this (such that chains of setters can be stringed together)
327      */
setMaxRenderSize(int maxRenderWidth, int maxRenderHeight)328     public RenderService setMaxRenderSize(int maxRenderWidth, int maxRenderHeight) {
329         mHardwareConfigHelper.setMaxRenderSize(maxRenderWidth, maxRenderHeight);
330         return this;
331     }
332 
333     /**
334      * Sets the {@link RenderingMode} to be used during rendering. If none is specified,
335      * the default is {@link RenderingMode#NORMAL}.
336      *
337      * @param renderingMode the rendering mode to be used
338      * @return this (such that chains of setters can be stringed together)
339      */
setRenderingMode(RenderingMode renderingMode)340     public RenderService setRenderingMode(RenderingMode renderingMode) {
341         mRenderingMode = renderingMode;
342         return this;
343     }
344 
345     /**
346      * Sets the overriding background color to be used, if any. The color should be a
347      * bitmask of AARRGGBB. The default is null.
348      *
349      * @param overrideBgColor the overriding background color to be used in the rendering,
350      *            in the form of a AARRGGBB bitmask, or null to use no custom background.
351      * @return this (such that chains of setters can be stringed together)
352      */
setOverrideBgColor(Integer overrideBgColor)353     public RenderService setOverrideBgColor(Integer overrideBgColor) {
354         mOverrideBgColor = overrideBgColor;
355         return this;
356     }
357 
358     /**
359      * Sets whether the rendering should include decorations such as a system bar, an
360      * application bar etc depending on the SDK target and theme. The default is true.
361      *
362      * @param showDecorations true if the rendering should include system bars etc.
363      * @return this (such that chains of setters can be stringed together)
364      */
setDecorations(boolean showDecorations)365     public RenderService setDecorations(boolean showDecorations) {
366         mShowDecorations = showDecorations;
367         return this;
368     }
369 
370     /**
371      * Sets the nodes to expand during rendering. These will be padded with approximately
372      * 20 pixels and also highlighted by the {@link EmptyViewsOverlay}. The default is an
373      * empty collection.
374      *
375      * @param nodesToExpand the nodes to be expanded
376      * @return this (such that chains of setters can be stringed together)
377      */
setNodesToExpand(Set<UiElementNode> nodesToExpand)378     public RenderService setNodesToExpand(Set<UiElementNode> nodesToExpand) {
379         mExpandNodes = nodesToExpand;
380         return this;
381     }
382 
383     /**
384      * Sets the {@link Reference} to an outer layout that this layout should be rendered
385      * within. The outer layout <b>must</b> contain an include tag which points to this
386      * layout. The default is null.
387      *
388      * @param includedWithin a reference to an outer layout to render this layout within
389      * @return this (such that chains of setters can be stringed together)
390      */
setIncludedWithin(Reference includedWithin)391     public RenderService setIncludedWithin(Reference includedWithin) {
392         mIncludedWithin = includedWithin;
393         return this;
394     }
395 
396     /** Initializes any remaining optional fields after all setters have been called */
finishConfiguration()397     private void finishConfiguration() {
398         if (mLogger == null) {
399             // Silent logging
400             mLogger = new LayoutLog();
401         }
402     }
403 
404     /**
405      * Renders the model and returns the result as a {@link RenderSession}.
406      * @return the {@link RenderSession} resulting from rendering the current model
407      */
createRenderSession()408     public RenderSession createRenderSession() {
409         assert mModel != null : "Incomplete service config";
410         finishConfiguration();
411 
412         if (mResourceResolver == null) {
413             // Abort the rendering if the resources are not found.
414             return null;
415         }
416 
417         HardwareConfig hardwareConfig = mHardwareConfigHelper.getConfig();
418 
419         UiElementPullParser modelParser = new UiElementPullParser(mModel,
420                 false, mExpandNodes, hardwareConfig.getDensity(), mProject);
421         ILayoutPullParser topParser = modelParser;
422 
423         // Code to support editing included layout
424         // first reset the layout parser just in case.
425         mProjectCallback.setLayoutParser(null, null);
426 
427         if (mIncludedWithin != null) {
428             // Outer layout name:
429             String contextLayoutName = mIncludedWithin.getName();
430 
431             // Find the layout file.
432             ResourceValue contextLayout = mResourceResolver.findResValue(
433                     LAYOUT_RESOURCE_PREFIX + contextLayoutName, false  /* forceFrameworkOnly*/);
434             if (contextLayout != null) {
435                 File layoutFile = new File(contextLayout.getValue());
436                 if (layoutFile.isFile()) {
437                     try {
438                         // Get the name of the layout actually being edited, without the extension
439                         // as it's what IXmlPullParser.getParser(String) will receive.
440                         String queryLayoutName = mEditor.getLayoutResourceName();
441                         mProjectCallback.setLayoutParser(queryLayoutName, modelParser);
442                         topParser = new ContextPullParser(mProjectCallback, layoutFile);
443                         topParser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
444                         String xmlText = Files.toString(layoutFile, Charsets.UTF_8);
445                         topParser.setInput(new StringReader(xmlText));
446                     } catch (IOException e) {
447                         AdtPlugin.log(e, null);
448                     } catch (XmlPullParserException e) {
449                         AdtPlugin.log(e, null);
450                     }
451                 }
452             }
453         }
454 
455         SessionParams params = new SessionParams(
456                 topParser,
457                 mRenderingMode,
458                 mProject /* projectKey */,
459                 hardwareConfig,
460                 mResourceResolver,
461                 mProjectCallback,
462                 mMinSdkVersion,
463                 mTargetSdkVersion,
464                 mLogger);
465 
466         // Request margin and baseline information.
467         // TODO: Be smarter about setting this; start without it, and on the first request
468         // for an extended view info, re-render in the same session, and then set a flag
469         // which will cause this to create extended view info each time from then on in the
470         // same session
471         params.setExtendedViewInfoMode(true);
472 
473         params.setLocale(mLocale.toLocaleId());
474 
475         ManifestInfo manifestInfo = ManifestInfo.get(mProject);
476         try {
477             params.setRtlSupport(manifestInfo.isRtlSupported());
478         } catch (Exception e) {
479             // ignore.
480         }
481         if (!mShowDecorations) {
482             params.setForceNoDecor();
483         } else {
484             try {
485                 params.setAppLabel(manifestInfo.getApplicationLabel());
486                 params.setAppIcon(manifestInfo.getApplicationIcon());
487                 String activity = mEditor.getConfigurationChooser().getConfiguration().getActivity();
488                 if (activity != null) {
489                     ActivityAttributes info = manifestInfo.getActivityAttributes(activity);
490                     if (info != null) {
491                         if (info.getLabel() != null) {
492                             params.setAppLabel(info.getLabel());
493                         }
494                         if (info.getIcon() != null) {
495                             params.setAppIcon(info.getIcon());
496                         }
497                     }
498                 }
499             } catch (Exception e) {
500                 // ignore.
501             }
502         }
503 
504         if (mOverrideBgColor != null) {
505             params.setOverrideBgColor(mOverrideBgColor.intValue());
506         }
507 
508         // set the Image Overlay as the image factory.
509         params.setImageFactory(mImageFactory);
510 
511         mProjectCallback.setLogger(mLogger);
512         mProjectCallback.setResourceResolver(mResourceResolver);
513         RenderSecurityManager securityManager = createSecurityManager();
514         try {
515             securityManager.setActive(true, mCredential);
516             synchronized (RENDERING_LOCK) {
517                 return mLayoutLib.createSession(params);
518             }
519         } catch (RuntimeException t) {
520             // Exceptions from the bridge
521             mLogger.error(null, t.getLocalizedMessage(), t, null);
522             throw t;
523         } finally {
524             securityManager.dispose(mCredential);
525             mProjectCallback.setLogger(null);
526             mProjectCallback.setResourceResolver(null);
527         }
528     }
529 
530     /**
531      * Renders the given resource value (which should refer to a drawable) and returns it
532      * as an image
533      *
534      * @param drawableResourceValue the drawable resource value to be rendered, or null
535      * @return the image, or null if something went wrong
536      */
renderDrawable(ResourceValue drawableResourceValue)537     public BufferedImage renderDrawable(ResourceValue drawableResourceValue) {
538         if (drawableResourceValue == null) {
539             return null;
540         }
541 
542         finishConfiguration();
543 
544         HardwareConfig hardwareConfig = mHardwareConfigHelper.getConfig();
545 
546         DrawableParams params = new DrawableParams(drawableResourceValue, mProject, hardwareConfig,
547                 mResourceResolver, mProjectCallback, mMinSdkVersion,
548                 mTargetSdkVersion, mLogger);
549         params.setForceNoDecor();
550         Result result = mLayoutLib.renderDrawable(params);
551         if (result != null && result.isSuccess()) {
552             Object data = result.getData();
553             if (data instanceof BufferedImage) {
554                 return (BufferedImage) data;
555             }
556         }
557 
558         return null;
559     }
560 
561     /**
562      * Measure the children of the given parent node, applying the given filter to the
563      * pull parser's attribute values.
564      *
565      * @param parent the parent node to measure children for
566      * @param filter the filter to apply to the attribute values
567      * @return a map from node children of the parent to new bounds of the nodes
568      */
measureChildren(INode parent, final IClientRulesEngine.AttributeFilter filter)569     public Map<INode, Rect> measureChildren(INode parent,
570             final IClientRulesEngine.AttributeFilter filter) {
571         finishConfiguration();
572         HardwareConfig hardwareConfig = mHardwareConfigHelper.getConfig();
573 
574         final NodeFactory mNodeFactory = mEditor.getCanvasControl().getNodeFactory();
575         UiElementNode parentNode = ((NodeProxy) parent).getNode();
576         UiElementPullParser topParser = new UiElementPullParser(parentNode,
577                 false, Collections.<UiElementNode>emptySet(), hardwareConfig.getDensity(),
578                 mProject) {
579             @Override
580             public String getAttributeValue(String namespace, String localName) {
581                 if (filter != null) {
582                     Object cookie = getViewCookie();
583                     if (cookie instanceof UiViewElementNode) {
584                         NodeProxy node = mNodeFactory.create((UiViewElementNode) cookie);
585                         if (node != null) {
586                             String value = filter.getAttribute(node, namespace, localName);
587                             if (value != null) {
588                                 return value;
589                             }
590                             // null means no preference, not "unset".
591                         }
592                     }
593                 }
594 
595                 return super.getAttributeValue(namespace, localName);
596             }
597 
598             /**
599              * The parser usually assumes that the top level node is a document node that
600              * should be skipped, and that's not the case when we render in the middle of
601              * the tree, so override {@link UiElementPullParser#onNextFromStartDocument}
602              * to change this behavior
603              */
604             @Override
605             public void onNextFromStartDocument() {
606                 mParsingState = START_TAG;
607             }
608         };
609 
610         SessionParams params = new SessionParams(
611                 topParser,
612                 RenderingMode.FULL_EXPAND,
613                 mProject /* projectKey */,
614                 hardwareConfig,
615                 mResourceResolver,
616                 mProjectCallback,
617                 mMinSdkVersion,
618                 mTargetSdkVersion,
619                 mLogger);
620         params.setLayoutOnly();
621         params.setForceNoDecor();
622 
623         RenderSession session = null;
624         mProjectCallback.setLogger(mLogger);
625         mProjectCallback.setResourceResolver(mResourceResolver);
626         RenderSecurityManager securityManager = createSecurityManager();
627         try {
628             securityManager.setActive(true, mCredential);
629             synchronized (RENDERING_LOCK) {
630                 session = mLayoutLib.createSession(params);
631             }
632             if (session.getResult().isSuccess()) {
633                 assert session.getRootViews().size() == 1;
634                 ViewInfo root = session.getRootViews().get(0);
635                 List<ViewInfo> children = root.getChildren();
636                 Map<INode, Rect> map = new HashMap<INode, Rect>(children.size());
637                 for (ViewInfo info : children) {
638                     if (info.getCookie() instanceof UiViewElementNode) {
639                         UiViewElementNode uiNode = (UiViewElementNode) info.getCookie();
640                         NodeProxy node = mNodeFactory.create(uiNode);
641                         map.put(node, new Rect(info.getLeft(), info.getTop(),
642                                 info.getRight() - info.getLeft(),
643                                 info.getBottom() - info.getTop()));
644                     }
645                 }
646 
647                 return map;
648             }
649         } catch (RuntimeException t) {
650             // Exceptions from the bridge
651             mLogger.error(null, t.getLocalizedMessage(), t, null);
652             throw t;
653         } finally {
654             securityManager.dispose(mCredential);
655             mProjectCallback.setLogger(null);
656             mProjectCallback.setResourceResolver(null);
657             if (session != null) {
658                 session.dispose();
659             }
660         }
661 
662         return null;
663     }
664 }
665