summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/content/pm/PackageParser.java11
-rw-r--r--core/res/res/values/attrs_manifest.xml2
-rw-r--r--docs/html/distribute/distribute_toc.cs123
-rw-r--r--docs/html/distribute/googleplay/policies/ads.jd352
-rw-r--r--docs/html/distribute/googleplay/policies/index.jd59
-rw-r--r--docs/html/distribute/googleplay/policies/ip.jd345
-rw-r--r--docs/html/distribute/googleplay/policies/spam.jd421
-rw-r--r--docs/html/images/example-bad.pngbin0 -> 501 bytes
-rw-r--r--docs/html/images/example-good.pngbin0 -> 834 bytes
-rw-r--r--docs/html/images/gp-policy-ads-eula-violation.pngbin0 -> 111656 bytes
-rw-r--r--docs/html/images/gp-policy-ads-eula.pngbin0 -> 163101 bytes
-rw-r--r--docs/html/images/gp-policy-ads-impersonate-violation.pngbin0 -> 65883 bytes
-rw-r--r--docs/html/images/gp-policy-ads-maturity-violation.pngbin0 -> 81774 bytes
-rw-r--r--docs/html/images/gp-policy-ads-notif-attr-violation.pngbin0 -> 18974 bytes
-rw-r--r--docs/html/images/gp-policy-ads-notif-attr.pngbin0 -> 15726 bytes
-rw-r--r--docs/html/images/gp-policy-ads-paywall-violation.pngbin0 -> 129229 bytes
-rw-r--r--docs/html/images/gp-policy-ads-paywall.pngbin0 -> 121501 bytes
-rw-r--r--docs/html/images/gp-policy-ads-terms.pngbin0 -> 161466 bytes
-rw-r--r--docs/html/images/gp-policy-ip-copyright-violation.pngbin0 -> 178784 bytes
-rw-r--r--docs/html/images/gp-policy-ip-impersonation-violation.pngbin0 -> 117027 bytes
-rw-r--r--docs/html/images/gp-policy-ip-trademark-violation.pngbin0 -> 117292 bytes
-rw-r--r--docs/html/images/gp-policy-spam-negreview.pngbin0 -> 43371 bytes
-rw-r--r--docs/html/images/gp-policy-spam-reqrating.pngbin0 -> 50100 bytes
-rw-r--r--keystore/java/android/security/KeyStore.java24
-rw-r--r--libs/hwui/Debug.h3
-rw-r--r--libs/hwui/DeferredDisplayList.cpp259
-rw-r--r--libs/hwui/DeferredDisplayList.h28
-rw-r--r--libs/hwui/DisplayList.cpp2
-rw-r--r--libs/hwui/DisplayListOp.h249
-rw-r--r--libs/hwui/DisplayListRenderer.cpp13
-rw-r--r--libs/hwui/DisplayListRenderer.h5
-rw-r--r--libs/hwui/FontRenderer.cpp14
-rw-r--r--libs/hwui/FontRenderer.h6
-rw-r--r--libs/hwui/OpenGLRenderer.cpp93
-rw-r--r--libs/hwui/OpenGLRenderer.h59
-rw-r--r--libs/hwui/utils/TinyHashMap.h72
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java12
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java9
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java5
-rw-r--r--services/java/com/android/server/accessibility/AccessibilityManagerService.java12
-rw-r--r--services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java8
-rw-r--r--services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java113
-rw-r--r--wifi/java/android/net/wifi/WifiConfigStore.java3
43 files changed, 1993 insertions, 309 deletions
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 3586573..acb3725 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1817,11 +1817,12 @@ public class PackageParser {
false)) {
owner.mRequiredForAllUsers = true;
}
- String restrictedAccountType = sa.getString(com.android.internal.R.styleable
- .AndroidManifestApplication_restrictedAccountType);
- if (restrictedAccountType != null && restrictedAccountType.length() > 0) {
- owner.mRestrictedAccountType = restrictedAccountType;
- }
+ }
+
+ String restrictedAccountType = sa.getString(com.android.internal.R.styleable
+ .AndroidManifestApplication_restrictedAccountType);
+ if (restrictedAccountType != null && restrictedAccountType.length() > 0) {
+ owner.mRestrictedAccountType = restrictedAccountType;
}
String requiredAccountType = sa.getString(com.android.internal.R.styleable
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 8821a01..f2c0aa0 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -909,7 +909,7 @@
<!-- Declare that this application requires access to restricted accounts of a certain
type. The default value is null and restricted accounts won\'t be visible to this
application. The type should correspond to the account authenticator type, such as
- "com.google". Only usable by system apps. -->
+ "com.google". -->
<attr name="restrictedAccountType" format="string"/>
<!-- Declare that this application requires an account of a certain
type. The default value is null and indicates that the application can work without
diff --git a/docs/html/distribute/distribute_toc.cs b/docs/html/distribute/distribute_toc.cs
index ad3121c..3ea11bf 100644
--- a/docs/html/distribute/distribute_toc.cs
+++ b/docs/html/distribute/distribute_toc.cs
@@ -1,106 +1,71 @@
<ul id="nav">
<li class="nav-section">
- <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/index.html">
- <span class="en">Google Play</span></a>
- </div>
+ <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/index.html">Google Play</a></div>
<ul>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/about/visibility.html">
- <span class="en">Visibility</a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/about/monetizing.html">
- <span class="en">Monetizing</a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/about/distribution.html">
- <span class="en">Distribution</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/about/visibility.html">Visibility</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/about/monetizing.html">Monetizing</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/about/distribution.html">Distribution</a></li>
</ul>
</li>
<li class="nav-section">
- <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/publish/index.html">
- <span class="en">Publishing</span></a>
- </div>
+ <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/publish/index.html">Publishing</a></div>
<ul>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/publish/register.html">
- <span class="en">Get Started</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/publish/console.html">
- <span class="en">Developer Console</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/publish/preparing.html">
- <span class="en">Publishing Checklist</span>
- </a></li>
-
- </ul>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/publish/register.html">Get Started</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/publish/console.html">Developer Console</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/publish/preparing.html">Publishing Checklist</a></li>
+ </ul>
</li>
<!-- <li class="nav-section">
- <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/developer-console.html">
- <span class="en">The Developer Console</span>
- </a>
+ <div class="nav-section-header">
+ <a href="<?cs var:toroot ?>distribute/googleplay/developer-console.html">The Developer Console</a>
</div>
<ul>
- <li class="nav-section"><a href="<?cs var:toroot ?>distribute/googleplay/register.html">
- <span class="en">Get Started</span></a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/distribution-controls.html">
- <span class="en">Managing Distribution</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/pricing-billing.html">
- <span class="en">Pricing and Billing</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/app-data.html">
- <span class="en">Reviewing App Data</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/advanced-options.html">
- <span class="en">Advanced Options</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/publishing.html">
- <span class="en">Publishing and Updating</span>
- </a></li>
+ <li class="nav-section"><a href="<?cs var:toroot ?>distribute/googleplay/register.html">Get Started</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/distribution-controls.html">Managing Distribution</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/pricing-billing.html">Pricing and Billing</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/app-data.html">Reviewing App Data</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/advanced-options.html">Advanced Options</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/publishing.html">Publishing and Updating</a></li>
</ul>
</li> end of Developer Console -->
-
+
<li class="nav-section">
- <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/promote/index.html">
- <span class="en">Promoting</span></a>
+ <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/promote/index.html">Promoting</a>
</div>
<ul>
-<!-- <li><a href="<?cs var:toroot ?>distribute/googleplay/promote/product-pages.html">
- <span class="en">Your Product Pages</a></li>
--->
- <li><a href="<?cs var:toroot ?>distribute/googleplay/promote/linking.html">
- <span class="en">Linking to Your Products</a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/promote/badges.html">
- <span class="en">Google Play Badges</a></li>
- <li><a href="<?cs var:toroot ?>distribute/promote/device-art.html">
- <span class="en">Device Art Generator</a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/promote/brand.html">
- <span class="en">Brand Guidelines</a></li>
+<!-- <li><a href="<?cs var:toroot ?>distribute/googleplay/promote/product-pages.html">Your Product Pages</a></li> -->
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/promote/linking.html">Linking to Your Products</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/promote/badges.html">Google Play Badges</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/promote/device-art.html">Device Art Generator</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/promote/brand.html">Brand Guidelines</a></li>
</ul>
</li>
-
<li class="nav-section">
- <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/quality/index.html">
- <span class="en">App Quality</span></a>
- </div>
+ <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/quality/index.html">App Quality</a></div>
<ul>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/quality/core.html">
- <span class="en">Core App Quality</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/quality/tablet.html">
- <span class="en">Tablet App Quality</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/strategies/app-quality.html">
- <span class="en">Improving App Quality</span>
- </a></li>
-
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/quality/core.html">Core App Quality</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/quality/tablet.html">Tablet App Quality</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/strategies/app-quality.html">Improving App Quality</a></li>
</ul>
</li>
+ <li class="nav-section">
+ <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/policies/index.html">Policies</a></div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/policies/spam.html">Spam</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/policies/ip.html">Intellectual<br />Property</a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/policies/ads.html">Ads</a></li>
+ </ul>
+ </li>
<!--
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/after.html">
- <span class="en">After Launch</span></a>
+ After Launch</a>
</div>
<ul>
<li><a href="<?cs var:toroot ?>distribute/googleplay/errors.html.html">Reviewing Errors</a></li>
@@ -111,22 +76,14 @@
-->
<li class="nav-section">
- <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/spotlight/index.html">
- <span class="en">Spotlight</span></a>
- </div>
+ <div class="nav-section-header"><a href="<?cs var:toroot ?>distribute/googleplay/spotlight/index.html">Spotlight</a></div>
<ul>
- <li><a href="<?cs var:toroot ?>distribute/googleplay/spotlight/tablets.html">
- <span class="en">Tablet Stories</span>
- </a></li>
+ <li><a href="<?cs var:toroot ?>distribute/googleplay/spotlight/tablets.html">Tablet Stories</a></li>
</ul>
</li>
<li class="nav-section">
- <div class="nav-section-header empty">
- <a href="<?cs var:toroot ?>distribute/open.html">
- <span class="en">Open Distribution</span>
- </a>
- </div>
+ <div class="nav-section-header empty"><a href="<?cs var:toroot ?>distribute/open.html">Open Distribution</a></div>
</li>
</ul>
\ No newline at end of file
diff --git a/docs/html/distribute/googleplay/policies/ads.jd b/docs/html/distribute/googleplay/policies/ads.jd
new file mode 100644
index 0000000..8920499
--- /dev/null
+++ b/docs/html/distribute/googleplay/policies/ads.jd
@@ -0,0 +1,352 @@
+page.title=Ads
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+ <h2>In This Document</h2>
+ <ol>
+ <li><a href="#content-maturity">Content and Maturity</a></li>
+ <li><a href="#context">Context and Behavior</a></li>
+ <li><a href="#disclosure" style="clear:right">Disclosure</a></li>
+ <li><a href="#impersonation">Impersonation of System UI</a></li>
+ <li><a href="#adwalls">Adwalls</a></li>
+ <li><a href="#interfering" style="clear:right;">Interference with Ads and Websites</a></li>
+ </ol>
+
+ <h2>More Resources</h2>
+ <ol>
+ <li><a href="http://play.google.com/about/developer-content-policy.html" target="_policies">Developer Program Policies</a></li>
+ <li><a href="http://www.android.com/us/developer-distribution-agreement.html#showlanguages" target="_policies">Developer Distribution Agreement</a></li>
+ <li><a href="http://support.google.com/googleplay/android-developer/answer/188189" target="_policies">Maturity Ratings</a></p>
+ </ol>
+</div>
+</div>
+
+<p>
+ Google Play policies guide how you can use ads in your apps, to help ensure
+ the best experience for users visiting and downloading apps from the store.
+</p>
+
+<p>
+ In general, for the purposes of policy, the content of ads displayed by your
+ app is considered part of your app. As an app developer, it is your
+ responsibility to ensure that the content, context, and behavior of ads in
+ your apps conforms to Google Play policies.
+</p>
+
+<p>
+ Before you publish, make sure you understand Google Play ad policies and how
+ to display ads in conformance with those policies. The sections below
+ highlight best practices and common examples to help you avoid the most
+ common types of policy violations.
+</p>
+
+<p>
+ For more information about Google Play policies that apply to your apps and
+ content, please see the <a href=
+ "http://play.google.com/about/developer-content-policy.html" target=
+ "_policies">Developer Program Policies</a> and <a href=
+ "http://play.google.com/about/developer-distribution-agreement.html" target=
+ "_policies">Developer Distribution Agreement</a>.
+</p>
+
+
+<h2 id="content-maturity">Content and Maturity</h2>
+
+<div class="example-block bad">
+ <div class="heading">Ad maturity exceeds app</div>
+ <img src="{@docRoot}images/gp-policy-ads-maturity-violation.png">
+</div>
+
+<p>
+ From a policy perspective, ads shown in your app are part of your content
+ and your app is responsible for any violations. If an ad shown in your app
+ violates Google Play policies, your app may be suspended or your developer
+ account terminated.
+</p>
+
+<p>
+ For this reason, it's important for you to be be aware of what ads will be
+ displayed in your app and to manage the ads content according to Google Play
+ policies. Here are some guidelines:
+</p>
+
+<ul>
+ <li>
+ <strong>Ads must not violate Content Policy</strong>&mdash;Ads in
+ your app must not violate the terms of Google Play’s Content Policy,
+ including those concerning illegal activities, violence, sexually
+ explicit content, or privacy violations.
+ </li>
+ <li>
+ <strong>Ads maturity must be consistent with your app's
+ maturity</strong>&mdash;Content shown in your ads must be consistent
+ with the app’s maturity rating in Google Play. Especially, ads content
+ should never exceed your app's maturity rating, even if the ads content
+ by itself complies with general policies.
+ </li>
+</ul>
+
+<p>
+ In the example at right, the app's maturity rating is set to
+ "Everyone", which is the lowest maturity level on Google Play. By choosing
+ the "Everyone" maturity level, the developer is declaring that all of the
+ content in the app, <em>including ads</em>, is suitable for all users
+ regardless of age.
+</p>
+
+<p>
+ The example app violates Google Play policies by displaying ad content with a
+ higher maturity level&mdash;ad content showing gambling, profanity, user
+ location, suggestive content, or content from another app with higher
+ maturity exceeds the "Everyone" maturity rating. Because the ad's
+ maturity is higher than the app's maturity level, the app itself is in
+ violation of policy. To correct the problem, the developer must either
+ restrict ads content to "Everyone" level or raise the app's maturity rating.
+</p>
+
+<p>
+ For detailed information about how to choose the appropriate maturity level
+ for your app, or to assess the maturity requirement of ads in your app, see
+ <a href=
+ "http://support.google.com/googleplay/android-developer/answer/188189"
+ target="_policies">Rating your application content for Google Play</a>.
+</p>
+
+
+<h2 id="context">Context and Behavior</h2>
+
+<p>
+ If your app displays ads, it should do so in ways that do not interrupt users,
+ mislead them into clicking on ads, or make changes outside the app without
+ the user's knowledge or consent. Here are some guidelines:
+</p>
+
+<ul>
+ <li>
+ <strong>Display your ads within your UI</strong>&mdash;If possible,
+ display ads only within your app's UI. This leads to a better user
+ experience and helps avoid policy violations
+ </li>
+
+ <li>
+ <strong>Make sure app origin is clear</strong>&mdash;When you display an
+ ad, it must be clear to the user that the ad has originated from your app.
+ If you show the ad in your app's UI while your app has focus, the user
+ understands the ad origin without explicit attribution. However, if you
+ display the ad outside of your app, such as in a notification, you must
+ explicitly indicate the origin.
+ </li>
+
+ <li>
+ <strong>Don't make changes outside of the app without consent</strong>
+ &mdash;Ads must not make changes outside of the app without the user's
+ full knowledge and consent. For example, ads should not install shortcuts,
+ bookmarks, or icons, or change default settings without user consent.
+ </li>
+
+ <li>
+ <strong>Changes outside the app must be reversible</strong>&mdash;If an
+ ad makes changes outside the app as described above, the changes (and
+ origin app) must be evident and easily reversible. For example, the user
+ must be able to locate and reverse the changes by adjusting settings,
+ changing ad preferences in the app, or uninstalling the app altogether.
+ </li>
+
+ <li>
+ <strong>Notification ads require user opt-in</strong>&mdash;Your app
+ should not create <a href=
+ "{@docRoot}design/patterns/notifications.html">notifications</a>
+ containing ads unless the user has specifically opted-in to this behavior
+ and is able to easily opt-out.
+ </li>
+
+ <li>
+ <strong>Use low priority for notification ads</strong>&mdash;Always
+ assign your notification ads <a href="
+ {@docRoot}reference/android/app/Notification.html#PRIORITY_LOW">low
+ priority</a> (for API level 16 and above).
+ </li>
+</ul>
+
+<div class="example-block bad" style="width:400px;margin:.5em 0 0 2em;">
+ <div class="heading">Does not fully indicate origin app</div>
+ <img src="{@docRoot}images/gp-policy-ads-notif-attr-violation.png">
+</div>
+<div class="example-block good" style="width:400px;margin:.5em 0 0 2em;">
+ <div class="heading">Indicates origin app by name and icon</div>
+ <img src="{@docRoot}images/gp-policy-ads-notif-attr.png">
+</div>
+
+<p>
+ In particular, note that notification ads must clearly identify your app as
+ the ad origin. If your app sends notification ads that do not sufficiently
+ identify your app as the origin, the app will be in violation of policy.
+</p>
+
+<p>
+ To identify your app as the origin, you should display the <strong>app's full
+ name and and icon</strong> in the notification to provide the clearest
+ identification and best policy compliance. Displaying a partial app name can
+ also be sufficient, provided the name unambiguously identifies your app.
+</p>
+
+<p>
+ Above right is an example notification ad that violates ad policy by not
+ providing attribution of the origin app. Below right, the notification ads
+ comply with policy by providing both the app icon and full app name (in this
+ case, "Turtle Test").
+</p>
+
+
+<h2 id="disclosure" style="clear:right">Disclosure of Ads to Users</h2>
+
+<p>
+ It's important to sufficiently disclose to users how your app will use ads.
+ You must make it easy for users to understand what ads will be shown in your
+ app, where they will be shown, and what the associated behaviors are, if any.
+ Further, you should ask for user consent and provide options for managing ads
+ or opt-out. Here are some guidelines:
+</p>
+
+<ul>
+ <li>
+ <strong>Tell users about your ads</strong>&mdash;Create a simple,
+ complete disclosure that tells users how your app uses ads, where the ads
+ are shown, and how they can manage ad options. Take common-sense steps to
+ make the disclosure as clear as possible.
+ </li>
+
+ <li>
+ <strong>Make sure users know</strong>&mdash;Present your ads disclosure
+ is an easy-to-see location, rather than hiding it where users are not
+ likely to find it.
+ </li>
+
+ <li>
+ <strong>Ask for consent (opt-in) at launch</strong>&mdash;Where possible,
+ include your ads disclosure in the app description as well as in an Ads
+ Terms, End User License Agreement (EULA), or similar document. Display the
+ terms at first launch and ask for the user's consent before continuing to
+ the app.
+ </li>
+</ul>
+
+<p>
+ A recommended approach is to provide an ads disclosure in an End-User License
+ Agreement (EULA). The disclosure should be clear and succinct and displayed
+ in a modal dialog that asks the user to agree to the terms before using the
+ app.
+</p>
+
+<p>
+ If your app adds homescreen icons and/or browser bookmarks, an acceptable
+ practice for revealing that behavior is to provide a disclosure in both the
+ app description and an opt-in EULA on app launch. This ensures that the
+ behaviors are clearly explained to the user up-front and requires the user’s
+ consent in a pop-up EULA to continue using the app.
+</p>
+
+<div class="example-block good" style="width:213px;margin-right:2em;">
+ <div class="heading">Disclosure in Terms</div>
+ <img src="{@docRoot}images/gp-policy-ads-terms.png">
+</div>
+
+<div class="example-block good" style="width:213px;">
+ <div class="heading">Disclosure in EULA</div>
+ <img src="{@docRoot}images/gp-policy-ads-eula.png">
+</div>
+
+<div class="example-block bad" style="width:213px;margin-left:0em;">
+ <div class="heading">Disclosure is hidden</div>
+ <img src="{@docRoot}images/gp-policy-ads-eula-violation.png">
+</div>
+
+<p style="clear:right">
+ Above left is an example of ads disclosure that is hidden in a long EULA. The
+ disclosure information itself is not clearly indicated in the document text
+ and it's not visible unless the user happens to scroll down far enough in the
+ EULA. Above middle and right show two alternative approaches that
+ present the disclosure in an obvious and clear manner at the top of a
+ EULA and in a dedicated Terms agreement.
+</p>
+
+
+<h2 id="impersonation">Impersonation of System UI</h2>
+
+<div class="example-block bad">
+ <div class="heading">Ad impersonates system dialog</div>
+ <img src="{@docRoot}images/gp-policy-ads-impersonate-violation.png">
+</div>
+
+<p>
+ Your app must not display any ad that attempts to impersonate or represent a
+ system function or UI component. If such an ad is displayed in your app, your
+ app will be in violation of policy and subject to suspension. Here are some
+ guidelines:
+</p>
+
+<ul>
+ <li>
+ <strong>No fake system dialogs or warnings</strong>&mdash;Any ad that
+ presents itself as a system dialog or warning and asks for user input is in
+ violation of Google Play policies.
+ </li>
+
+ <li>
+ <strong>No fake app updates</strong>&mdash;Ads should not impersonate
+ system UI for app updates.
+ </li>
+</ul>
+
+<p>
+ At right is an example of a pop-up ad impersonating a system dialog, warning
+ the user about viruses. This is a violation of policy.
+</p>
+
+
+<h2 id="adwalls">Adwalls</h2>
+
+<div class="example-block good" style="width:213px;">
+ <div class="heading">Adwall lets user cancel</div>
+ <img src="{@docRoot}images/gp-policy-ads-paywall.png">
+</div>
+
+<div class="example-block bad" style="width:213px;">
+ <div class="heading">Adwall forces user action</div>
+ <img src="{@docRoot}images/gp-policy-ads-paywall-violation.png">
+</div>
+
+<p>
+ If your app uses adwalls to drive affiliate traffic, those adwalls must not
+ force the user to click on ads or submit personal information for advertising
+ purposes before using the app.
+</p>
+
+<p>
+ Forcing a user action in an adwall is not only a poor user experience, it is
+ a violation of Google Play policies.
+</p>
+
+<p>
+ For this reason, <strong>all adwalls must give the user the option to
+ cancel</strong> or otherwise dismiss the ad without penalty.
+</p>
+
+<p>
+ At right is an example of an app that requires the user to click through the
+ ad to fully use the app. This is a violation of policy.
+</p>
+
+<p>
+ The adjacent example demonstrates an adequate option to let the user dismiss
+ the ad wall easily by cancelling.
+</p>
+
+
+<h2 id="interfering" style="clear:right;">Interference with Third-party Ads and Websites</h2>
+
+<p>
+ Ads associated with your app <strong>must not interfere</strong> with any
+ other ads originating in other applications.
+</p> \ No newline at end of file
diff --git a/docs/html/distribute/googleplay/policies/index.jd b/docs/html/distribute/googleplay/policies/index.jd
new file mode 100644
index 0000000..fb46055
--- /dev/null
+++ b/docs/html/distribute/googleplay/policies/index.jd
@@ -0,0 +1,59 @@
+page.title=Google Play Policies and Guidelines
+page.metaDescription=Guidelines and tips for creating apps that comply with Google Play content and distribution policies.
+@jd:body
+
+<p>
+ Before publishing your apps on Google Play, take a few minutes to read and
+ understand the content and distribution policies that apply to all apps
+ in the store. These policies help to keep Android and Google Play an enjoyable
+ and trusted platform for content consumers and developers alike.
+</p>
+
+<p>
+ The documents below highlight important policy areas and provide tips to help
+ you create policy-compliant apps. You'll also find examples and guidance on common
+ policy questions that can help your app stay clear of practices that can result in
+ low ratings or even suspensions from the store.
+</p>
+
+<p>
+ For complete information about Google Play policies, please see the full
+ <a href="http://play.google.com/about/developer-content-policy.html" target=
+ "_policies">Developer Program Policies</a> and <a href=
+ "http://play.google.com/about/developer-distribution-agreement.html" target=
+ "_policies">Developer Distribution Agreement</a> documents.
+</p>
+
+<div class="vspace size-1">
+ &nbsp;
+</div>
+<div class="layout-content-row">
+ <div class="layout-content-col span-4">
+ <h4>
+ Spam
+ </h4>
+ <p>
+ Make sure that your app does not present content that is unwanted,
+ deceptive, repetitive, or unrelated to the core function of the app.
+ </p><a href="{@docRoot}distribute/googleplay/policies/spam.html">Learn more &raquo;</a>
+ </div>
+ <div class="layout-content-col span-4">
+ <h4>
+ Intellectual Property
+ </h4>
+ <p>
+ Tips and examples of how to use intelletual property (IP) properly,
+ including when to ask permission to use someone else's copyright or
+ trademark.
+ </p><a href="{@docRoot}distribute/googleplay/policies/ip.html">Learn more &raquo;</a>
+ </div>
+ <div class="layout-content-col span-4">
+ <h4>
+ Ads
+ </h4>
+ <p>
+ Make sure that the ads displayed in your app follow the Google Play Content
+ Policy and meet the maturity rating that you have selected for your app.
+ </p><a href="{@docRoot}distribute/googleplay/policies/ads.html">Learn more &raquo;</a>
+ </div>
+</div> \ No newline at end of file
diff --git a/docs/html/distribute/googleplay/policies/ip.jd b/docs/html/distribute/googleplay/policies/ip.jd
new file mode 100644
index 0000000..0d1f68d
--- /dev/null
+++ b/docs/html/distribute/googleplay/policies/ip.jd
@@ -0,0 +1,345 @@
+page.title=Intellectual Property
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+ <h2>In This Document</h2>
+ <ol>
+ <li><a href="#copyright">Copyright Infringement</a></li>
+ <li><a href="#impersonation">Impersonation</a></li>
+ <li><a href="#trademarks">Trademark Infringement</a></li>
+ <li><a href="#other">DDA 4.4 Prohibited Actions</a></li>
+ </ol>
+
+ <h2>More Resources</h2>
+ <ol>
+ <li><a href="http://play.google.com/about/developer-content-policy.html"
+ target="_policies">Developer Program Policies</a></li>
+ <li><a href="http://www.android.com/us/developer-distribution-agreement.html#showlanguages"
+ target="_policies">Developer Distribution Agreement</a></li>
+ </ol>
+</div>
+</div>
+
+<p>
+ Google Play policies protect your intellectual property (IP) as well as that
+ of other app developers and content creators in the store. The policies and
+ their enforcements help ensure proper use of copyright, trademarks, and
+ developer identity in Google Play.
+</p>
+
+<p>
+ As an app developer, these IP policies benefit you. At the same time, it's
+ your responsibility to ensure that your app does not violate the IP of other
+ developers or content creators. Violations of IP-related policy may result in
+ suspension of your apps from the store and termination of your developer
+ account.
+</p>
+
+<p>
+ This document introduces several key areas of IP-related policy that you
+ should understand before publishing on Google Play. In each area you'll find
+ best practices and examples to help you avoid common types of mistakes and
+ violations.
+</p>
+
+<p>
+ For more information about Google Play policies that apply to your apps and
+ content, please see the <a href=
+ "http://play.google.com/about/developer-content-policy.html" target=
+ "_policies">Developer Program Policies</a> and <a href=
+ "http://play.google.com/about/developer-distribution-agreement.html" target=
+ "_policies">Developer Distribution Agreement</a>.
+</p>
+
+
+
+<h2 id="copyright">Copyright Infringement</h2>
+
+<p>
+ Copyright is the legal right granted to an author or creator for a literary,
+ dramatic or artistic piece of work. As soon as you create an original piece
+ of work and fix it in a tangible medium, the work is automatically protected
+ by copyright law and you are the owner of the copyright. Likewise, when other
+ people create content, they may own the copyrights for those works.
+</p>
+
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h2>How to report infringements</h2>
+<p>If you feel your copyright is being infringed, you may file a Digital Millenium
+ Copyright Act (DMCA) request. Please see <a
+ href="http://support.google.com/bin/request.py?&product=androidmarket&contact_type=lr_dmca"
+ target="_policies">copyright procedures</a> for more information.</p>
+</div>
+</div>
+
+<p>
+ Copyright infringement is an improper or unauthorized use of a copyrighted
+ work. If you publish an app in Google Play that uses another party's copyrighted
+ works improperly or without permission, your apps can be suspended and your
+ developer account terminated.
+</p>
+
+<p>
+ As you design your app and prepare for publishing, make sure to review Google
+ Play policies and analyze all of your content. If your app uses or links to
+ another party's original work, make sure that your app is not infringing on
+ copyright. Not all uses of another party’s work are infringements on
+ copyright, and the rules vary by country and can be complex.
+</p>
+
+<p>
+ If you are unsure whether your use of another party's work infringes on a
+ copyright, consider getting legal advice before publishing, or simply request
+ permission to use the work from the copyright owner.
+</p>
+
+<p>
+ Here are some guidelines to help you avoid copyright infringement policy
+ violations:
+</p>
+
+<ul>
+ <li>
+ <strong>Respect copyright laws</strong>&mdash;Do not let your app infringe
+ on the copyrights of others. That includes linking to other apps or web
+ sites that contain obviously infringing material (please refer to the <a href="
+ {@docRoot}distribute/googleplay/policies/spam.html#webview-spam">Spam in WebViews</a> guidelines), and using icons or images that are obvious infringements.
+ </li>
+
+ <li>
+ <strong>Know your app's content</strong>&mdash;Before you publish, look
+ for content that may be protected by trademark or copyright in your app
+ and get legal advice if necessary. Protected work could typically include
+ product names, brands, images, music, and similar works.
+ </li>
+
+ <li>
+ <strong>Create original work</strong>&mdash;If you’re not sure whether
+ something will violate another party's copyright, the safest approach is to
+ create something that's completely original, such as images or audio
+ that you’ve created yourself. When you create your own original content,
+ you rarely have to worry about infringing on existing copyright.
+ </li>
+
+ <li>
+ <strong>Ask permission to use copyrighted work</strong>&mdash;If you want
+ to use another party's copyrighted work in your app, you should ask for
+ permission from the work's creator or copyright owner and include
+ appropriate copyright attribution.
+ </li>
+</ul>
+
+<p>
+ A common misunderstanding is believing that your app may use copyrighted
+ content without permission, provided that you clearly indicate that your app
+ is not the "official" app that readers may be familiar with. That is not the
+ case. Even if you let users know that your app is "unofficial", it still
+ violates Google Play policies if it uses or links to copyrighted content
+ without permission. Also, this type of "unofficial" app may violate <a
+ href="#impersonation">impersonation policies</a>.
+</p>
+
+<p>
+ The example app below shows an app that uses screenshots/images of known
+ artists without their authorization and lists popular songs. The combination
+ of these may induce users to download music ringtones that infringe on
+ copyright. This is a violation of Google Play policy.
+</p>
+
+<div class="example-block bad" style="width:100%;float:none;margin:.5em auto 2em 0;">
+ <div class="heading">Images and downloads that violate copyright</div>
+ <img src="{@docRoot}images/gp-policy-ip-copyright-violation.png">
+</div>
+
+
+<h2 id="impersonation">Impersonation</h2>
+
+<p>
+ Impersonation is when an app attempts to imply a relationship to another app
+ or developer, where no relationship actually exists.
+</p>
+
+<p>
+ For example, if your app displays the brand, icon, or title from another app
+ in order to get to users to download your app, you are leading users to
+ believe that your app is developed by the same entity as the other app and
+ offers similar content or experience. This is an impersonation of the other
+ app and developer, and it is a violation of Google Play policy. If you
+ publish apps that violate impersonation policies, your apps can be suspended
+ and your developer account terminated.
+</p>
+
+<p>
+ No matter what type of app you offer or what your motivation, don’t try to
+ imply an endorsement or relationship to another company or product where none
+ exists. Don’t try to establish your app as the "official" version of another
+ party's work by prominently featuring their brand names or trademarks in your
+ app title or description.
+</p>
+
+<p>
+ Even if your app description states that your app is an "unofficial" version,
+ the use of the other app's branding, trademarks, and other content still can
+ violate policy by presenting content that isn’t yours.
+</p>
+
+<p>
+ Here are some guidelines:
+</p>
+
+<ul>
+ <li>
+ <strong>Don't pretend to be someone else</strong>&mdash; Don't represent
+ that your content is produced by another company or organization if that is
+ not the case.
+ </li>
+
+ <li>
+ <strong>Don't support infringing sites or apps</strong>&mdash; Don't divert
+ users or provide links to any other site that mimics Google Play or
+ represents itself as another application or service.
+ </li>
+
+ <li>
+ <strong>Don't use another app's branding</strong>&mdash; Don’t try to pass
+ off your app as the official version of someone else’s property by using a
+ person or entity (or brand) name in your app title or description.
+ </li>
+</ul>
+
+<p>
+ Below is an example of an "unofficial" app that violates Google Play policy
+ by impersonating another company and an existing product. Specifically:
+</p>
+
+<ul>
+ <li>The example app has a name and icon that appear to be impersonating an
+ existing product.
+ </li>
+
+ <li>The example developer name implies an endorsement or relationship to
+ another company and their products where none exists.
+ </li>
+</ul>
+
+<div class="example-block bad" style="width:100%;float:none;margin:.5em auto 2em 0;">
+ <div class="heading">App name, icon, and developer name that impersonate another</div>
+ <img src="{@docRoot}images/gp-policy-ip-impersonation-violation.png">
+</div>
+
+
+<h2 id="trademarks">Trademark Infringement</h2>
+
+<p>
+ A trademark is a brand that uniquely identifies a product and distinguishes
+ it from other products. It can be a word, name, symbol, or combination of
+ those that is intended to identify the source of the product. A trademark is
+ specifically acquired by a company or other entity through a legal process
+ and once acquired gives the owner exclusive rights to the trademark usage.
+</p>
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h2>How to report infringements</h2>
+<p>If you feel your trademark is being infringed, you can request a content review.
+See <a href="http://support.google.com/bin/static.py?&ts=1114905&page=ts.cs"
+target="_policies">Removing content from Google</a> for more information.</p>
+</div>
+</div>
+
+<p>
+ Trademark infringement is improper or unauthorized use of a trademark. Google
+ Play policies prohibit apps that infringe trademarks. If you publish apps in
+ Google Play that use another party's trademarks, your apps can be suspended
+ and your developer account terminated.
+</p>
+
+<p>
+ As you design your app and prepare for publishing, make sure to review Google
+ Play policies and analyze all of your content. If your app uses a trademark
+ not owned by you, or if you are not sure whether a brand is a trademark, you
+ should get legal advice before publishing. As with copyright, the rules vary
+ by country and can be complex.
+</p>
+
+<p>
+ Here are some guidelines for avoiding trademark infringement policy
+ violations:
+</p>
+
+<ul>
+ <li>
+ <strong>Understand and follow trademark laws</strong>&mdash;Don't let your
+ app infringe on the trademarks of others.
+ </li>
+
+ <li>
+ <strong>Know your app's content</strong>&mdash;Before you publish, look for
+ brands and potential trademarks used in your app and store listing and get
+ legal advice if necessary.
+ </li>
+
+ <li>
+ <strong>Use a distinct name</strong>&mdash;Don't give your app a name that
+ is confusingly similar to another company's trademark.
+ </li>
+
+ <li>
+ <strong>Don't use trademarks to imply a relationship</strong>&mdash;Don't
+ describe your app using another company's trademarks in a way that implies
+ an endorsement by or affiliation with the other company.
+ </li>
+
+ <li>
+ <strong>Use a distinct app icon and logo</strong>&mdash;Don't use a
+ modified version of another company’s trademarked logo.
+ </li>
+</ul>
+
+<p>
+ A common misunderstanding is believing that your app may use a brand or
+ trademark without permission, provided you clearly indicate that the app is
+ not the "official" or original app. That is not the case. Even if you let
+ users know that your app is "unofficial", it still violates Google Play
+ policies if it uses another party's trademarks. Also, this type of
+ "unofficial" app may violate <a href="#impersonation">impersonation
+ policies</a>.
+</p>
+
+<p>
+ Below is an example app that violates Google Play policies by infringing on
+ another party's trademarks. Specifically:
+</p>
+
+<ul>
+ <li>The example app name is confusingly similar to another party's trademark.</li>
+ <li>The example app icon is a modified version of a another party's logo.</li>
+</ul>
+
+<div class="example-block bad" style="width:100%;float:none;margin:.5em auto 2em 0;">
+ <div class="heading">App name and icon that infringe trademarks</div>
+ <img src="{@docRoot}images/gp-policy-ip-trademark-violation.png">
+</div>
+
+
+<h2 id="other">DDA 4.4 Prohibited Actions</h2>
+
+<p>
+ When you publish an app on Google Play, you agree to the terms of the
+ Developer Distribution Agreement (DDA). Section 4.4 of the DDA prohibits certain
+ types of actions on your part. For reference, you agree that you will not
+ engage in any activity with the Market, including the development or
+ distribution of Products, that interferes with, disrupts, damages, or
+ accesses in an unauthorized manner the devices, servers, networks, or other
+ properties or services of any third party including, but not limited to,
+ Android users, Google or any mobile network operator.
+</p>
+
+<p>
+ For details, please refer to the complete <a href=
+ "http://play.google.com/about/developer-distribution-agreement.html" target=
+ "_policies">Developer Distribution Agreement</a>.
+</p> \ No newline at end of file
diff --git a/docs/html/distribute/googleplay/policies/spam.jd b/docs/html/distribute/googleplay/policies/spam.jd
new file mode 100644
index 0000000..602c89a
--- /dev/null
+++ b/docs/html/distribute/googleplay/policies/spam.jd
@@ -0,0 +1,421 @@
+page.title=Spam
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+ <h2>In This Document</h2>
+ <ol>
+ <li><a href="#keyword-spam">Spam in App Title and Description</a></li>
+ <li><a href="#ratings">Spam in Ratings and Reviews</a></li>
+ <li><a href="#webview-spam">Spam in WebViews</a></li>
+ <li><a href="#wizard-spam">Spam from Wizards</a></li>
+ <li><a href="#message-spam">Spam in Messaging</a></li>
+ </ol>
+
+ <h2>More Resources</h2>
+ <ol>
+ <li><a href="http://play.google.com/about/developer-content-policy.html" target="_policies">Developer Program Policies</a></li>
+ <li><a href="http://play.google.com/about/developer-distribution-agreement.html" target="_policies">Developer Distribution Agreement</a></li>
+ </ol>
+</div>
+</div>
+
+<p>
+ Google Play policies prohibit spam, to help ensure the best experience for
+ Android users. Please do not publish deceptive, repetitive, or irrelevant
+ content on Google Play. Not only will it lower your app's rating and cause
+ negative reviews, it can result in your app being suspended or your developer
+ account terminated.
+</p>
+
+<p>
+ As an app developer, it is your responsibility to ensure that your apps are
+ free from spam and conform to the Google Play policies highlighted in this
+ document. Before you publish, make sure that you understand what is
+ considered spam on Google Play and check your apps for violations, even those
+ that might be inadvertent. The sections below highlight best practices and
+ common spam examples to help you avoid the most common types of policy
+ violations.
+</p>
+
+<p>
+ For more information about Google Play policies that apply to your apps and
+ content, please see the <a href=
+ "http://play.google.com/about/developer-content-policy.html" target=
+ "_policies">Developer Program Policies</a> and <a href=
+ "http://play.google.com/about/developer-distribution-agreement.html" target=
+ "_policies">Developer Distribution Agreement</a>.
+</p>
+
+
+<h2 id="keyword-spam">Spam in App Title and Description</h2>
+
+<p>
+ When you publish an app on Google Play, you should pay special attention to
+ the app's title and description in its store listing. Those fields are
+ important because they make your app recognizable to users, and they help to
+ drive downloads by highlighting what's great about your app. A memorable
+ title and compelling description are essential to effective marketing, but
+ you should realize that these must follow Google Play policies, just as your
+ app content must do.
+</p>
+
+<p>
+ Many developers unknowingly violate spam policy in their app titles and
+ descriptions in ways that are easy to avoid. In general, you can
+ avoid spam violations in your app title and description by following these
+ best practices:
+</p>
+
+<ul>
+ <li>
+ <strong>Highlight what's great about your app</strong>&mdash;Share
+ interesting and exciting facts about your app with users. Help users
+ understand what makes your app special.
+ </li>
+
+ <li>
+ <strong>Describe your app accurately</strong>&mdash;Make sure the title
+ and description describe the app function and user experience accurately.
+ </li>
+
+ <li>
+ <strong>Don't use repetitive keywords</strong>&mdash;Avoid keywords that
+ are repetitive or excessive.
+ </li>
+
+ <li>
+ <strong>Don't include unrelated keywords or references</strong> &mdash;
+ Your description should not be loaded with irrelevant keywords in an
+ attempt to manipulate ranking or relevancy.
+ </li>
+
+ <li>
+ <strong>Keep it brief</strong>&mdash;Keep the description succinct and
+ straightforward. Shorter descriptions tend to give a better user experience
+ on devices with smaller displays. Excessive length, detail, or repetition
+ can violate spam policy.
+ </li>
+</ul>
+
+<p>
+ Here's an example app title and description that follows best practices and
+ does not violate Google Play spam policies.
+</p>
+
+<div class="example-block good" style="width:100%;float:none;margin:.5em auto 2em 0;">
+ <div class="heading">Best practice: App description</div>
+ <table>
+ <tr>
+ <td>App Title:</td>
+ <td>Kids puzzle: Identify Turtles</td>
+ </tr>
+ <tr>
+ <td style="white-space:nowrap;">App Description:</td>
+ <td>
+ <p>This is the perfect app to have a good time with your children. It
+ is designed to help kids learn different species of turtles through
+ cute pictures and amusing puzzle games.</p>
+ <p>The rules of Kids puzzle: Identify Turtles are quite simple. Have
+ your child drag images around the screen to fit them into the shaded
+ region. Phonics is also utilized, as a child can also tap the word
+ below the image and hear the name pronounced.</p>
+ </td>
+ </tr>
+ </table>
+</div>
+
+<p>
+ The sections below highlight common types of policy violations in an app
+ title and description, illustrated with variations on the best practice
+ example.
+</p>
+
+<h3 id="repetitive-keywords">Repetitive keywords</h3>
+
+<p>
+ Your app description should not include keywords that are repetitive or excessive.
+</p>
+
+<div class="example-block bad" style="width:100%;float:none;margin:.5em auto 2em 0;">
+ <div class="heading">Description includes repetitive keywords</div>
+ <table>
+ <tr>
+ <td>App Title:</td>
+ <td>Kids puzzle: Identify Turtles</td>
+ </tr>
+ <tr>
+ <td style="white-space:nowrap;">App Description:</td>
+ <td>
+ <p>This is the perfect app to have a good time with your children. It is
+ designed to help kids learn different species of turtles through cute
+ pictures and amusing puzzle games.</p>
+ <p>The rules of Kids puzzle: Identify Turtles are quite simple. Have your
+ child drag images around the screen to fit them into the shaded region.
+ Phonics is also utilized, as a child can also tap the word below the image
+ and hear the name pronounced.</p>
+ <p style="border:2px solid red;">KEYWORDS: game, games, fun, funny, child,
+ children, kid, kids, puzzle, puzzle games, sound, turtle, turtles, sea turtles,
+ turtles, turtle, turtles, tortoise, tortoises, tortoise, tortoise, turtles,
+ turtles, turtles, turtles, tortoises, tortoise</p>
+ </td>
+ </tr>
+ </table>
+</div>
+
+<h3 id="unrelated-keywords">Unrelated keywords or references</h3>
+
+<p>
+ The description should not be loaded with irrelevant keywords in an attempt
+ to manipulate ranking or relevancy in Google Play search results.
+</p>
+
+<p>
+ For example, if your app has nothing to do with Lady Gaga, then she shouldn’t
+ be included in your description. Also, do not add highly searched, irrelevant
+ keywords that are unrelated to the function of the app. This is in breach of
+ policy.
+</p>
+
+<div class="example-block bad" style="width:100%;float:none;margin:.5em auto 2em 0;">
+ <div class="heading">Description includes unrelated keywords or references</div>
+ <table>
+ <tr>
+ <td>App Title:</td>
+ <td>Kids puzzle: Identify Turtles</td>
+ </tr>
+ <tr>
+ <td style="white-space:nowrap;">App Description:</td>
+ <td>
+ <p>This is the perfect app to have a good time with your children. It is designed to
+ help kids learn different species of turtles through cute pictures and amusing puzzle
+ games.</p>
+ <p>The rules of Kids puzzle: Identify Turtles are quite simple. Have your child drag
+ images around the screen to fit them into the shaded region. Phonics is also utilized,
+ as a child can also tap the word below the image and hear the name pronounced.</p>
+ <p style="border:2px solid red;">This game is as addictive as Angry Birds, more social
+ than Facebook and Twitter, and has a soundtrack reminiscent of Katy Perry and Lady
+ Gaga.</p>
+ <p style="border:2px solid red;">KEYWORDS: Angry Birds, Facebook, Twitter, Katy Perry,
+ Lady Gaga</p>
+ </td>
+ </tr>
+ </table>
+</div>
+
+<h3 id="excessive-detail">Excessive detail, references to your other apps</h3>
+
+<p>
+ Your app description should avoid excessive detail and references to your
+ other apps or products. For example, you should not list all of the details
+ of content included in the app or its various components, as shown in the
+ example below. Also, the description should not include any references to
+ other apps you’ve published.
+</p>
+
+<div class="example-block bad" style="width:100%;float:none;margin:.5em auto 2em 0;">
+ <div class="heading">Description includes excessive detail, references to your other apps</div>
+ <table>
+ <tr>
+ <td>App Title:</td>
+ <td>Kids puzzle: Identify Turtles</td>
+ </tr>
+ <tr>
+ <td style="white-space:nowrap;">App Description:</td>
+ <td>
+ <p>This is the perfect app to have a good time with your children. It is designed
+ to help kids learn different species of turtles through cute pictures and amusing
+ puzzle games.</p>
+ <p>The rules of Kids puzzle: Identify Turtles are quite simple. Have your child
+ drag images around the screen to fit them into the shaded region. Phonics is also
+ utilized, as a child can also tap the word below the image and hear the name
+ pronounced.</p>
+ <p style="border:2px solid red;">Turtles included in the app: Alligator
+ Snapping Turtle, Asian Box Turtle, Bog Turtle, Common Musk Turtle, Common Snapping
+ Turtle, Diamondback Terrapin, Eastern Box Turtle, Eastern Mud Turtle, Eastern Painted
+ Turtle, False Map Turtle, Florida Pond Cooter, Florida Softshell Turtle, Green Sea
+ Turtle, Map Turtle, Matamata Ornate Box Turtle, Red-bellied Side-necked Turtle,
+ Red-eared Slider, Smooth Softshell Turtle, Spiny Softshell Turtle, Spotted Turtle,
+ Western Painted Turtle, Wood Turtle, Yellow-bellied Slider</p>
+ <p style="border:2px solid red;">If you like this app try our other free apps:<br />
+ ★ Fun Zoo<br />
+ ★ CD Guns<br />
+ ★ Dessert House<br />
+ ★ Playground<br />
+ ★ 578 Weapons</p>
+ </td>
+ </tr>
+ </table>
+</div>
+
+
+<h2 id="ratings">Spam in Ratings and Reviews</h2>
+
+<p>
+ Ratings and reviews are benchmarks of app quality and users depend on them to
+ be authentic and relevant. As an app developer, you should not attempt to
+ artificially influence your app's ratings and reviews or those of your
+ competitor, such as by posting fake ratings or reviews or including spam
+ content in app reviews. The sections below provide guidelines for rating and
+ reviewing apps.
+</p>
+
+<p>
+ So that you can stay in touch with any issues that users are having with your
+ app, you should read through your ratings and reviews on a regular basis. If
+ you choose to reply to reviews, make sure to keep your reply focused on the
+ actual issues raised in the user's comments and do not ask for a higher
+ rating.
+</p>
+
+<p>
+ If you see an app or developer reply that doesn’t follow these guidelines,
+ you can report it. See <a href=
+ "http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=113417&topic=2364761&ctx=topic"
+ target="_policies">Inappropriate content in comments and applications</a> for
+ more information.
+</p>
+
+<div class="example-block bad" style="width:440px;">
+ <div class="heading">Inappropriate content in a review</div>
+ <img src="{@docRoot}images/gp-policy-spam-negreview.png">
+</div>
+
+<div class="example-block bad" style="margin-top:3em;">
+ <div class="heading">Soliciting ratings</div>
+ <img src="{@docRoot}images/gp-policy-spam-reqrating.png">
+</div>
+
+<h3 id="fake-ratings">Fake or inappropriate ratings and reviews</h3>
+
+<p>
+ To help ensure the quality of ratings and reviews, Google Play policies limit
+ the ways that individuals can use ratings and reviews. In particular, note
+ that it is a violation of policy to use ratings and reviews to influence the
+ placement of any app in Google Play.
+</p>
+
+<p>
+ As an app developer, make sure that you follow these guidelines:
+</p>
+
+<ul>
+ <li>
+ <strong>Don't try to manipulate ratings</strong>&mdash;Do not engage in
+ attempts to manipulate the ratings, reviews, or ranking of your apps,
+ either directly or indirectly, or by manipulating the ratings of your
+ competitors. Do not attempt to artificially boost reviews, ratings, or
+ installs through any means.
+ </li>
+
+ <li>
+ <strong>Don't solicit ratings through incentives</strong>&mdash;Do not
+ offer users any incentives to rate your app, such as offering rewards of
+ any kind or tying app functionality to rating.
+ </li>
+
+ <li>
+ <strong>Don't rate apps multiple times</strong>&mdash;Do not review or
+ rate any app multiple times in an attempt to influence its placement in
+ Google Play.
+ </li>
+
+ <li>
+ <strong>Don't add improper content to reviews</strong>&mdash;Do not
+ include affiliate, coupon, game codes, email addresses, or links to
+ websites or other apps in your reviews. If you are responding to a user
+ review, feel free to include references to helpful resources such as a
+ support address or FAQ page.
+ </li>
+</ul>
+
+<h3 id="solicited-ratings">Soliciting ratings from users</h3>
+
+<p>
+ In general, <strong>do not offer incentives for ratings</strong>. You should
+ not offer users incentives of any kind for rating your app (or any other app)
+ on Google Play, and you should not tie your app's functionality or content to
+ rating in any way.
+</p>
+
+<p>
+ It's acceptable to ask users to rate your app without incentives, for
+ example: "If you like this game, rate us in Google Play!" On the other hand,
+ it's a policy violation to ask users to rate your app based on incentives,
+ for example: "Rate this app and get 500 coins" or "Rate this app 5 stars and
+ get you 500 coins!"
+</p>
+
+
+<h2 id="webview-spam" style="clear:right">Spam in WebViews</h2>
+
+<p>
+ Apps published on Google Play should provide their own content. Do not
+ publish an app whose primary function is to reproduce or frame someone else’s
+ website (unless you have permission).
+</p>
+
+<p>
+ Similarly, do not publish an app whose primary function is to drive affiliate
+ traffic to a website. Although affiliate deals can exist where an app's
+ primary purpose is delivering its own content or functionality, it's a
+ violation of Google Play policies to publish an app whose primary (or
+ only) purpose is to direct affiliate traffic to another website.
+</p>
+
+<div class="example-block bad" style="width:100%;float:none;margin:.5em auto 2em 0;">
+ <div class="heading">WebView spam</div>
+ <table>
+ <tr>
+ <td>App Title:</td>
+ <td>Kids puzzle: Desktop Browser for Turtoogle Game</td>
+ </tr>
+ <tr>
+ <td>Developer:</td>
+ <td>AAZZZ <span style="border:2px solid red;">(not affiliated with Turtoogle
+ Inc.)</span></td>
+ </tr>
+ <tr>
+ <td style="white-space:nowrap;">App Description:</td>
+ <td>
+ <p>Have you ever wanted to use the full, desktop web version of Turtoogle
+ Game from your phone or tablet instead of the Turtoogle Game mobile app
+ or Turtoogle Game mobile web site?</p>
+ <p style="border:2px solid red;">This app lets you access Turtoogle Game
+ on your Android device in the same way as you access the game on your
+ desktop computer, and with all the same Turtoogle Game features.</p>
+ </td>
+ </tr>
+ </table>
+</div>
+
+
+<h2 id="wizard-spam">Spam from Wizards</h2>
+
+<p>
+ Apps that are created by an automated tool or wizard service must not be
+ submitted to Google Play by the operator of that service on behalf of other
+ persons. Such tools often produce too many duplicative or low-quality
+ apps which crowd the higher-quality apps in the Play Store.
+</p>
+
+<p>
+ Please be advised that apps created by an automated tool are only permissible
+ if the app end-product complies with Google Play policies and is published in
+ the Play Store through a developer account that is registered and owned by
+ you.
+</p>
+
+
+<h2 id="message-spam">Spam in Messaging</h2>
+
+<p>
+ Your app may not send SMS, email, or other messages on behalf of the user
+ without providing the user with the ability to confirm the content and intended
+ recipient.
+</p>
+
+<p>
+ Google Play will aggressively remove applications that are found to send or
+ modify SMS messages without user knowledge or consent.
+</p> \ No newline at end of file
diff --git a/docs/html/images/example-bad.png b/docs/html/images/example-bad.png
new file mode 100644
index 0000000..b19a9f7
--- /dev/null
+++ b/docs/html/images/example-bad.png
Binary files differ
diff --git a/docs/html/images/example-good.png b/docs/html/images/example-good.png
new file mode 100644
index 0000000..6bd2408
--- /dev/null
+++ b/docs/html/images/example-good.png
Binary files differ
diff --git a/docs/html/images/gp-policy-ads-eula-violation.png b/docs/html/images/gp-policy-ads-eula-violation.png
new file mode 100644
index 0000000..e8ffa5b
--- /dev/null
+++ b/docs/html/images/gp-policy-ads-eula-violation.png
Binary files differ
diff --git a/docs/html/images/gp-policy-ads-eula.png b/docs/html/images/gp-policy-ads-eula.png
new file mode 100644
index 0000000..68a6b95
--- /dev/null
+++ b/docs/html/images/gp-policy-ads-eula.png
Binary files differ
diff --git a/docs/html/images/gp-policy-ads-impersonate-violation.png b/docs/html/images/gp-policy-ads-impersonate-violation.png
new file mode 100644
index 0000000..385ae6e
--- /dev/null
+++ b/docs/html/images/gp-policy-ads-impersonate-violation.png
Binary files differ
diff --git a/docs/html/images/gp-policy-ads-maturity-violation.png b/docs/html/images/gp-policy-ads-maturity-violation.png
new file mode 100644
index 0000000..d41870e
--- /dev/null
+++ b/docs/html/images/gp-policy-ads-maturity-violation.png
Binary files differ
diff --git a/docs/html/images/gp-policy-ads-notif-attr-violation.png b/docs/html/images/gp-policy-ads-notif-attr-violation.png
new file mode 100644
index 0000000..af53f10
--- /dev/null
+++ b/docs/html/images/gp-policy-ads-notif-attr-violation.png
Binary files differ
diff --git a/docs/html/images/gp-policy-ads-notif-attr.png b/docs/html/images/gp-policy-ads-notif-attr.png
new file mode 100644
index 0000000..4934d21
--- /dev/null
+++ b/docs/html/images/gp-policy-ads-notif-attr.png
Binary files differ
diff --git a/docs/html/images/gp-policy-ads-paywall-violation.png b/docs/html/images/gp-policy-ads-paywall-violation.png
new file mode 100644
index 0000000..8bbfd1b
--- /dev/null
+++ b/docs/html/images/gp-policy-ads-paywall-violation.png
Binary files differ
diff --git a/docs/html/images/gp-policy-ads-paywall.png b/docs/html/images/gp-policy-ads-paywall.png
new file mode 100644
index 0000000..e7b1e19
--- /dev/null
+++ b/docs/html/images/gp-policy-ads-paywall.png
Binary files differ
diff --git a/docs/html/images/gp-policy-ads-terms.png b/docs/html/images/gp-policy-ads-terms.png
new file mode 100644
index 0000000..dcbdf4a
--- /dev/null
+++ b/docs/html/images/gp-policy-ads-terms.png
Binary files differ
diff --git a/docs/html/images/gp-policy-ip-copyright-violation.png b/docs/html/images/gp-policy-ip-copyright-violation.png
new file mode 100644
index 0000000..a4e96a8
--- /dev/null
+++ b/docs/html/images/gp-policy-ip-copyright-violation.png
Binary files differ
diff --git a/docs/html/images/gp-policy-ip-impersonation-violation.png b/docs/html/images/gp-policy-ip-impersonation-violation.png
new file mode 100644
index 0000000..b1d9923
--- /dev/null
+++ b/docs/html/images/gp-policy-ip-impersonation-violation.png
Binary files differ
diff --git a/docs/html/images/gp-policy-ip-trademark-violation.png b/docs/html/images/gp-policy-ip-trademark-violation.png
new file mode 100644
index 0000000..c05b67b
--- /dev/null
+++ b/docs/html/images/gp-policy-ip-trademark-violation.png
Binary files differ
diff --git a/docs/html/images/gp-policy-spam-negreview.png b/docs/html/images/gp-policy-spam-negreview.png
new file mode 100644
index 0000000..f68eba3
--- /dev/null
+++ b/docs/html/images/gp-policy-spam-negreview.png
Binary files differ
diff --git a/docs/html/images/gp-policy-spam-reqrating.png b/docs/html/images/gp-policy-spam-reqrating.png
new file mode 100644
index 0000000..aaf9e53
--- /dev/null
+++ b/docs/html/images/gp-policy-spam-reqrating.png
Binary files differ
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 45385ee..fb5e039 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -103,14 +103,6 @@ public class KeyStore {
}
}
- public boolean put(String key, byte[] value, int uid) {
- return put(key, value, uid, FLAG_ENCRYPTED);
- }
-
- public boolean put(String key, byte[] value) {
- return put(key, value, UID_SELF);
- }
-
public boolean delete(String key, int uid) {
try {
return mBinder.del(key, uid) == NO_ERROR;
@@ -205,14 +197,6 @@ public class KeyStore {
}
}
- public boolean generate(String key, int uid) {
- return generate(key, uid, FLAG_ENCRYPTED);
- }
-
- public boolean generate(String key) {
- return generate(key, UID_SELF);
- }
-
public boolean importKey(String keyName, byte[] key, int uid, int flags) {
try {
return mBinder.import_key(keyName, key, uid, flags) == NO_ERROR;
@@ -222,14 +206,6 @@ public class KeyStore {
}
}
- public boolean importKey(String keyName, byte[] key, int uid) {
- return importKey(keyName, key, uid, FLAG_ENCRYPTED);
- }
-
- public boolean importKey(String keyName, byte[] key) {
- return importKey(keyName, key, UID_SELF);
- }
-
public byte[] getPubkey(String key) {
try {
return mBinder.get_pubkey(key);
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 46beb94..790c4f4 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -84,6 +84,9 @@
// Turn on to insert an event marker for each display list op
#define DEBUG_DISPLAY_LIST_OPS_AS_EVENTS 0
+// Turn on to highlight drawing batches and merged batches with different colors
+#define DEBUG_MERGE_BEHAVIOR 0
+
#if DEBUG_INIT
#define INIT_LOGD(...) ALOGD(__VA_ARGS__)
#else
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index d5007e1..f0084f2 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -23,6 +23,7 @@
#include "Caches.h"
#include "Debug.h"
+#include "DeferredDisplayList.h"
#include "DisplayListOp.h"
#include "OpenGLRenderer.h"
@@ -38,15 +39,27 @@ namespace uirenderer {
// Depth of the save stack at the beginning of batch playback at flush time
#define FLUSH_SAVE_STACK_DEPTH 2
+#define DEBUG_COLOR_BARRIER 0x1f000000
+#define DEBUG_COLOR_MERGEDBATCH 0x5f7f7fff
+#define DEBUG_COLOR_MERGEDBATCH_SOLO 0x5f7fff7f
+
/////////////////////////////////////////////////////////////////////////////////
// Operation Batches
/////////////////////////////////////////////////////////////////////////////////
-class DrawOpBatch {
+class Batch {
public:
- DrawOpBatch() { mOps.clear(); }
+ virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) = 0;
+ virtual ~Batch() {}
+};
+
+class DrawBatch : public Batch {
+public:
+ DrawBatch(int batchId, mergeid_t mergeId) : mBatchId(batchId), mMergeId(mergeId) {
+ mOps.clear();
+ }
- virtual ~DrawOpBatch() { mOps.clear(); }
+ virtual ~DrawBatch() { mOps.clear(); }
void add(DrawOp* op) {
// NOTE: ignore empty bounds special case, since we don't merge across those ops
@@ -54,7 +67,7 @@ public:
mOps.add(op);
}
- virtual bool intersects(Rect& rect) {
+ bool intersects(Rect& rect) {
if (!rect.intersects(mBounds)) return false;
for (unsigned int i = 0; i < mOps.size(); i++) {
@@ -71,8 +84,9 @@ public:
return false;
}
- virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty) {
- DEFER_LOGD("replaying draw batch %p", this);
+ virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
+ DEFER_LOGD("%d replaying DrawingBatch %p, with %d ops (batch id %x, merge id %p)",
+ index, this, mOps.size(), mOps[0]->getBatchId(), mOps[0]->getMergeId());
status_t status = DrawGlInfo::kStatusDone;
DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
@@ -84,31 +98,127 @@ public:
#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
renderer.eventMark(op->name());
#endif
- status |= op->applyDraw(renderer, dirty, 0);
+ status |= op->applyDraw(renderer, dirty);
logBuffer.writeCommand(0, op->name());
+
+#if DEBUG_MERGE_BEHAVIOR
+ Rect& bounds = mOps[i]->state.mBounds;
+ int batchColor = 0x1f000000;
+ if (getBatchId() & 0x1) batchColor |= 0x0000ff;
+ if (getBatchId() & 0x2) batchColor |= 0x00ff00;
+ if (getBatchId() & 0x4) batchColor |= 0xff0000;
+ renderer.drawScreenSpaceColorRect(bounds.left, bounds.top, bounds.right, bounds.bottom,
+ batchColor);
+#endif
}
return status;
}
+ inline int getBatchId() const { return mBatchId; }
+ inline mergeid_t getMergeId() const { return mMergeId; }
inline int count() const { return mOps.size(); }
-private:
+
+protected:
Vector<DrawOp*> mOps;
Rect mBounds;
+private:
+ int mBatchId;
+ mergeid_t mMergeId;
};
-class StateOpBatch : public DrawOpBatch {
+// compare alphas approximately, with a small margin
+#define NEQ_FALPHA(lhs, rhs) \
+ fabs((float)lhs - (float)rhs) > 0.001f
+
+class MergingDrawBatch : public DrawBatch {
public:
- // creates a single operation batch
- StateOpBatch(StateOp* op) : mOp(op) {}
+ MergingDrawBatch(int batchId, mergeid_t mergeId) : DrawBatch(batchId, mergeId) {}
- bool intersects(Rect& rect) {
- // if something checks for intersection, it's trying to go backwards across a state op,
- // something not currently supported - state ops are always barriers
- CRASH();
- return false;
+ /*
+ * Checks if a (mergeable) op can be merged into this batch
+ *
+ * If true, the op's multiDraw must be guaranteed to handle both ops simultaneously, so it is
+ * important to consider all paint attributes used in the draw calls in deciding both a) if an
+ * op tries to merge at all, and b) if the op
+ *
+ * False positives can lead to information from the paints of subsequent merged operations being
+ * dropped, so we make simplifying qualifications on the ops that can merge, per op type.
+ */
+ bool canMergeWith(DrawOp* op) {
+ if (!op->state.mMatrix.isPureTranslate()) return false;
+
+ bool isTextBatch = getBatchId() == DeferredDisplayList::kOpBatch_Text ||
+ getBatchId() == DeferredDisplayList::kOpBatch_ColorText;
+
+ // Overlapping other operations is only allowed for text without shadow. For other ops,
+ // multiDraw isn't guaranteed to overdraw correctly
+ if (!isTextBatch || op->state.mDrawModifiers.mHasShadow) {
+ if (intersects(op->state.mBounds)) return false;
+ }
+
+ const DeferredDisplayState& lhs = op->state;
+ const DeferredDisplayState& rhs = mOps[0]->state;
+
+ if (NEQ_FALPHA(lhs.mAlpha, rhs.mAlpha)) return false;
+
+ // if paints are equal, then modifiers + paint attribs don't need to be compared
+ if (op->mPaint == mOps[0]->mPaint) return true;
+
+ if (op->getPaintAlpha() != mOps[0]->getPaintAlpha()) return false;
+
+ /* Draw Modifiers compatibility check
+ *
+ * Shadows are ignored, as only text uses them, and in that case they are drawn
+ * per-DrawTextOp, before the unified text draw. Because of this, it's always safe to merge
+ * text UNLESS a later draw's shadow should overlays a previous draw's text. This is covered
+ * above with the intersection check.
+ *
+ * OverrideLayerAlpha is also ignored, as it's only used for drawing layers, which are never
+ * merged.
+ *
+ * These ignore cases prevent us from simply memcmp'ing the drawModifiers
+ */
+
+ const DrawModifiers& lhsMod = lhs.mDrawModifiers;
+ const DrawModifiers& rhsMod = rhs.mDrawModifiers;
+ if (lhsMod.mShader != rhsMod.mShader) return false;
+ if (lhsMod.mColorFilter != rhsMod.mColorFilter) return false;
+
+ // Draw filter testing expects bit fields to be clear if filter not set.
+ if (lhsMod.mHasDrawFilter != rhsMod.mHasDrawFilter) return false;
+ if (lhsMod.mPaintFilterClearBits != rhsMod.mPaintFilterClearBits) return false;
+ if (lhsMod.mPaintFilterSetBits != rhsMod.mPaintFilterSetBits) return false;
+
+ return true;
+ }
+
+ virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
+ DEFER_LOGD("%d replaying DrawingBatch %p, with %d ops (batch id %x, merge id %p)",
+ index, this, mOps.size(), getBatchId(), getMergeId());
+ if (mOps.size() == 1) {
+ return DrawBatch::replay(renderer, dirty, false);
+ }
+
+ DrawOp* op = mOps[0];
+ status_t status = op->multiDraw(renderer, dirty, mOps, mBounds);
+ DisplayListLogBuffer& buffer = DisplayListLogBuffer::getInstance();
+ buffer.writeCommand(0, "multiDraw");
+ buffer.writeCommand(1, op->name());
+
+#if DEBUG_MERGE_BEHAVIOR
+ renderer.drawScreenSpaceColorRect(mBounds.left, mBounds.top, mBounds.right, mBounds.bottom,
+ DEBUG_COLOR_MERGEDBATCH);
+#endif
+ return status;
}
+};
- virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty) {
+class StateOpBatch : public Batch {
+public:
+ // creates a single operation batch
+ StateOpBatch(StateOp* op) : mOp(op) {}
+
+ virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
DEFER_LOGD("replaying state op batch %p", this);
renderer.restoreDisplayState(mOp->state);
@@ -124,18 +234,11 @@ private:
const StateOp* mOp;
};
-class RestoreToCountBatch : public DrawOpBatch {
+class RestoreToCountBatch : public Batch {
public:
RestoreToCountBatch(StateOp* op, int restoreCount) : mOp(op), mRestoreCount(restoreCount) {}
- bool intersects(Rect& rect) {
- // if something checks for intersection, it's trying to go backwards across a state op,
- // something not currently supported - state ops are always barriers
- CRASH();
- return false;
- }
-
- virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty) {
+ virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
DEFER_LOGD("batch %p restoring to count %d", this, mRestoreCount);
renderer.restoreDisplayState(mOp->state);
@@ -155,14 +258,30 @@ private:
const int mRestoreCount;
};
+#if DEBUG_MERGE_BEHAVIOR
+class BarrierDebugBatch : public Batch {
+ virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
+ renderer.drawScreenSpaceColorRect(0, 0, 10000, 10000, DEBUG_COLOR_BARRIER);
+ return DrawGlInfo::kStatusDrew;
+ }
+};
+#endif
+
/////////////////////////////////////////////////////////////////////////////////
// DeferredDisplayList
/////////////////////////////////////////////////////////////////////////////////
void DeferredDisplayList::resetBatchingState() {
for (int i = 0; i < kOpBatch_Count; i++) {
- mBatchIndices[i] = -1;
+ mBatchLookup[i] = NULL;
+ mMergingBatches[i].clear();
+ }
+#if DEBUG_MERGE_BEHAVIOR
+ if (mBatches.size() != 0) {
+ mBatches.add(new BarrierDebugBatch());
}
+#endif
+ mEarliestBatchIndex = mBatches.size();
}
void DeferredDisplayList::clear() {
@@ -174,6 +293,7 @@ void DeferredDisplayList::clear() {
}
mBatches.clear();
mSaveStack.clear();
+ mEarliestBatchIndex = 0;
}
/////////////////////////////////////////////////////////////////////////////////
@@ -282,28 +402,35 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
return; // quick rejected
}
- op->onDrawOpDeferred(renderer);
+ int batchId = kOpBatch_None;
+ mergeid_t mergeId = (mergeid_t) -1;
+ bool mergeable = op->onDefer(renderer, &batchId, &mergeId);
+
+ // complex clip has a complex set of expectations on the renderer state - for now, avoid taking
+ // the merge path in those cases
+ mergeable &= !recordingComplexClip();
if (CC_UNLIKELY(renderer.getCaches().drawReorderDisabled)) {
// TODO: elegant way to reuse batches?
- DrawOpBatch* b = new DrawOpBatch();
+ DrawBatch* b = new DrawBatch(batchId, mergeId);
b->add(op);
mBatches.add(b);
return;
}
- // disallowReorder isn't set, so find the latest batch of the new op's type, and try to merge
- // the new op into it
- DrawOpBatch* targetBatch = NULL;
- int batchId = op->getBatchId();
+ // find the latest batch of the new op's type, and try to merge the new op into it
+ DrawBatch* targetBatch = NULL;
+ // insertion point of a new batch, will hopefully be immediately after similar batch
+ // (eventually, should be similar shader)
+ int insertBatchIndex = mBatches.size();
if (!mBatches.isEmpty()) {
if (op->state.mBounds.isEmpty()) {
// don't know the bounds for op, so add to last batch and start from scratch on next op
- mBatches.top()->add(op);
- for (int i = 0; i < kOpBatch_Count; i++) {
- mBatchIndices[i] = -1;
- }
+ DrawBatch* b = new DrawBatch(batchId, mergeId);
+ b->add(op);
+ mBatches.add(b);
+ resetBatchingState();
#if DEBUG_DEFER
DEFER_LOGD("Warning: Encountered op with empty bounds, resetting batches");
op->output(2);
@@ -311,13 +438,36 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
return;
}
- if (batchId >= 0 && mBatchIndices[batchId] != -1) {
- int targetIndex = mBatchIndices[batchId];
- targetBatch = mBatches[targetIndex];
+ if (mergeable) {
+ // Try to merge with any existing batch with same mergeId.
+ if (mMergingBatches[batchId].get(mergeId, targetBatch)) {
+ if (!((MergingDrawBatch*) targetBatch)->canMergeWith(op)) {
+ targetBatch = NULL;
+ }
+ }
+ } else {
+ // join with similar, non-merging batch
+ targetBatch = (DrawBatch*)mBatchLookup[batchId];
+ }
+
+ if (targetBatch || mergeable) {
// iterate back toward target to see if anything drawn since should overlap the new op
- for (int i = mBatches.size() - 1; i > targetIndex; i--) {
- DrawOpBatch* overBatch = mBatches[i];
+ // if no target, merging ops still interate to find similar batch to insert after
+ for (int i = mBatches.size() - 1; i >= mEarliestBatchIndex; i--) {
+ DrawBatch* overBatch = (DrawBatch*)mBatches[i];
+
+ if (overBatch == targetBatch) break;
+
+ // TODO: also consider shader shared between batch types
+ if (batchId == overBatch->getBatchId()) {
+ insertBatchIndex = i + 1;
+ if (!targetBatch) break; // found insert position, quit
+ }
+
if (overBatch->intersects(op->state.mBounds)) {
+ // NOTE: it may be possible to optimize for special cases where two operations
+ // of the same batch/paint could swap order, such as with a non-mergeable
+ // (clipped) and a mergeable text operation
targetBatch = NULL;
#if DEBUG_DEFER
DEFER_LOGD("op couldn't join batch %d, was intersected by batch %d",
@@ -329,13 +479,21 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
}
}
}
+
if (!targetBatch) {
- targetBatch = new DrawOpBatch();
- mBatches.add(targetBatch);
- if (batchId >= 0) {
- mBatchIndices[batchId] = mBatches.size() - 1;
+ if (mergeable) {
+ targetBatch = new MergingDrawBatch(batchId, mergeId);
+ mMergingBatches[batchId].put(mergeId, targetBatch);
+ } else {
+ targetBatch = new DrawBatch(batchId, mergeId);
+ mBatchLookup[batchId] = targetBatch;
+ DEFER_LOGD("creating Batch %p, bid %x, at %d",
+ targetBatch, batchId, insertBatchIndex);
}
+
+ mBatches.insertAt(targetBatch, insertBatchIndex);
}
+
targetBatch->add(op);
}
@@ -363,16 +521,14 @@ void DeferredDisplayList::storeRestoreToCountBarrier(OpenGLRenderer& renderer, S
// Replay / flush
/////////////////////////////////////////////////////////////////////////////////
-static status_t replayBatchList(Vector<DrawOpBatch*>& batchList,
+static status_t replayBatchList(const Vector<Batch*>& batchList,
OpenGLRenderer& renderer, Rect& dirty) {
status_t status = DrawGlInfo::kStatusDone;
- int opCount = 0;
for (unsigned int i = 0; i < batchList.size(); i++) {
- status |= batchList[i]->replay(renderer, dirty);
- opCount += batchList[i]->count();
+ status |= batchList[i]->replay(renderer, dirty, i);
}
- DEFER_LOGD("--flushed, drew %d batches (total %d ops)", batchList.size(), opCount);
+ DEFER_LOGD("--flushed, drew %d batches", batchList.size());
return status;
}
@@ -400,7 +556,6 @@ status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) {
renderer.setDrawModifiers(restoreDrawModifiers);
DEFER_LOGD("--flush complete, returning %x", status);
-
clear();
return status;
}
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index 653f315..9782c1c 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -22,6 +22,9 @@
#include "Matrix.h"
#include "Rect.h"
+#include "utils/TinyHashMap.h"
+
+class SkBitmap;
namespace android {
namespace uirenderer {
@@ -31,16 +34,21 @@ class DrawOp;
class SaveOp;
class SaveLayerOp;
class StateOp;
-class DrawOpBatch;
class OpenGLRenderer;
+class Batch;
+class DrawBatch;
+class MergingDrawBatch;
+
+typedef void* mergeid_t;
+
class DeferredDisplayList {
public:
DeferredDisplayList() { clear(); }
~DeferredDisplayList() { clear(); }
enum OpBatchId {
- kOpBatch_None = -1, // Don't batch
+ kOpBatch_None = 0, // Don't batch
kOpBatch_Bitmap,
kOpBatch_Patch,
kOpBatch_AlphaVertices,
@@ -96,8 +104,20 @@ private:
Vector<int> mSaveStack;
int mComplexClipStackStart;
- Vector<DrawOpBatch*> mBatches;
- int mBatchIndices[kOpBatch_Count];
+ Vector<Batch*> mBatches;
+
+ // Maps batch ids to the most recent *non-merging* batch of that id
+ Batch* mBatchLookup[kOpBatch_Count];
+
+ // Points to the index after the most recent barrier
+ int mEarliestBatchIndex;
+
+ /**
+ * Maps the mergeid_t returned by an op's getMergeId() to the most recently seen
+ * MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not
+ * collide, which avoids the need to resolve mergeid collisions.
+ */
+ TinyHashMap<mergeid_t, DrawBatch*> mMergingBatches[kOpBatch_Count];
};
}; // namespace uirenderer
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 36c95f9..26abec2 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -485,7 +485,7 @@ void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level)
#if DEBUG_DISPLAY_LIST
Rect* clipRect = renderer.getClipRect();
- DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.f, %.0f, %.0f",
+ DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.0f, %.0f, %.0f",
level * 2, "", this, mName.string(), clipRect->left, clipRect->top,
clipRect->right, clipRect->bottom);
#endif
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index a5dee9f..ad7edb1 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -121,6 +121,7 @@ public:
};
class DrawOp : public DisplayListOp {
+friend class MergingDrawBatch;
public:
DrawOp(SkPaint* paint)
: mPaint(paint), mQuickRejected(false) {}
@@ -145,12 +146,41 @@ public:
return;
}
- replayStruct.mDrawGlStatus |= applyDraw(replayStruct.mRenderer, replayStruct.mDirty, level);
+ replayStruct.mDrawGlStatus |= applyDraw(replayStruct.mRenderer, replayStruct.mDirty);
}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) = 0;
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) = 0;
- virtual void onDrawOpDeferred(OpenGLRenderer& renderer) {
+ /**
+ * Draw multiple instances of an operation, must be overidden for operations that merge
+ *
+ * Currently guarantees certain similarities between ops (see MergingDrawBatch::canMergeWith),
+ * and pure translation transformations. Other guarantees of similarity should be enforced by
+ * reducing which operations are tagged as mergeable.
+ */
+ virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty,
+ const Vector<DrawOp*>& ops, const Rect& bounds) {
+ status_t status = DrawGlInfo::kStatusDone;
+ for (unsigned int i = 0; i < ops.size(); i++) {
+ renderer.restoreDisplayState(ops[i]->state);
+ status |= ops[i]->applyDraw(renderer, dirty);
+ }
+ return status;
+ }
+
+ /*
+ * When this method is invoked the state field is initialized to have the
+ * final rendering state. We can thus use it to process data as it will be
+ * used at draw time.
+ *
+ * Additionally, this method allows subclasses to provide defer-time preferences for batching
+ * and merging.
+ *
+ * Return true if the op can merge with others of its kind (such subclasses should implement
+ * multiDraw)
+ */
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ return false;
}
// returns true if bounds exist
@@ -160,12 +190,11 @@ public:
void setQuickRejected(bool quickRejected) { mQuickRejected = quickRejected; }
bool getQuickRejected() { return mQuickRejected; }
- /** Batching disabled by default, turned on for individual ops */
- virtual DeferredDisplayList::OpBatchId getBatchId() {
- return DeferredDisplayList::kOpBatch_None;
+ inline int getPaintAlpha() {
+ return OpenGLRenderer::getAlphaDirect(mPaint);
}
- float strokeWidthOutset() {
+ inline float strokeWidthOutset() {
float width = mPaint->getStrokeWidth();
if (width == 0) return 0.5f; // account for hairline
return width * 0.5f;
@@ -207,6 +236,14 @@ public:
return true;
}
+ bool mergeAllowed() {
+ // checks that we're unclipped, and srcover
+ const Rect& opBounds = state.mBounds;
+ return fabs(opBounds.getWidth() - mLocalBounds.getWidth()) < 0.1 &&
+ fabs(opBounds.getHeight() - mLocalBounds.getHeight()) < 0.1 &&
+ (OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode);
+ }
+
protected:
Rect mLocalBounds; // displayed area in LOCAL coord. doesn't incorporate stroke, so check paint
};
@@ -686,20 +723,58 @@ public:
paint),
mBitmap(bitmap) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawBitmap(mBitmap, mLocalBounds.left, mLocalBounds.top,
getPaint(renderer));
}
+#define SET_TEXTURE(ptr, posRect, offsetRect, texCoordsRect, xDim, yDim) \
+ TextureVertex::set(ptr++, posRect.xDim - offsetRect.left, posRect.yDim - offsetRect.top, \
+ texCoordsRect.xDim, texCoordsRect.yDim)
+
+ virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty,
+ const Vector<DrawOp*>& ops, const Rect& bounds) {
+ renderer.restoreDisplayState(state, true); // restore all but the clip
+ renderer.setFullScreenClip(); // ensure merged ops aren't clipped
+ TextureVertex vertices[6 * ops.size()];
+ TextureVertex* vertex = &vertices[0];
+
+ // TODO: manually handle rect clip for bitmaps by adjusting texCoords per op, and allowing
+ // them to be merged in getBatchId()
+ const Rect texCoords(0, 0, 1, 1);
+
+ const float width = mBitmap->width();
+ const float height = mBitmap->height();
+ for (unsigned int i = 0; i < ops.size(); i++) {
+ const Rect& opBounds = ops[i]->state.mBounds;
+ SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, top);
+ SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, top);
+ SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, bottom);
+
+ SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, bottom);
+ SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, top);
+ SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, bottom);
+ }
+
+ return renderer.drawBitmaps(mBitmap, ops.size(), &vertices[0], bounds, mPaint);
+ }
+
virtual void output(int level, uint32_t logFlags) {
OP_LOG("Draw bitmap %p at %f %f", mBitmap, mLocalBounds.left, mLocalBounds.top);
}
virtual const char* name() { return "DrawBitmap"; }
- virtual DeferredDisplayList::OpBatchId getBatchId() {
- return DeferredDisplayList::kOpBatch_Bitmap;
+
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ *batchId = DeferredDisplayList::kOpBatch_Bitmap;
+ *mergeId = (mergeid_t)mBitmap;
+
+ // don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in
+ // MergingDrawBatch::canMergeWith
+ return mergeAllowed() && (mBitmap->getConfig() != SkBitmap::kA8_Config);
}
+ const SkBitmap* bitmap() { return mBitmap; }
protected:
SkBitmap* mBitmap;
};
@@ -713,7 +788,7 @@ public:
transform.mapRect(mLocalBounds);
}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawBitmap(mBitmap, mMatrix, getPaint(renderer));
}
@@ -721,9 +796,11 @@ public:
OP_LOG("Draw bitmap %p matrix " MATRIX_STRING, mBitmap, MATRIX_ARGS(mMatrix));
}
- virtual const char* name() { return "DrawBitmap"; }
- virtual DeferredDisplayList::OpBatchId getBatchId() {
- return DeferredDisplayList::kOpBatch_Bitmap;
+ virtual const char* name() { return "DrawBitmapMatrix"; }
+
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ *batchId = DeferredDisplayList::kOpBatch_Bitmap;
+ return false;
}
private:
@@ -738,7 +815,7 @@ public:
: DrawBoundedOp(dstLeft, dstTop, dstRight, dstBottom, paint),
mBitmap(bitmap), mSrc(srcLeft, srcTop, srcRight, srcBottom) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawBitmap(mBitmap, mSrc.left, mSrc.top, mSrc.right, mSrc.bottom,
mLocalBounds.left, mLocalBounds.top, mLocalBounds.right, mLocalBounds.bottom,
getPaint(renderer));
@@ -750,8 +827,10 @@ public:
}
virtual const char* name() { return "DrawBitmapRect"; }
- virtual DeferredDisplayList::OpBatchId getBatchId() {
- return DeferredDisplayList::kOpBatch_Bitmap;
+
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ *batchId = DeferredDisplayList::kOpBatch_Bitmap;
+ return false;
}
private:
@@ -764,7 +843,7 @@ public:
DrawBitmapDataOp(SkBitmap* bitmap, float left, float top, SkPaint* paint)
: DrawBitmapOp(bitmap, left, top, paint) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawBitmapData(mBitmap, mLocalBounds.left,
mLocalBounds.top, getPaint(renderer));
}
@@ -774,8 +853,10 @@ public:
}
virtual const char* name() { return "DrawBitmapData"; }
- virtual DeferredDisplayList::OpBatchId getBatchId() {
- return DeferredDisplayList::kOpBatch_Bitmap;
+
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ *batchId = DeferredDisplayList::kOpBatch_Bitmap;
+ return false;
}
};
@@ -787,7 +868,7 @@ public:
mBitmap(bitmap), mMeshWidth(meshWidth), mMeshHeight(meshHeight),
mVertices(vertices), mColors(colors) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawBitmapMesh(mBitmap, mMeshWidth, mMeshHeight,
mVertices, mColors, getPaint(renderer));
}
@@ -797,8 +878,10 @@ public:
}
virtual const char* name() { return "DrawBitmapMesh"; }
- virtual DeferredDisplayList::OpBatchId getBatchId() {
- return DeferredDisplayList::kOpBatch_Bitmap;
+
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ *batchId = DeferredDisplayList::kOpBatch_Bitmap;
+ return false;
}
private:
@@ -820,7 +903,7 @@ public:
mColors(colors), mxDivsCount(width), myDivsCount(height),
mNumColors(numColors), mAlpha(alpha), mMode(mode) {};
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
// NOTE: not calling the virtual method, which takes a paint
return renderer.drawPatch(mBitmap, mxDivs, myDivs, mColors,
mxDivsCount, myDivsCount, mNumColors,
@@ -833,8 +916,11 @@ public:
}
virtual const char* name() { return "DrawPatch"; }
- virtual DeferredDisplayList::OpBatchId getBatchId() {
- return DeferredDisplayList::kOpBatch_Patch;
+
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ *batchId = DeferredDisplayList::kOpBatch_Patch;
+ *mergeId = (mergeid_t)mBitmap;
+ return true;
}
private:
@@ -854,7 +940,7 @@ public:
DrawColorOp(int color, SkXfermode::Mode mode)
: DrawOp(0), mColor(color), mMode(mode) {};
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawColor(mColor, mMode);
}
@@ -882,13 +968,15 @@ public:
return true;
}
- virtual DeferredDisplayList::OpBatchId getBatchId() {
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
if (mPaint->getPathEffect()) {
- return DeferredDisplayList::kOpBatch_AlphaMaskTexture;
+ *batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture;
+ } else {
+ *batchId = mPaint->isAntiAlias() ?
+ DeferredDisplayList::kOpBatch_AlphaVertices :
+ DeferredDisplayList::kOpBatch_Vertices;
}
- return mPaint->isAntiAlias() ?
- DeferredDisplayList::kOpBatch_AlphaVertices :
- DeferredDisplayList::kOpBatch_Vertices;
+ return false;
}
};
@@ -897,7 +985,7 @@ public:
DrawRectOp(float left, float top, float right, float bottom, SkPaint* paint)
: DrawStrokableOp(left, top, right, bottom, paint) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawRect(mLocalBounds.left, mLocalBounds.top,
mLocalBounds.right, mLocalBounds.bottom, getPaint(renderer));
}
@@ -915,7 +1003,7 @@ public:
: DrawBoundedOp(rects, count, paint),
mRects(rects), mCount(count) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawRects(mRects, mCount, getPaint(renderer));
}
@@ -925,8 +1013,9 @@ public:
virtual const char* name() { return "DrawRects"; }
- virtual DeferredDisplayList::OpBatchId getBatchId() {
- return DeferredDisplayList::kOpBatch_Vertices;
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ *batchId = DeferredDisplayList::kOpBatch_Vertices;
+ return false;
}
private:
@@ -940,7 +1029,7 @@ public:
float rx, float ry, SkPaint* paint)
: DrawStrokableOp(left, top, right, bottom, paint), mRx(rx), mRy(ry) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawRoundRect(mLocalBounds.left, mLocalBounds.top,
mLocalBounds.right, mLocalBounds.bottom, mRx, mRy, getPaint(renderer));
}
@@ -962,7 +1051,7 @@ public:
: DrawStrokableOp(x - radius, y - radius, x + radius, y + radius, paint),
mX(x), mY(y), mRadius(radius) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawCircle(mX, mY, mRadius, getPaint(renderer));
}
@@ -983,7 +1072,7 @@ public:
DrawOvalOp(float left, float top, float right, float bottom, SkPaint* paint)
: DrawStrokableOp(left, top, right, bottom, paint) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawOval(mLocalBounds.left, mLocalBounds.top,
mLocalBounds.right, mLocalBounds.bottom, getPaint(renderer));
}
@@ -1002,7 +1091,7 @@ public:
: DrawStrokableOp(left, top, right, bottom, paint),
mStartAngle(startAngle), mSweepAngle(sweepAngle), mUseCenter(useCenter) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawArc(mLocalBounds.left, mLocalBounds.top,
mLocalBounds.right, mLocalBounds.bottom,
mStartAngle, mSweepAngle, mUseCenter, getPaint(renderer));
@@ -1033,13 +1122,16 @@ public:
mLocalBounds.set(left, top, left + width, top + height);
}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawPath(mPath, getPaint(renderer));
}
- virtual void onDrawOpDeferred(OpenGLRenderer& renderer) {
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
SkPaint* paint = getPaint(renderer);
renderer.getCaches().pathCache.precache(mPath, paint);
+
+ *batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture;
+ return false;
}
virtual void output(int level, uint32_t logFlags) {
@@ -1048,9 +1140,6 @@ public:
virtual const char* name() { return "DrawPath"; }
- virtual DeferredDisplayList::OpBatchId getBatchId() {
- return DeferredDisplayList::kOpBatch_AlphaMaskTexture;
- }
private:
SkPath* mPath;
};
@@ -1063,7 +1152,7 @@ public:
mLocalBounds.outset(strokeWidthOutset());
}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawLines(mPoints, mCount, getPaint(renderer));
}
@@ -1073,10 +1162,11 @@ public:
virtual const char* name() { return "DrawLines"; }
- virtual DeferredDisplayList::OpBatchId getBatchId() {
- return mPaint->isAntiAlias() ?
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ *batchId = mPaint->isAntiAlias() ?
DeferredDisplayList::kOpBatch_AlphaVertices :
DeferredDisplayList::kOpBatch_Vertices;
+ return false;
}
protected:
@@ -1089,7 +1179,7 @@ public:
DrawPointsOp(float* points, int count, SkPaint* paint)
: DrawLinesOp(points, count, paint) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawPoints(mPoints, mCount, getPaint(renderer));
}
@@ -1109,17 +1199,18 @@ public:
OP_LOG("Draw some text, %d bytes", mBytesCount);
}
- virtual void onDrawOpDeferred(OpenGLRenderer& renderer) {
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
SkPaint* paint = getPaint(renderer);
FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint);
fontRenderer.precache(paint, mText, mCount, mat4::identity());
- }
- virtual DeferredDisplayList::OpBatchId getBatchId() {
- return mPaint->getColor() == 0xff000000 ?
+ *batchId = mPaint->getColor() == 0xff000000 ?
DeferredDisplayList::kOpBatch_Text :
DeferredDisplayList::kOpBatch_ColorText;
+
+ return false;
}
+
protected:
const char* mText;
int mBytesCount;
@@ -1135,7 +1226,7 @@ public:
/* TODO: inherit from DrawBounded and init mLocalBounds */
}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawTextOnPath(mText, mBytesCount, mCount, mPath,
mHOffset, mVOffset, getPaint(renderer));
}
@@ -1156,7 +1247,7 @@ public:
/* TODO: inherit from DrawBounded and init mLocalBounds */
}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawPosText(mText, mBytesCount, mCount, mPositions, getPaint(renderer));
}
@@ -1189,12 +1280,7 @@ public:
memset(&mPrecacheTransform.data[0], 0xff, 16 * sizeof(float));
}
- /*
- * When this method is invoked the state field is initialized to have the
- * final rendering state. We can thus use it to process data as it will be
- * used at draw time.
- */
- virtual void onDrawOpDeferred(OpenGLRenderer& renderer) {
+ virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
SkPaint* paint = getPaint(renderer);
FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint);
const mat4& transform = renderer.findBestFontTransform(state.mMatrix);
@@ -1202,25 +1288,44 @@ public:
fontRenderer.precache(paint, mText, mCount, transform);
mPrecacheTransform = transform;
}
+ *batchId = mPaint->getColor() == 0xff000000 ?
+ DeferredDisplayList::kOpBatch_Text :
+ DeferredDisplayList::kOpBatch_ColorText;
+
+ *mergeId = (mergeid_t)mPaint->getColor();
+
+ // don't merge decorated text - the decorations won't draw in order
+ bool noDecorations = !(mPaint->getFlags() & (SkPaint::kUnderlineText_Flag |
+ SkPaint::kStrikeThruText_Flag));
+ return mergeAllowed() && noDecorations;
}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawText(mText, mBytesCount, mCount, mX, mY,
mPositions, getPaint(renderer), mLength);
}
+ virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty,
+ const Vector<DrawOp*>& ops, const Rect& bounds) {
+ status_t status = DrawGlInfo::kStatusDone;
+ renderer.setFullScreenClip(); // ensure merged ops aren't clipped
+ for (unsigned int i = 0; i < ops.size(); i++) {
+ DrawOpMode drawOpMode = (i == ops.size() - 1) ? kDrawOpMode_Flush : kDrawOpMode_Defer;
+ renderer.restoreDisplayState(ops[i]->state, true); // restore all but the clip
+
+ DrawTextOp& op = *((DrawTextOp*)ops[i]);
+ status |= renderer.drawText(op.mText, op.mBytesCount, op.mCount, op.mX, op.mY,
+ op.mPositions, op.getPaint(renderer), op.mLength, drawOpMode);
+ }
+ return status;
+ }
+
virtual void output(int level, uint32_t logFlags) {
OP_LOG("Draw Text of count %d, bytes %d", mCount, mBytesCount);
}
virtual const char* name() { return "DrawText"; }
- virtual DeferredDisplayList::OpBatchId getBatchId() {
- return mPaint->getColor() == 0xff000000 ?
- DeferredDisplayList::kOpBatch_Text :
- DeferredDisplayList::kOpBatch_ColorText;
- }
-
private:
const char* mText;
int mBytesCount;
@@ -1241,7 +1346,7 @@ public:
DrawFunctorOp(Functor* functor)
: DrawOp(0), mFunctor(functor) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
renderer.startMark("GL functor");
status_t ret = renderer.callDrawGLFunction(mFunctor, dirty);
renderer.endMark();
@@ -1269,14 +1374,14 @@ public:
mDisplayList->defer(deferStruct, level + 1);
}
}
-virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level) {
+ virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level) {
if (mDisplayList && mDisplayList->isRenderable()) {
mDisplayList->replay(replayStruct, level + 1);
}
}
// NOT USED since replay() is overridden
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return DrawGlInfo::kStatusDone;
}
@@ -1299,7 +1404,7 @@ public:
DrawLayerOp(Layer* layer, float x, float y)
: DrawOp(0), mLayer(layer), mX(x), mY(y) {}
- virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, int level) {
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawLayer(mLayer, mX, mY);
}
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 0b8f7e6..876c38a 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -276,6 +276,15 @@ status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float
bitmap = refBitmap(bitmap);
paint = refPaint(paint);
+ if (srcLeft == 0 && srcTop == 0 &&
+ srcRight == bitmap->width() && srcBottom == bitmap->height() &&
+ (srcBottom - srcTop == dstBottom - dstTop) &&
+ (srcRight - srcLeft == dstRight - dstLeft)) {
+ // transform simple rect to rect drawing case into position bitmap ops, since they merge
+ addDrawOp(new (alloc()) DrawBitmapOp(bitmap, dstLeft, dstTop, paint));
+ return DrawGlInfo::kStatusDone;
+ }
+
addDrawOp(new (alloc()) DrawBitmapRectOp(bitmap,
srcLeft, srcTop, srcRight, srcBottom,
dstLeft, dstTop, dstRight, dstBottom, paint));
@@ -413,7 +422,9 @@ status_t DisplayListRenderer::drawPosText(const char* text, int bytesCount, int
}
status_t DisplayListRenderer::drawText(const char* text, int bytesCount, int count,
- float x, float y, const float* positions, SkPaint* paint, float length) {
+ float x, float y, const float* positions, SkPaint* paint,
+ float length, DrawOpMode drawOpMode) {
+
if (!text || count <= 0) return DrawGlInfo::kStatusDone;
if (length < 0.0f) length = paint->measureText(text, bytesCount);
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 19f7eb6..75abad6 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -121,8 +121,9 @@ public:
float hOffset, float vOffset, SkPaint* paint);
virtual status_t drawPosText(const char* text, int bytesCount, int count,
const float* positions, SkPaint* paint);
- virtual status_t drawText(const char* text, int bytesCount, int count,
- float x, float y, const float* positions, SkPaint* paint, float length);
+ virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y,
+ const float* positions, SkPaint* paint, float length, DrawOpMode drawOpMode);
+
virtual status_t drawRects(const float* rects, int count, SkPaint* paint);
virtual void resetShader();
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 6894ef9..543cfa2 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -57,7 +57,6 @@ FontRenderer::FontRenderer() :
mGammaTable = NULL;
mInitialized = false;
- mMaxNumberOfQuads = 1024;
mCurrentCacheTexture = NULL;
@@ -293,7 +292,7 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
}
CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
- CacheTexture* cacheTexture = new CacheTexture(width, height, mMaxNumberOfQuads);
+ CacheTexture* cacheTexture = new CacheTexture(width, height, gMaxNumberOfQuads);
if (allocate) {
Caches::getInstance().activeTexture(0);
@@ -320,12 +319,12 @@ void FontRenderer::initTextTexture() {
// Avoid having to reallocate memory and render quad by quad
void FontRenderer::initVertexArrayBuffers() {
- uint32_t numIndices = mMaxNumberOfQuads * 6;
+ uint32_t numIndices = gMaxNumberOfQuads * 6;
uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
// Four verts, two triangles , six indices per quad
- for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
+ for (uint32_t i = 0; i < gMaxNumberOfQuads; i++) {
int i6 = i * 6;
int i4 = i * 4;
@@ -594,7 +593,7 @@ void FontRenderer::endPrecaching() {
bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
- const float* positions, Rect* bounds, Functor* functor) {
+ const float* positions, Rect* bounds, Functor* functor, bool forceFinish) {
if (!mCurrentFont) {
ALOGE("No font set");
return false;
@@ -602,7 +601,10 @@ bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *t
initRender(clip, bounds, functor);
mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
- finishRender();
+
+ if (forceFinish) {
+ finishRender();
+ }
return mDrawn;
}
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 348b7e3..307a1d9 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -66,7 +66,8 @@ public:
// bounds is an out parameter
bool renderPosText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
uint32_t len, int numGlyphs, int x, int y, const float* positions, Rect* bounds,
- Functor* functor);
+ Functor* functor, bool forceFinish = true);
+
// bounds is an out parameter
bool renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
uint32_t len, int numGlyphs, SkPath* path, float hOffset, float vOffset, Rect* bounds);
@@ -101,6 +102,8 @@ public:
private:
friend class Font;
+ static const uint32_t gMaxNumberOfQuads = 2048;
+
const uint8_t* mGammaTable;
void allocateTextureMemory(CacheTexture* cacheTexture);
@@ -154,7 +157,6 @@ private:
bool mUploadTexture;
- uint32_t mMaxNumberOfQuads;
uint32_t mIndexBufferID;
Functor* mFunctor;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index dcd1eb8..f81b4ff 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -112,11 +112,9 @@ static const Blender gBlendsSwap[] = {
OpenGLRenderer::OpenGLRenderer():
mCaches(Caches::getInstance()), mExtensions(Extensions::getInstance()) {
- mDrawModifiers.mShader = NULL;
- mDrawModifiers.mColorFilter = NULL;
+ // *set* draw modifiers to be 0
+ memset(&mDrawModifiers, 0, sizeof(mDrawModifiers));
mDrawModifiers.mOverrideLayerAlpha = 1.0f;
- mDrawModifiers.mHasShadow = false;
- mDrawModifiers.mHasDrawFilter = false;
memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
@@ -1330,10 +1328,9 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef
}
}
- if (stateDeferFlags & kStateDeferFlag_Clip) {
+ state.mClipValid = (stateDeferFlags & kStateDeferFlag_Clip);
+ if (state.mClipValid) {
state.mClip.set(currentClip);
- } else {
- state.mClip.setEmpty();
}
// Transform, drawModifiers, and alpha always deferred, since they are used by state operations
@@ -1344,17 +1341,22 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef
return false;
}
-void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state) {
+void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore) {
currentTransform().load(state.mMatrix);
mDrawModifiers = state.mDrawModifiers;
mSnapshot->alpha = state.mAlpha;
- if (!state.mClip.isEmpty()) {
+ if (state.mClipValid && !skipClipRestore) {
mSnapshot->setClip(state.mClip.left, state.mClip.top, state.mClip.right, state.mClip.bottom);
dirtyClip();
}
}
+void OpenGLRenderer::setFullScreenClip() {
+ mSnapshot->setClip(0, 0, mWidth, mHeight);
+ dirtyClip();
+}
+
///////////////////////////////////////////////////////////////////////////////
// Transforms
///////////////////////////////////////////////////////////////////////////////
@@ -1963,6 +1965,42 @@ void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, Sk
(GLvoid*) gMeshTextureOffset, GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform);
}
+status_t OpenGLRenderer::drawBitmaps(SkBitmap* bitmap, int bitmapCount, TextureVertex* vertices,
+ const Rect& bounds, SkPaint* paint) {
+
+ // merged draw operations don't need scissor, but clip should still be valid
+ mCaches.setScissorEnabled(mScissorOptimizationDisabled);
+
+ mCaches.activeTexture(0);
+ Texture* texture = mCaches.textureCache.get(bitmap);
+ if (!texture) return DrawGlInfo::kStatusDone;
+ const AutoTexture autoCleanup(texture);
+
+ int alpha;
+ SkXfermode::Mode mode;
+ getAlphaAndMode(paint, &alpha, &mode);
+
+ texture->setWrap(GL_CLAMP_TO_EDGE, true);
+ texture->setFilter(GL_NEAREST, true); // merged ops are always pure-translation for now
+
+ const float x = (int) floorf(bounds.left + 0.5f);
+ const float y = (int) floorf(bounds.top + 0.5f);
+ if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) {
+ int color = paint != NULL ? paint->getColor() : 0;
+ drawAlpha8TextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(),
+ texture->id, paint != NULL, color, alpha, mode,
+ &vertices[0].position[0], &vertices[0].texture[0],
+ GL_TRIANGLES, bitmapCount * 6, true, true);
+ } else {
+ drawTextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(),
+ texture->id, alpha / 255.0f, mode, texture->blend,
+ &vertices[0].position[0], &vertices[0].texture[0],
+ GL_TRIANGLES, bitmapCount * 6, false, true, 0, true);
+ }
+
+ return DrawGlInfo::kStatusDrew;
+}
+
status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint) {
const float right = left + bitmap->width();
const float bottom = top + bitmap->height();
@@ -2796,8 +2834,11 @@ mat4 OpenGLRenderer::findBestFontTransform(const mat4& transform) const {
}
status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
- float x, float y, const float* positions, SkPaint* paint, float length) {
- if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint)) {
+ float x, float y, const float* positions, SkPaint* paint, float length,
+ DrawOpMode drawOpMode) {
+
+ if (drawOpMode == kDrawOpMode_Immediate &&
+ (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint))) {
return DrawGlInfo::kStatusDone;
}
@@ -2815,8 +2856,13 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
SkPaint::FontMetrics metrics;
paint->getFontMetrics(&metrics, 0.0f);
- if (quickReject(x, y + metrics.fTop, x + length, y + metrics.fBottom)) {
- return DrawGlInfo::kStatusDone;
+ if (drawOpMode == kDrawOpMode_Immediate) {
+ if (quickReject(x, y + metrics.fTop, x + length, y + metrics.fBottom)) {
+ return DrawGlInfo::kStatusDone;
+ }
+ } else {
+ // merged draw operations don't need scissor, but clip should still be valid
+ mCaches.setScissorEnabled(mScissorOptimizationDisabled);
}
const float oldX = x;
@@ -2868,17 +2914,20 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
bool status;
TextSetupFunctor functor(*this, x, y, pureTranslate, alpha, mode, paint);
+
+ // don't call issuedrawcommand, do it at end of batch
+ bool forceFinish = (drawOpMode != kDrawOpMode_Defer);
if (CC_UNLIKELY(paint->getTextAlign() != SkPaint::kLeft_Align)) {
SkPaint paintCopy(*paint);
paintCopy.setTextAlign(SkPaint::kLeft_Align);
status = fontRenderer.renderPosText(&paintCopy, clip, text, 0, bytesCount, count, x, y,
- positions, hasActiveLayer ? &bounds : NULL, &functor);
+ positions, hasActiveLayer ? &bounds : NULL, &functor, forceFinish);
} else {
status = fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
- positions, hasActiveLayer ? &bounds : NULL, &functor);
+ positions, hasActiveLayer ? &bounds : NULL, &functor, forceFinish);
}
- if (status && hasActiveLayer) {
+ if ((status || drawOpMode != kDrawOpMode_Immediate) && hasActiveLayer) {
if (!pureTranslate) {
transform.mapRect(bounds);
}
@@ -3093,7 +3142,11 @@ void OpenGLRenderer::setupShadow(float radius, float dx, float dy, int color) {
///////////////////////////////////////////////////////////////////////////////
void OpenGLRenderer::resetPaintFilter() {
+ // when clearing the PaintFilter, the masks should also be cleared for simple DrawModifier
+ // comparison, see MergingDrawBatch::canMergeWith
mDrawModifiers.mHasDrawFilter = false;
+ mDrawModifiers.mPaintFilterClearBits = 0;
+ mDrawModifiers.mPaintFilterSetBits = 0;
}
void OpenGLRenderer::setupPaintFilter(int clearBits, int setBits) {
@@ -3365,7 +3418,7 @@ void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float b
void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, float bottom,
GLuint texture, bool hasColor, int color, int alpha, SkXfermode::Mode mode,
GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
- bool ignoreTransform, bool dirty) {
+ bool ignoreTransform, bool ignoreScale, bool dirty) {
setupDraw();
setupDrawWithTexture(true);
@@ -3377,7 +3430,11 @@ void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, f
setupDrawBlending(true, mode);
setupDrawProgram();
if (!dirty) setupDrawDirtyRegionsDisabled();
- setupDrawModelView(left, top, right, bottom, ignoreTransform);
+ if (!ignoreScale) {
+ setupDrawModelView(left, top, right, bottom, ignoreTransform);
+ } else {
+ setupDrawModelViewTranslate(left, top, right, bottom, ignoreTransform);
+ }
setupDrawTexture(texture);
setupDrawPureColorUniforms();
setupDrawColorFilterUniforms();
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index dd7a5a2..a0ad888 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -71,10 +71,17 @@ enum StateDeferFlags {
kStateDeferFlag_Clip = 0x2
};
+enum DrawOpMode {
+ kDrawOpMode_Immediate,
+ kDrawOpMode_Defer,
+ kDrawOpMode_Flush
+};
+
struct DeferredDisplayState {
- Rect mBounds; // local bounds, mapped with matrix to be in screen space coordinates, clipped.
+ Rect mBounds; // global op bounds, mapped by mMatrix to be in screen space coordinates, clipped.
// the below are set and used by the OpenGLRenderer at record and deferred playback
+ bool mClipValid;
Rect mClip;
mat4 mMatrix;
DrawModifiers mDrawModifiers;
@@ -232,6 +239,8 @@ public:
virtual void outputDisplayList(DisplayList* displayList);
virtual status_t drawLayer(Layer* layer, float x, float y);
virtual status_t drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
+ status_t drawBitmaps(SkBitmap* bitmap, int bitmapCount, TextureVertex* vertices,
+ const Rect& bounds, SkPaint* paint);
virtual status_t drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
virtual status_t drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
@@ -261,7 +270,8 @@ public:
virtual status_t drawPosText(const char* text, int bytesCount, int count,
const float* positions, SkPaint* paint);
virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y,
- const float* positions, SkPaint* paint, float length = -1.0f);
+ const float* positions, SkPaint* paint, float length = -1.0f,
+ DrawOpMode drawOpMode = kDrawOpMode_Immediate);
virtual status_t drawRects(const float* rects, int count, SkPaint* paint);
virtual void resetShader();
@@ -282,7 +292,8 @@ public:
SkPaint* filterPaint(SkPaint* paint);
bool storeDisplayState(DeferredDisplayState& state, int stateDeferFlags);
- void restoreDisplayState(const DeferredDisplayState& state);
+ void restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore = false);
+ void setFullScreenClip();
const DrawModifiers& getDrawModifiers() { return mDrawModifiers; }
void setDrawModifiers(const DrawModifiers& drawModifiers) { mDrawModifiers = drawModifiers; }
@@ -336,20 +347,18 @@ public:
* @param mode Where to store the resulting xfermode
*/
static inline void getAlphaAndModeDirect(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) {
- if (paint) {
- *mode = getXfermode(paint->getXfermode());
-
- // Skia draws using the color's alpha channel if < 255
- // Otherwise, it uses the paint's alpha
- int color = paint->getColor();
- *alpha = (color >> 24) & 0xFF;
- if (*alpha == 255) {
- *alpha = paint->getAlpha();
- }
- } else {
- *mode = SkXfermode::kSrcOver_Mode;
- *alpha = 255;
- }
+ *mode = getXfermodeDirect(paint);
+ *alpha = getAlphaDirect(paint);
+ }
+
+ static inline SkXfermode::Mode getXfermodeDirect(SkPaint* paint) {
+ if (!paint) return SkXfermode::kSrcOver_Mode;
+ return getXfermode(paint->getXfermode());
+ }
+
+ static inline int getAlphaDirect(SkPaint* paint) {
+ if (!paint) return 255;
+ return paint->getAlpha();
}
/**
@@ -358,6 +367,20 @@ public:
*/
mat4 findBestFontTransform(const mat4& transform) const;
+#if DEBUG_MERGE_BEHAVIOR
+ void drawScreenSpaceColorRect(float left, float top, float right, float bottom, int color) {
+ mCaches.setScissorEnabled(false);
+
+ // should only be called outside of other draw ops, so stencil can only be in test state
+ bool stencilWasEnabled = mCaches.stencil.isTestEnabled();
+ mCaches.stencil.disable();
+
+ drawColorRect(left, top, right, bottom, color, SkXfermode::kSrcOver_Mode, true);
+
+ if (stencilWasEnabled) mCaches.stencil.enableTest();
+ }
+#endif
+
protected:
/**
* Computes the projection matrix, initialize the first snapshot
@@ -778,7 +801,7 @@ private:
void drawAlpha8TextureMesh(float left, float top, float right, float bottom,
GLuint texture, bool hasColor, int color, int alpha, SkXfermode::Mode mode,
GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
- bool ignoreTransform, bool dirty = true);
+ bool ignoreTransform, bool ignoreScale = false, bool dirty = true);
/**
* Draws text underline and strike-through if needed.
diff --git a/libs/hwui/utils/TinyHashMap.h b/libs/hwui/utils/TinyHashMap.h
new file mode 100644
index 0000000..8855140
--- /dev/null
+++ b/libs/hwui/utils/TinyHashMap.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_TINYHASHMAP_H
+#define ANDROID_HWUI_TINYHASHMAP_H
+
+#include <utils/BasicHashtable.h>
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * A very simple hash map that doesn't allow duplicate keys, overwriting the older entry.
+ *
+ * Currently, expects simple keys that are handled by hash_t()
+ */
+template <typename TKey, typename TValue>
+class TinyHashMap {
+public:
+ typedef key_value_pair_t<TKey, TValue> TEntry;
+
+ /**
+ * Puts an entry in the hash, removing any existing entry with the same key
+ */
+ void put(TKey key, TValue value) {
+ hash_t hash = hash_t(key);
+
+ ssize_t index = mTable.find(-1, hash, key);
+ if (index != -1) {
+ mTable.removeAt(index);
+ }
+
+ TEntry initEntry(key, value);
+ mTable.add(hash, initEntry);
+ }
+
+ /**
+ * Return true if key is in the map, in which case stores the value in the output ref
+ */
+ bool get(TKey key, TValue& outValue) {
+ hash_t hash = hash_t(key);
+ ssize_t index = mTable.find(-1, hash, key);
+ if (index == -1) {
+ return false;
+ }
+ outValue = mTable.entryAt(index).value;
+ return true;
+ }
+
+ void clear() { mTable.clear(); }
+
+private:
+ BasicHashtable<TKey, TEntry> mTable;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_TINYHASHMAP_H
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java
index 965e378..7315aad 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java
@@ -151,7 +151,9 @@ public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecu
public void onResume(int reason) {
if (DEBUG) Log.d(TAG, "onResume()");
mIsShowing = KeyguardUpdateMonitor.getInstance(mContext).isKeyguardVisible();
- maybeStartBiometricUnlock();
+ if (!KeyguardUpdateMonitor.getInstance(mContext).isSwitchingUser()) {
+ maybeStartBiometricUnlock();
+ }
KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback);
// Registers a callback which handles stopping the biometric unlock and restarting it in
@@ -269,6 +271,14 @@ public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecu
}
@Override
+ public void onUserSwitchComplete(int userId) {
+ if (DEBUG) Log.d(TAG, "onUserSwitchComplete(" + userId + ")");
+ if (mBiometricUnlock != null) {
+ maybeStartBiometricUnlock();
+ }
+ }
+
+ @Override
public void onKeyguardVisibilityChanged(boolean showing) {
if (DEBUG) Log.d(TAG, "onKeyguardVisibilityChanged(" + showing + ")");
boolean wasShowing = false;
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
index ad87a4b..986dc49 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
@@ -124,6 +124,8 @@ public class KeyguardUpdateMonitor {
mCallbacks = Lists.newArrayList();
private ContentObserver mDeviceProvisionedObserver;
+ private boolean mSwitchingUser;
+
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -461,11 +463,13 @@ public class KeyguardUpdateMonitor {
public void onUserSwitching(int newUserId, IRemoteCallback reply) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHING,
newUserId, 0, reply));
+ mSwitchingUser = true;
}
@Override
public void onUserSwitchComplete(int newUserId) throws RemoteException {
mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCH_COMPLETE,
newUserId));
+ mSwitchingUser = false;
}
});
} catch (RemoteException e) {
@@ -529,7 +533,6 @@ public class KeyguardUpdateMonitor {
cb.onUserSwitching(userId);
}
}
- setAlternateUnlockEnabled(false);
try {
reply.sendResult(null);
} catch (RemoteException e) {
@@ -733,6 +736,10 @@ public class KeyguardUpdateMonitor {
return mKeyguardIsVisible;
}
+ public boolean isSwitchingUser() {
+ return mSwitchingUser;
+ }
+
private static boolean isBatteryUpdateInteresting(BatteryStatus old, BatteryStatus current) {
final boolean nowPluggedIn = current.isPluggedIn();
final boolean wasPluggedIn = old.isPluggedIn();
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
index c49228e..08a95a6 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
@@ -319,8 +319,9 @@ public class KeyguardViewMediator {
mSwitchingUser = true;
resetStateLocked(null);
adjustStatusBarLocked();
- // Disable face unlock when the user switches.
- KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(false);
+ // When we switch users we want to bring the new user to the biometric unlock even
+ // if the current user has gone to the backup.
+ KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
}
}
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 110c4da..128a49f 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1817,11 +1817,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
addServiceLocked(this, userState);
if (userState.mBindingServices.contains(mComponentName)) {
userState.mBindingServices.remove(mComponentName);
- onUserStateChangedLocked(userState);
try {
- mServiceInterface.setConnection(this, mId);
+ mServiceInterface.setConnection(this, mId);
+ onUserStateChangedLocked(userState);
} catch (RemoteException re) {
- Slog.w(LOG_TAG, "Error while setting connection for service: " + service, re);
+ Slog.w(LOG_TAG, "Error while setting connection for service: "
+ + service, re);
+ binderDied();
}
} else {
binderDied();
@@ -2499,7 +2501,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
public void flush() {
synchronized (mLock) {
cancelAllPendingEventsLocked();
- mSentEventsVerifier.reset();
+ if (mSentEventsVerifier != null) {
+ mSentEventsVerifier.reset();
+ }
}
}
diff --git a/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java b/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
index c94f7c1..9601e9a 100644
--- a/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
+++ b/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
@@ -56,9 +56,9 @@ public class ConfigUpdateInstallReceiver extends BroadcastReceiver {
private static final String UPDATE_CERTIFICATE_KEY = "config_update_certificate";
- private final File updateDir;
- private final File updateContent;
- private final File updateVersion;
+ protected final File updateDir;
+ protected final File updateContent;
+ protected final File updateVersion;
public ConfigUpdateInstallReceiver(String updateDir, String updateContentPath,
String updateMetadataPath, String updateVersionPath) {
@@ -222,7 +222,7 @@ public class ConfigUpdateInstallReceiver extends BroadcastReceiver {
return signer.verify(Base64.decode(signature.getBytes(), Base64.DEFAULT));
}
- private void writeUpdate(File dir, File file, byte[] content) throws IOException {
+ protected void writeUpdate(File dir, File file, byte[] content) throws IOException {
FileOutputStream out = null;
File tmp = null;
try {
diff --git a/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java b/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
index 748849e..e8337f6 100644
--- a/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
+++ b/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
@@ -18,28 +18,127 @@ package com.android.server.updates;
import android.content.Context;
import android.content.Intent;
+import android.os.FileUtils;
import android.os.SELinux;
+import android.os.SystemProperties;
import android.provider.Settings;
import android.util.Base64;
import android.util.Slog;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
+import libcore.io.ErrnoException;
+import libcore.io.IoUtils;
+import libcore.io.Libcore;
+
public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver {
+ private static final String TAG = "SELinuxPolicyInstallReceiver";
+
+ private static final String sepolicyPath = "sepolicy";
+ private static final String fileContextsPath = "file_contexts";
+ private static final String propertyContextsPath = "property_contexts";
+ private static final String seappContextsPath = "seapp_contexts";
+
public SELinuxPolicyInstallReceiver() {
- super("/data/security/", "sepolicy", "metadata/", "version");
+ super("/data/security/bundle", "sepolicy_bundle", "metadata/", "version");
}
- @Override
- protected void install(byte[] encodedContent, int version) throws IOException {
- super.install(Base64.decode(encodedContent, Base64.DEFAULT), version);
+ private void backupContexts(File contexts) {
+ new File(contexts, seappContextsPath).renameTo(
+ new File(contexts, seappContextsPath + "_backup"));
+
+ new File(contexts, propertyContextsPath).renameTo(
+ new File(contexts, propertyContextsPath + "_backup"));
+
+ new File(contexts, fileContextsPath).renameTo(
+ new File(contexts, fileContextsPath + "_backup"));
+
+ new File(contexts, sepolicyPath).renameTo(
+ new File(contexts, sepolicyPath + "_backup"));
+ }
+
+ private void copyUpdate(File contexts) {
+ new File(updateDir, seappContextsPath).renameTo(new File(contexts, seappContextsPath));
+ new File(updateDir, propertyContextsPath).renameTo(new File(contexts, propertyContextsPath));
+ new File(updateDir, fileContextsPath).renameTo(new File(contexts, fileContextsPath));
+ new File(updateDir, sepolicyPath).renameTo(new File(contexts, sepolicyPath));
+ }
+
+ private int readInt(BufferedInputStream reader) throws IOException {
+ int value = 0;
+ for (int i=0; i < 4; i++) {
+ value = (value << 8) | reader.read();
+ }
+ return value;
+ }
+
+ private int[] readChunkLengths(BufferedInputStream bundle) throws IOException {
+ int[] chunks = new int[4];
+ chunks[0] = readInt(bundle);
+ chunks[1] = readInt(bundle);
+ chunks[2] = readInt(bundle);
+ chunks[3] = readInt(bundle);
+ return chunks;
+ }
+
+ private void installFile(File destination, BufferedInputStream stream, int length)
+ throws IOException {
+ byte[] chunk = new byte[length];
+ stream.read(chunk, 0, length);
+ writeUpdate(updateDir, destination, Base64.decode(chunk, Base64.DEFAULT));
+ }
+
+ private void unpackBundle() throws IOException {
+ BufferedInputStream stream = new BufferedInputStream(new FileInputStream(updateContent));
+ int[] chunkLengths = readChunkLengths(stream);
+ installFile(new File(updateDir, seappContextsPath), stream, chunkLengths[0]);
+ installFile(new File(updateDir, propertyContextsPath), stream, chunkLengths[1]);
+ installFile(new File(updateDir, fileContextsPath), stream, chunkLengths[2]);
+ installFile(new File(updateDir, sepolicyPath), stream, chunkLengths[3]);
+ }
+
+ private void applyUpdate() throws IOException, ErrnoException {
+ Slog.i(TAG, "Applying SELinux policy");
+ File contexts = new File(updateDir.getParentFile(), "contexts");
+ File current = new File(updateDir.getParentFile(), "current");
+ File update = new File(updateDir.getParentFile(), "update");
+ File tmp = new File(updateDir.getParentFile(), "tmp");
+ if (current.exists()) {
+ Libcore.os.symlink(updateDir.getPath(), update.getPath());
+ Libcore.os.rename(update.getPath(), current.getPath());
+ } else {
+ Libcore.os.symlink(updateDir.getPath(), current.getPath());
+ }
+ contexts.mkdirs();
+ backupContexts(contexts);
+ copyUpdate(contexts);
+ Libcore.os.symlink(contexts.getPath(), tmp.getPath());
+ Libcore.os.rename(tmp.getPath(), current.getPath());
+ SystemProperties.set("selinux.reload_policy", "1");
+ }
+
+ private void setEnforcingMode(Context context) {
+ boolean mode = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.SELINUX_STATUS, 0) == 1;
+ SELinux.setSELinuxEnforce(mode);
}
@Override
protected void postInstall(Context context, Intent intent) {
- boolean mode = Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.SELINUX_STATUS, 0) == 1;
- SELinux.setSELinuxEnforce(mode);
+ try {
+ unpackBundle();
+ applyUpdate();
+ setEnforcingMode(context);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "SELinux policy update malformed: ", e);
+ } catch (IOException e) {
+ Slog.e(TAG, "Could not update selinux policy: ", e);
+ } catch (ErrnoException e) {
+ Slog.e(TAG, "Could not update selinux policy: ", e);
+ }
}
}
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index d3d5b1b..23a4e71 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -1111,7 +1111,8 @@ class WifiConfigStore {
break setVariables;
}
- if (config.enterpriseConfig != null) {
+ if (config.enterpriseConfig != null &&
+ config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;