diff options
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>—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>—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—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>—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>—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> + —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>—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>—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>—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>—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>—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>—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>—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>—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"> + +</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 »</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 »</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 »</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>—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>—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>—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>—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>— 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>— 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>— 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>—Don't let your + app infringe on the trademarks of others. + </li> + + <li> + <strong>Know your app's content</strong>—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>—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>—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>—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>—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>—Make sure the title + and description describe the app function and user experience accurately. + </li> + + <li> + <strong>Don't use repetitive keywords</strong>—Avoid keywords that + are repetitive or excessive. + </li> + + <li> + <strong>Don't include unrelated keywords or references</strong> — + Your description should not be loaded with irrelevant keywords in an + attempt to manipulate ranking or relevancy. + </li> + + <li> + <strong>Keep it brief</strong>—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>—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>—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>—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>—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 Binary files differnew file mode 100644 index 0000000..b19a9f7 --- /dev/null +++ b/docs/html/images/example-bad.png diff --git a/docs/html/images/example-good.png b/docs/html/images/example-good.png Binary files differnew file mode 100644 index 0000000..6bd2408 --- /dev/null +++ b/docs/html/images/example-good.png diff --git a/docs/html/images/gp-policy-ads-eula-violation.png b/docs/html/images/gp-policy-ads-eula-violation.png Binary files differnew file mode 100644 index 0000000..e8ffa5b --- /dev/null +++ b/docs/html/images/gp-policy-ads-eula-violation.png diff --git a/docs/html/images/gp-policy-ads-eula.png b/docs/html/images/gp-policy-ads-eula.png Binary files differnew file mode 100644 index 0000000..68a6b95 --- /dev/null +++ b/docs/html/images/gp-policy-ads-eula.png diff --git a/docs/html/images/gp-policy-ads-impersonate-violation.png b/docs/html/images/gp-policy-ads-impersonate-violation.png Binary files differnew file mode 100644 index 0000000..385ae6e --- /dev/null +++ b/docs/html/images/gp-policy-ads-impersonate-violation.png diff --git a/docs/html/images/gp-policy-ads-maturity-violation.png b/docs/html/images/gp-policy-ads-maturity-violation.png Binary files differnew file mode 100644 index 0000000..d41870e --- /dev/null +++ b/docs/html/images/gp-policy-ads-maturity-violation.png diff --git a/docs/html/images/gp-policy-ads-notif-attr-violation.png b/docs/html/images/gp-policy-ads-notif-attr-violation.png Binary files differnew file mode 100644 index 0000000..af53f10 --- /dev/null +++ b/docs/html/images/gp-policy-ads-notif-attr-violation.png diff --git a/docs/html/images/gp-policy-ads-notif-attr.png b/docs/html/images/gp-policy-ads-notif-attr.png Binary files differnew file mode 100644 index 0000000..4934d21 --- /dev/null +++ b/docs/html/images/gp-policy-ads-notif-attr.png diff --git a/docs/html/images/gp-policy-ads-paywall-violation.png b/docs/html/images/gp-policy-ads-paywall-violation.png Binary files differnew file mode 100644 index 0000000..8bbfd1b --- /dev/null +++ b/docs/html/images/gp-policy-ads-paywall-violation.png diff --git a/docs/html/images/gp-policy-ads-paywall.png b/docs/html/images/gp-policy-ads-paywall.png Binary files differnew file mode 100644 index 0000000..e7b1e19 --- /dev/null +++ b/docs/html/images/gp-policy-ads-paywall.png diff --git a/docs/html/images/gp-policy-ads-terms.png b/docs/html/images/gp-policy-ads-terms.png Binary files differnew file mode 100644 index 0000000..dcbdf4a --- /dev/null +++ b/docs/html/images/gp-policy-ads-terms.png diff --git a/docs/html/images/gp-policy-ip-copyright-violation.png b/docs/html/images/gp-policy-ip-copyright-violation.png Binary files differnew file mode 100644 index 0000000..a4e96a8 --- /dev/null +++ b/docs/html/images/gp-policy-ip-copyright-violation.png diff --git a/docs/html/images/gp-policy-ip-impersonation-violation.png b/docs/html/images/gp-policy-ip-impersonation-violation.png Binary files differnew file mode 100644 index 0000000..b1d9923 --- /dev/null +++ b/docs/html/images/gp-policy-ip-impersonation-violation.png diff --git a/docs/html/images/gp-policy-ip-trademark-violation.png b/docs/html/images/gp-policy-ip-trademark-violation.png Binary files differnew file mode 100644 index 0000000..c05b67b --- /dev/null +++ b/docs/html/images/gp-policy-ip-trademark-violation.png diff --git a/docs/html/images/gp-policy-spam-negreview.png b/docs/html/images/gp-policy-spam-negreview.png Binary files differnew file mode 100644 index 0000000..f68eba3 --- /dev/null +++ b/docs/html/images/gp-policy-spam-negreview.png diff --git a/docs/html/images/gp-policy-spam-reqrating.png b/docs/html/images/gp-policy-spam-reqrating.png Binary files differnew file mode 100644 index 0000000..aaf9e53 --- /dev/null +++ b/docs/html/images/gp-policy-spam-reqrating.png 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; |