diff options
29 files changed, 523 insertions, 141 deletions
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index d68124660a51..d7550a4d9695 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -18,6 +18,7 @@ #include "CreateJavaOutputStreamAdaptor.h" #include <Caches.h> #include <hwui/Paint.h> +#include <renderthread/RenderProxy.h> #include "core_jni_helpers.h" @@ -1361,6 +1362,14 @@ static jlong Bitmap_refPixelRef(JNIEnv* env, jobject, jlong bitmapHandle) { return reinterpret_cast<jlong>(pixelRef); } +static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapPtr) { + LocalScopedBitmap bitmapHandle(bitmapPtr); + if (!bitmapHandle.valid()) return; + SkBitmap bitmap; + bitmapHandle->getSkBitmap(&bitmap); + android::uirenderer::renderthread::RenderProxy::prepareToDraw(bitmap); +} + /////////////////////////////////////////////////////////////////////////////// static const JNINativeMethod gBitmapMethods[] = { @@ -1404,6 +1413,7 @@ static const JNINativeMethod gBitmapMethods[] = { (void*)Bitmap_copyPixelsFromBuffer }, { "nativeSameAs", "(JJ)Z", (void*)Bitmap_sameAs }, { "nativeRefPixelRef", "(J)J", (void*)Bitmap_refPixelRef }, + { "nativePrepareToDraw", "(J)V", (void*)Bitmap_prepareToDraw }, }; int register_android_graphics_Bitmap(JNIEnv* env) diff --git a/docs/html/_redirects.yaml b/docs/html/_redirects.yaml index 3dcc736d3c90..00a7edd3f528 100644 --- a/docs/html/_redirects.yaml +++ b/docs/html/_redirects.yaml @@ -479,6 +479,14 @@ redirects: to: /distribute/stories/index.html - from: /distribute/stories/tablets.html to: /distribute/stories/index.html +- from: /distribute/stories/glu-dh.html + to: /distribute/stories/games/glu-dh.html +- from: /distribute/stories/apps/tapps.html + to: /distribute/stories/games/tapps.html +- from: /distribute/stories/apps/upbeat-games.html + to: /distribute/stories/games/upbeat-games.html +- from: /distribute/stories/games/two-dots.html + to: /distribute/stories/games/dots.html - from: /distribute/googleplay/edu/index.html to: /distribute/googleplay/edu/about.html - from: /distribute/googleplay/edu/contact.html diff --git a/docs/html/distribute/stories/apps/aftenposten.jd b/docs/html/distribute/stories/apps/aftenposten.jd index 149e6bbe5a4a..a813c00addca 100644 --- a/docs/html/distribute/stories/apps/aftenposten.jd +++ b/docs/html/distribute/stories/apps/aftenposten.jd @@ -2,7 +2,7 @@ page.title=Aftenposten Improves Retention by Allowing Readers to Customize Notif page.metaDescription=Aftenposten upgraded their app and improved user retention. page.tags="developerstory", "apps", "googleplay" page.image=images/cards/distribute/stories/aftenposten.png -page.timestamp=1468270114 +page.timestamp=1468901834 @jd:body diff --git a/docs/html/distribute/stories/apps/el-mundo.jd b/docs/html/distribute/stories/apps/el-mundo.jd index 2ee813d55d11..2dbaeea7c8f0 100644 --- a/docs/html/distribute/stories/apps/el-mundo.jd +++ b/docs/html/distribute/stories/apps/el-mundo.jd @@ -2,7 +2,7 @@ page.title=El Mundo Improves User Ratings and Engagement with Material Design page.metaDescription=El Mundo uses Material Design principles to enhance their app's user experience. page.tags="developerstory", "apps", "googleplay" page.image=images/cards/distribute/stories/el-mundo.png -page.timestamp=1468270112 +page.timestamp=1468901833 @jd:body diff --git a/docs/html/distribute/stories/apps/segundamano.jd b/docs/html/distribute/stories/apps/segundamano.jd index 4cbf817c6d43..7ed4d8e88a5d 100644 --- a/docs/html/distribute/stories/apps/segundamano.jd +++ b/docs/html/distribute/stories/apps/segundamano.jd @@ -2,7 +2,7 @@ page.title=Segundamano Develops Android-First as Its Fastest Channel for Growth page.metaDescription=Segundamano developed Android app to increase potential for growth. page.tags="developerstory", "apps", "googleplay" page.image=images/cards/distribute/stories/segundamano.png -page.timestamp=1468270110 +page.timestamp=1468901832 @jd:body diff --git a/docs/html/distribute/stories/glu-dh.jd b/docs/html/distribute/stories/games/glu-dh.jd index 3353f6998ee1..3353f6998ee1 100644 --- a/docs/html/distribute/stories/glu-dh.jd +++ b/docs/html/distribute/stories/games/glu-dh.jd diff --git a/docs/html/distribute/stories/apps/tapps.jd b/docs/html/distribute/stories/games/tapps.jd index 129213946630..221b9a88622f 100644 --- a/docs/html/distribute/stories/apps/tapps.jd +++ b/docs/html/distribute/stories/games/tapps.jd @@ -1,8 +1,8 @@ page.title=Tapps Games Increases Installs by More Than 20% with Store Listing Experiments page.metaDescription=Tapps Games increased their use of store listing experiments in the Developer Console, with impressive results. -page.tags="developerstory", "apps", "googleplay" +page.tags="developerstory", "games", "googleplay" page.image=images/cards/distribute/stories/tapps.png -page.timestamp=1468270108 +page.timestamp=1468901831 @jd:body diff --git a/docs/html/distribute/stories/games/two-dots.jd b/docs/html/distribute/stories/games/two-dots.jd deleted file mode 100644 index a2299ce1f7f9..000000000000 --- a/docs/html/distribute/stories/games/two-dots.jd +++ /dev/null @@ -1,77 +0,0 @@ -page.title=Two Dots increased installs by 7 percent using Store Listing Experiments -page.metaDescription=Two Dots, the sequel to the popular game Dots, is a free-to-play puzzle game launched by Playdots, Inc. Playdots decided to use Store Listing Experiments to see if adding a call to action in the games’ store listing short descriptions had an impact on installs. -page.tags="developerstory", "games", "googleplay" -page.image=images/cards/distribute/stories/two-dots.png -page.timestamp=1456431511 - -@jd:body - - -<h3>Background</h3> - -<div class="figure" style="width:113px"> - <img src="{@docRoot}images/distribute/stories/two-dots-icon.png" - height="113" /> -</div> - -<p> - <a class="external-link" - href="https://play.google.com/store/apps/details?id=com.weplaydots.twodotsandroid&hl=en"> - Two Dots</a>, the sequel to the popular game - <a class="external-link" - href="https://play.google.com/store/apps/details?id=com.nerdyoctopus.gamedots&hl=en"> - Dots</a>, is a free-to-play puzzle game launched by Playdots, Inc. in May - 2014. Since launch it has gained over 30 million downloads, seen over five - billion games played, and achieved 15 times the revenue of the original Dots - game within a year. Dots decided to use - <a class="external-link" - href="https://support.google.com/googleplay/android-developer/answer/6227309"> - Store Listing Experiments</a> to see if adding a call to action in the games' - store listing short descriptions had an impact on installs. - -</p> - -<h3>What they did</h3> - -<p> - Dots used localized store listing experiments in the Google Play Developer - Console to test both games’ short descriptions. They compared the games’ - current descriptions — the control, with no call to action — against variant - descriptions, targeting half of their traffic with the variant descriptions. -</p> - -<h3>Results</h3> - -<p> - The results showed that the addition of a call to action in the short - description had a positive impact on installs. -</p> - - - <img - src="{@docRoot}images/distribute/stories/two-dots-screenshot.png" - srcset= - "{@docRoot}images/distribute/stories/two-dots-screenshot.png 1x - {@docRoot}images/distribute/stories/two-dots-screenshot_2x.png 2x"> - <p class="img-caption"> - Beautifully designed achievements badges encourage unlock - </p> - - -<p> - In Dots, the conversion rate increased by 2 percent with a simple call to - action in the variant text. In Two Dots, where a call to action was combined - with messaging that the game is the “best puzzle game on Android”, conversion - rates increased by 7 percent compared to the control description. -</p> - -<h3>Get started</h3> - -<p> - Learn how to run - <a clas="external-link" - href="https://support.google.com/googleplay/android-developer/answer/6227309"> - Store Listing Experiments</a> and read our best practices for - <a href="https://developer.android.com/distribute/users/experiments.html"> - running successful experiments</a>. -</p> diff --git a/docs/html/distribute/stories/apps/upbeat-games.jd b/docs/html/distribute/stories/games/upbeat-games.jd index 02222d39e686..16a1d51c19c9 100644 --- a/docs/html/distribute/stories/apps/upbeat-games.jd +++ b/docs/html/distribute/stories/games/upbeat-games.jd @@ -1,8 +1,8 @@ page.title=Witch Puzzle Achieves 98% of International Installs on Android page.metaDescription=Witch Puzzle localized their app into 12 languages. -page.tags="developerstory", "apps", "googleplay" +page.tags="developerstory", "games", "googleplay" page.image=images/cards/distribute/stories/witch-puzzle.png -page.timestamp=1468270106 +page.timestamp=1468901832 @jd:body diff --git a/docs/html/distribute/stories/index.jd b/docs/html/distribute/stories/index.jd index 8fe10191247d..1adc5aeb4eba 100644 --- a/docs/html/distribute/stories/index.jd +++ b/docs/html/distribute/stories/index.jd @@ -19,21 +19,43 @@ page.metaDescription=Android developers, their apps, and their successes with An <section class="dac-section dac-small" id="latest-apps"><div class="wrap"> <h2 class="norule">Latest from apps</h2> + <h3 class="norule">Videos</h3> + + <div class="resource-widget resource-flow-layout col-16" + data-query="type:youtube+tag:developerstory+tag:apps" + data-sortOrder="-timestamp" + data-cardSizes="6x6" + data-items-per-page="15" + data-initial-results="6"></div> + + <h3 class="norule">Articles</h3> + <div class="resource-widget resource-flow-layout col-16" - data-query="type:distribute+tag:developerstory+tag:apps, type:youtube+tag:developerstory+tag:apps" + data-query="type:distribute+tag:developerstory+tag:apps" data-sortOrder="-timestamp" data-cardSizes="6x6" data-items-per-page="15" - data-initial-results="9"></div> + data-initial-results="6"></div> </div></section> <section class="dac-section dac-small" id="latest-games"><div class="wrap"> <h2 class="norule">Latest from games</h2> + <h3 class="norule">Videos</h3> + + <div class="resource-widget resource-flow-layout col-16" + data-query="type:youtube+tag:developerstory+tag:games" + data-sortOrder="-timestamp" + data-cardSizes="6x6" + data-items-per-page="15" + data-initial-results="6"></div> + + <h3 class="norule">Articles</h3> + <div class="resource-widget resource-flow-layout col-16" - data-query="type:distribute+tag:developerstory+tag:games,type:youtube+tag:developerstory+tag:games" + data-query="type:distribute+tag:developerstory+tag:games" data-sortOrder="-timestamp" data-cardSizes="6x6" data-items-per-page="15" - data-initial-results="9"></div> + data-initial-results="6"></div> </div></section> diff --git a/docs/html/guide/topics/manifest/uses-feature-element.jd b/docs/html/guide/topics/manifest/uses-feature-element.jd index 10841d675834..9b32244b736f 100755 --- a/docs/html/guide/topics/manifest/uses-feature-element.jd +++ b/docs/html/guide/topics/manifest/uses-feature-element.jd @@ -1230,7 +1230,7 @@ densities: '160' </p> <p> - When declared as required, this feature indicates that the app is + By default, your app requires this feature. This feature indicates that the app is compatible with a device only if that device emulates a touchscreen ("fake touch" interface) or has an actual touchscreen. </p> @@ -1240,19 +1240,12 @@ densities: '160' that emulates a subset of a touchscreen's capabilities. For example, a mouse or remote control could drive an on-screen cursor. If your app requires basic point and click interaction (in other words, it won't work - with only a d-pad controller), you should declare this feature. Because + with only a d-pad controller), you should declare this feature or simply + avoid declaring any {@code android.hardware.touchscreen.*} features. Because this is the minimum level of touch interaction, you can also use an app that declares this feature on devices that offer more complex touch interfaces. </p> - - <p class="note"> - <strong>Note:</strong> Apps require the {@code android.hardware.touchscreen} - feature by default. If you want your app to be available to devices that - provide a fake touch interface, you must also explicitly declare that a - touchscreen is not required as follows: - </p> - <pre><uses-feature android:name="android.hardware.touchscreen" <strong>android:required="false"</strong> /></pre> </dd> <dt> @@ -1327,21 +1320,9 @@ densities: '160' </p> <p> - By default, your app requires this feature. As such, your app is not - available to devices that provide only an emulated touch interface ("fake - touch") by default. If you want to make your app available on devices - that provide a fake touch interface (or even on devices that provide only - a d-pad controller), you must explicitly declare that a touchscreen is - not required by declaring {@code android.hardware.touchscreen} with - {@code android:required="false"}. You should add this declaration if your - app uses—but does not require—a real touchscreen interface. - </p> - - <p> If your app in fact requires a touch interface (to perform more advanced - touch gestures such as fling), then you don't need to declare any touch - interface features because they're required by default. However, it's - best if you explicitly declare all features that your app uses. + touch gestures such as fling), then you must explicitly declare this feature + or any advanced touchscreen features. </p> <p> diff --git a/docs/html/topic/arc/_book.yaml b/docs/html/topic/arc/_book.yaml new file mode 100644 index 000000000000..ad83ba998138 --- /dev/null +++ b/docs/html/topic/arc/_book.yaml @@ -0,0 +1,9 @@ +toc: +- title: Optimize Apps for Chromebooks + path: /topic/arc/index.html +- title: Loading Apps on Chromebooks + path: /topic/arc/sideload.html +- title: Chrome OS Device Support for Apps + path: /topic/arc/device-support.html +- title: App Manifest Compatibility for Chromebooks + path: /topic/arc/manifest.html
\ No newline at end of file diff --git a/docs/html/training/_book.yaml b/docs/html/training/_book.yaml index 0523ec9e12b0..891574fbc6ca 100644 --- a/docs/html/training/_book.yaml +++ b/docs/html/training/_book.yaml @@ -1384,6 +1384,11 @@ toc: path_attributes: - name: description value: How to use the SafetyNet service to analyze a device where your app is running and get information about its compatibility with your app. + - title: Checking URLs with the Safe Browsing API + path: /training/safebrowsing/index.html + path_attributes: + - name: description + value: How to use the SafetyNet service to determine if a URL is designated as a known threat. - title: Verifying Hardware-backed Key Pairs with Key Attestation path: /training/articles/security-key-attestation.html path_attributes: diff --git a/docs/html/training/safebrowsing/index.jd b/docs/html/training/safebrowsing/index.jd new file mode 100644 index 000000000000..c6c72bfd1460 --- /dev/null +++ b/docs/html/training/safebrowsing/index.jd @@ -0,0 +1,315 @@ +page.title=Checking URLs with the Safe Browsing API + +@jd:body + + +<div id="tb-wrapper"> + <div id="tb"> + <h2> + In this document + </h2> + + <ol> + <li> + <a href="#tos">Terms of Service</a> + </li> + + <li> + <a href="#api-key">Requesting and Registering an Android API Key</a> + <ol> + <li> + <a href="#manifest">Adding the Android API to your + AndroidManifest.xml</a> + </li> + </ol> + </li> + + <li> + <a href="#connect-google-play">Connect to Google Play Services</a> + </li> + + <li> + <a href="#url-check">Requesting a URL Check</a> + <ol> + <li> + <a href="#threat-types">Specifying threat types of interest</a> + </li> + + <li> + <a href="#url-check-request">Send the URL check request</a> + </li> + + <li> + <a href="#url-check-response">Read the URL check response</a> + </li> + </ol> + </li> + + <li> + <a href="#warning-lang">Suggested Warning Language</a> + </li> + </ol> + </div> +</div> + +<p> + SafetyNet provides services for determining whether a URL has been marked as + a known threat by Google. +</p> + +<p> + The service provides an API your app can use to determine whether a + particular URL has been classified by Google as a known threat. Internally, + SafetyNet implements a client for the Safe Browsing Network Protocol v4 + developed by Google. Both the client code and the v4 network protocol were + designed to preserve users' privacy, as well as keep battery and bandwidth + consumption to a minimum. This API allows you to take full advantage of + Google's Safe Browsing service on Android in the most resource-optimized way, + and without having to implement its network protocol. +</p> + +<p> + This document shows you how to use SafetyNet for checking a URL for threat + types of interest. +</p> + +<h2 id="tos"> + Terms of Service +</h2> + +<p> + By using the Safe Browsing API, you consent to be bound by the <a href= + "https://developers.google.com/safe-browsing/terms">Terms of Service</a>. + Please read and understand all applicable terms and policies before accessing + the Safe Browsing API. +</p> + +<h2 id="api-key"> + Requesting and Registering an Android API Key +</h2> + +<p> + To create an API key, complete the following steps: +</p> + +<ol> + <li>Go to the <a href="https://console.developers.google.com/project" + class="external-link">Google Developers Console</a>. + </li> + + <li>On the upper toolbar, choose <strong>Select a project > + <em>your-project-name</em></strong>. + </li> + + <li>In the search box, enter <em>Safe Browsing APIs</em>; when the Safe + Browsing API name appears in the table, select it. + </li> + + <li>After the page redisplays, select <strong>Enable</strong> then select + <strong>Go to Credentials</strong>. + </li> + + <li>When the <em>Add credentials to your project</em> window appears, choose + your parameters then select <strong>What credentials do I need?</strong>. + </li> + + <li>Enter a name for your API key then select <strong>Create API + key</strong>. + </li> + + <li> + <p> + Your new API key appears; copy and paste this key for future use. + </p> + + <p class="note"> + <strong>Note:</strong> Your API key allows you to perform a URL check + 10,000 times each day. The key, in this instance, should just be a + hexadecimal string, not part of a URL. + </p> + </li> + + <li>Select <strong>Done</strong> to complete the process. + </li> +</ol> + +<p> + If you need more help, check out the <a href= + "https://developers.google.com/console/help/new/">Google Developers Console + Help Center</a>. +</p> + +<h3 id="manifest"> + Adding the Android API key to your AndroidManifest.xml +</h3> + +<p> + Once your key has been whitelisted, you need to add the key to the + <code>AndroidManifest.xml</code> file for your app: +</p> + +<pre> +<application> + + ... + + <!-- SafetyNet API metadata --> + <meta-data android:name="com.google.android.safetynet.API_KEY" + android:value="<var>your-API-key</var>" /> + + ... + +</application> +</pre> +<h2 id="connect-google-play"> + Connect to Google Play Services +</h2> + +<p> + The SafetyNet API is part of Google Play services. To connect to the API, you + need to create an instance of the Google Play services API client. For + details about using the client in your app, see <a href= + "https://developers.google.com/android/guides/api-client#Starting">Accessing + Google APIs</a>. Once you have established a connection to Google Play + services, you can use the Google API client classes to connect to the + SafetyNet API. +</p> + +<p> + To connect to the API, in your activity's <code><a href= + "{@docRoot}reference/android/app/Activity.html#onCreate(android.os.Bundle)">onCreate()</a></code> + method, create an instance of Google API Client using <code><a href= + "https://developers.google.com/android/reference/com/google/android/gms/common/api/GoogleApiClient.Builder"> + GoogleApiClient.Builder</a></code>. Use the builder to add the SafetyNet API, + as shown in the following code example: +</p> + +<pre> +protected synchronized void buildGoogleApiClient() { + mGoogleApiClient = new GoogleApiClient.Builder(this) + .addApi(SafetyNet.API) + .addConnectionCallbacks(myMainActivity.this) + .build(); +} +</pre> +<p class="note"> + <strong>Note:</strong> You can only call these methods after your app has + established a connection to Google Play services by receiving the <code> + <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks#public-methods"> + onConnected()</a></code> callback. For details about listening for a completed + client connection, see <a href= + "https://developers.google.com/android/guides/api-client#Starting">Accessing + Google APIs</a>. +</p> + +<h2 id="url-check"> + Requesting a URL Check +</h2> + +<p> + A URL check allows your app to determine if a URL has been marked as a threat + of interest. Some threat types may not be of interest to your particular + app, and the API allows you to choose which threat types are important for + your needs. You can specify multiple threat types of interest. +</p> + +<h3 id="threat-types"> + Specifying threat types of interest +</h3> + +<p> + The constants in the {@code SafeBrowsingThreat} class contain the + currently-supported threat types: +</p> + +<pre> +package com.google.android.gms.safetynet; + +public class SafeBrowsingThreat { + + /** + * This threat type identifies URLs of pages that are flagged as containing potentially + * harmful applications. + */ + public static final int TYPE_POTENTIALLY_HARMFUL_APPLICATION = 4; + + /** + * This threat type identifies URLs of pages that are flagged as containing social + * engineering threats. + */ + public static final int TYPE_SOCIAL_ENGINEERING = 5; +} +</pre> +<p> + When using the API, you must use constants that are not marked as deprecated. + You add threat type constants as arguments to the API. You may add as many + threat type constants as is required for your app. +</p> + +<h3 id="url-check-request"> + Send the URL check request +</h3> + +<p> + The API is agnostic to the scheme used, so you can pass the URL with or + without a scheme. For example, either +</p> + +<pre> +String url = "https://www.google.com"; +</pre> +<p> + or +</p> + +<pre> +String url = "www.google.com"; +</pre> +<p> + is valid. +</p> + +<pre> +SafetyNet.SafetyNetApi.lookupUri(mGoogleApiClient, url, + SafeBrowsingThreat.TYPE_POTENTIALLY_HARMFUL_APPLICATION, + SafeBrowsingThreat.TYPE_SOCIAL_ENGINEERING) + .setResultCallback( + new ResultCallback<SafetyNetApi.SafeBrowsingResult>() { + + @Override + public void onResult(SafetyNetApi.SafeBrowsingResult result) { + Status status = result.getStatus(); + if ((status != null) && status.isSuccess()) { + // Indicates communication with the service was successful. + // Identify any detected threats. + if (result.getDetectedThreats().isEmpty()) { + + } + } else { + // An error occurred. Let the user proceed without warning. + } + } +}); +</pre> +<h3 id="url-check-response"> + Read the URL check response +</h3> + +<p> + The result is provided as a list of {@code SafeBrowsingThreat} objects by + calling the {@code SafetyNetApi.SafeBrowsingResult.getDetectedThreats()} + method of the returned {@code SafetyNetApi.SafeBrowsingResult} object. If the + list is empty, no threats were detected; otherwise, calling {@code + SafeBrowsingThreat.getThreatType()} on each element in the list enumerates + the threats that were detected. +</p> + +<h2 id="warning-lang"> + Suggested Warning Language +</h2> + +<p> + Please see the Safe Browsing API Developer's Guide for <a href= + "https://developers.google.com/safe-browsing/v4/usage-limits#suggested--warning-language"> + suggested warning language</a>. +</p>
\ No newline at end of file diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 1fdc1f575bd4..49721cf42666 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -1667,10 +1667,10 @@ public final class Bitmap implements Parcelable { * and therefore is harmless. */ public void prepareToDraw() { - // TODO: Consider having this start an async upload? - // With inPurgeable no-op'd there's currently no use for this - // method, but it could have interesting future uses. checkRecycled("Can't prepareToDraw on a recycled bitmap!"); + // Kick off an update/upload of the bitmap outside of the normal + // draw path. + nativePrepareToDraw(mNativePtr); } /** @@ -1741,4 +1741,5 @@ public final class Bitmap implements Parcelable { private static native void nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap); private static native boolean nativeSameAs(long nativeBitmap0, long nativeBitmap1); private static native long nativeRefPixelRef(long nativeBitmap); + private static native void nativePrepareToDraw(long nativeBitmap); } diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp index ade8600ab78b..523924af5ef1 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -167,6 +167,10 @@ bool TextureCache::prefetchAndMarkInUse(void* ownerToken, const SkBitmap* bitmap return texture; } +bool TextureCache::prefetch(const SkBitmap* bitmap) { + return getCachedTexture(bitmap, AtlasUsageType::Use); +} + Texture* TextureCache::get(const SkBitmap* bitmap, AtlasUsageType atlasUsageType) { Texture* texture = getCachedTexture(bitmap, atlasUsageType); diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h index a4317cee73fd..0a61b6b1a522 100644 --- a/libs/hwui/TextureCache.h +++ b/libs/hwui/TextureCache.h @@ -78,6 +78,13 @@ public: bool prefetchAndMarkInUse(void* ownerToken, const SkBitmap* bitmap); /** + * Attempts to precache the SkBitmap. Returns true if a Texture was successfully + * acquired for the bitmap, false otherwise. Does not mark the Texture + * as in use and won't update currently in-use Textures. + */ + bool prefetch(const SkBitmap* bitmap); + + /** * Returns the texture associated with the specified bitmap from either within the cache, or * the AssetAtlas. If the texture cannot be found in the cache, a new texture is generated. */ diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index d68f0e383152..c6b258b5287b 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -562,11 +562,16 @@ void CanvasContext::draw() { swap.damage = screenDirty; swap.swapCompletedTime = systemTime(CLOCK_MONOTONIC); swap.vsyncTime = mRenderThread.timeLord().latestVsync(); - int durationUs; - mNativeSurface->query(NATIVE_WINDOW_LAST_DEQUEUE_DURATION, &durationUs); - swap.dequeueDuration = us2ns(durationUs); - mNativeSurface->query(NATIVE_WINDOW_LAST_QUEUE_DURATION, &durationUs); - swap.queueDuration = us2ns(durationUs); + if (mNativeSurface.get()) { + int durationUs; + mNativeSurface->query(NATIVE_WINDOW_LAST_DEQUEUE_DURATION, &durationUs); + swap.dequeueDuration = us2ns(durationUs); + mNativeSurface->query(NATIVE_WINDOW_LAST_QUEUE_DURATION, &durationUs); + swap.queueDuration = us2ns(durationUs); + } else { + swap.dequeueDuration = 0; + swap.queueDuration = 0; + } mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) = swap.dequeueDuration; mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 06a24b248bf6..fb1c8962328b 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -22,9 +22,11 @@ #include "Readback.h" #include "Rect.h" #include "renderthread/CanvasContext.h" +#include "renderthread/EglManager.h" #include "renderthread/RenderTask.h" #include "renderthread/RenderThread.h" #include "utils/Macros.h" +#include "utils/TimeUtils.h" namespace android { namespace uirenderer { @@ -44,6 +46,8 @@ namespace renderthread { typedef struct { \ a1; a2; a3; a4; a5; a6; a7; a8; \ } ARGS(name); \ + static_assert(std::is_trivially_destructible<ARGS(name)>::value, \ + "Error, ARGS must be trivially destructible!"); \ static void* Bridge_ ## name(ARGS(name)* args) #define SETUP_TASK(method) \ @@ -636,6 +640,41 @@ int RenderProxy::copySurfaceInto(sp<Surface>& surface, SkBitmap* bitmap) { reinterpret_cast<intptr_t>( staticPostAndWait(task) )); } +CREATE_BRIDGE2(prepareToDraw, RenderThread* thread, SkBitmap* bitmap) { + if (Caches::hasInstance() && args->thread->eglManager().hasEglContext()) { + ATRACE_NAME("Bitmap#prepareToDraw task"); + Caches::getInstance().textureCache.prefetch(args->bitmap); + } + delete args->bitmap; + args->bitmap = nullptr; + return nullptr; +} + +void RenderProxy::prepareToDraw(const SkBitmap& bitmap) { + // If we haven't spun up a hardware accelerated window yet, there's no + // point in precaching these bitmaps as it can't impact jank. + // We also don't know if we even will spin up a hardware-accelerated + // window or not. + if (!RenderThread::hasInstance()) return; + RenderThread* renderThread = &RenderThread::getInstance(); + SETUP_TASK(prepareToDraw); + args->thread = renderThread; + args->bitmap = new SkBitmap(bitmap); + nsecs_t lastVsync = renderThread->timeLord().latestVsync(); + nsecs_t estimatedNextVsync = lastVsync + renderThread->timeLord().frameIntervalNanos(); + nsecs_t timeToNextVsync = estimatedNextVsync - systemTime(CLOCK_MONOTONIC); + // We expect the UI thread to take 4ms and for RT to be active from VSYNC+4ms to + // VSYNC+12ms or so, so aim for the gap during which RT is expected to + // be idle + // TODO: Make this concept a first-class supported thing? RT could use + // knowledge of pending draws to better schedule this task + if (timeToNextVsync > -6_ms && timeToNextVsync < 1_ms) { + renderThread->queueAt(task, estimatedNextVsync + 8_ms); + } else { + renderThread->queue(task); + } +} + void RenderProxy::post(RenderTask* task) { mRenderThread.queue(task); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index e31062c83734..bb111bd0de25 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -129,6 +129,7 @@ public: ANDROID_API long getDroppedFrameReportCount(); ANDROID_API static int copySurfaceInto(sp<Surface>& surface, SkBitmap* bitmap); + ANDROID_API static void prepareToDraw(const SkBitmap& bitmap); private: RenderThread& mRenderThread; diff --git a/packages/SystemUI/res/drawable/lockscreen_fingerprint_error_state_to_fp.xml b/packages/SystemUI/res/drawable/lockscreen_fingerprint_error_state_to_fp.xml index 67e6a01640b1..e207cb3808c0 100644 --- a/packages/SystemUI/res/drawable/lockscreen_fingerprint_error_state_to_fp.xml +++ b/packages/SystemUI/res/drawable/lockscreen_fingerprint_error_state_to_fp.xml @@ -98,11 +98,11 @@ <path android:name="path_2" android:pathData="M 1.35900878906,6.76104736328 c 0.0,0.0 -2.69998168945,0.0 -2.69998168945,0.0 c 0.0,0.0 0.0,-2.69995117188 0.0,-2.69995117188 c 0.0,0.0 2.69998168945,0.0 2.69998168945,0.0 c 0.0,0.0 0.0,2.69995117188 0.0,2.69995117188 Z" - android:fillColor="#FFF2501D" /> + android:fillColor="@*android:color/system_error" /> <path android:name="path_1" android:pathData="M 1.35363769531,1.36633300781 c 0.0,0.0 -2.69998168945,0.0 -2.69998168945,0.0 c 0.0,0.0 0.0,-8.09997558594 0.0,-8.09997558594 c 0.0,0.0 2.69998168945,0.0 2.69998168945,0.0 c 0.0,0.0 0.0,8.09997558594 0.0,8.09997558594 Z" - android:fillColor="#FFF2501D" /> + android:fillColor="@*android:color/system_error" /> </group> </group> <group @@ -117,7 +117,7 @@ <path android:name="path_3" android:pathData="M 0.0101470947266,10.8087768555 c -5.96701049805,0.0 -10.8000183105,-4.8330078125 -10.8000183105,-10.8000488281 c 0.0,-5.96691894531 4.8330078125,-10.7999267578 10.8000183105,-10.7999267578 c 5.96697998047,0.0 10.799987793,4.8330078125 10.799987793,10.7999267578 c 0.0,5.96704101562 -4.8330078125,10.8000488281 -10.799987793,10.8000488281 Z" - android:strokeColor="#FFF2501D" + android:strokeColor="@*android:color/system_error" android:strokeWidth="2" android:trimPathStart="0" android:trimPathEnd="1" /> diff --git a/packages/SystemUI/res/drawable/lockscreen_fingerprint_fp_to_error_state.xml b/packages/SystemUI/res/drawable/lockscreen_fingerprint_fp_to_error_state.xml index bbadec197dfd..2b4babce277b 100644 --- a/packages/SystemUI/res/drawable/lockscreen_fingerprint_fp_to_error_state.xml +++ b/packages/SystemUI/res/drawable/lockscreen_fingerprint_fp_to_error_state.xml @@ -96,7 +96,7 @@ <path android:name="ridge_5_path_0" android:pathData="M -25.3591003418,-24.4138946533 c -0.569000244141,0.106399536133 -1.12660217285,0.140594482422 -1.45460510254,0.140594482422 c -1.29689025879,0.0 -2.53239440918,-0.343307495117 -3.62019348145,-1.12400817871 c -1.67700195312,-1.20349121094 -2.76950073242,-3.17008972168 -2.76950073242,-5.39189147949" - android:strokeColor="#FFF2501D" + android:strokeColor="@*android:color/system_error" android:strokeWidth="1.45" android:strokeLineCap="round" android:trimPathEnd="0" /> @@ -106,7 +106,7 @@ <path android:name="ridge_7_path_0" android:pathData="M -36.1409912109,-21.7843475342 c -1.00540161133,-1.19300842285 -1.57499694824,-1.9181060791 -2.36520385742,-3.50170898438 c -0.827560424805,-1.65869140625 -1.31352233887,-3.49159240723 -1.31352233887,-5.48489379883 c 0.0,-3.66279602051 2.96932983398,-6.63220214844 6.63221740723,-6.63220214844 c 3.6628112793,0.0 6.63220214844,2.96940612793 6.63220214844,6.63220214844" - android:strokeColor="#FFF2501D" + android:strokeColor="@*android:color/system_error" android:strokeWidth="1.45" android:strokeLineCap="round" android:trimPathEnd="0" /> @@ -116,7 +116,7 @@ <path android:name="ridge_6_path_0" android:pathData="M -42.1907958984,-25.6756896973 c -0.758117675781,-2.14370727539 -0.896545410156,-3.86891174316 -0.896545410156,-5.12921142578 c 0.0,-1.46069335938 0.249176025391,-2.84799194336 0.814682006836,-4.09748840332 c 1.56153869629,-3.45030212402 5.03434753418,-5.85076904297 9.0679473877,-5.85076904297 c 5.49430847168,0.0 9.94830322266,4.4539642334 9.94830322266,9.94825744629 c 0.0,1.83151245117 -1.48460388184,3.31610107422 -3.31610107422,3.31610107422 c -1.83149719238,0.0 -3.31610107422,-1.48469543457 -3.31610107422,-3.31610107422 c 0.0,-1.83139038086 -1.48458862305,-3.31610107422 -3.31610107422,-3.31610107422 c -1.83149719238,0.0 -3.31610107422,1.48471069336 -3.31610107422,3.31610107422 c 0.0,2.57020568848 0.989517211914,4.88710021973 2.60510253906,6.5865020752 c 1.22210693359,1.28550720215 2.43139648438,2.09950256348 4.47590637207,2.69030761719" - android:strokeColor="#FFF2501D" + android:strokeColor="@*android:color/system_error" android:strokeWidth="1.45" android:strokeLineCap="round" android:trimPathEnd="0" /> @@ -126,7 +126,7 @@ <path android:name="ridge_2_path_0" android:pathData="M -44.0646514893,-38.1672973633 c 1.19026184082,-1.77430725098 2.67503356934,-3.24531555176 4.55902099609,-4.27278137207 c 1.88395690918,-1.0274810791 4.04466247559,-1.61137390137 6.34175109863,-1.61137390137 c 2.28761291504,0.0 4.43991088867,0.579071044922 6.31831359863,1.59861755371 c 1.8784942627,1.01954650879 3.36059570312,2.4796295166 4.55279541016,4.24153137207" - android:strokeColor="#FFF2501D" + android:strokeColor="@*android:color/system_error" android:strokeWidth="1.45" android:strokeLineCap="round" android:trimPathStart="1" /> @@ -138,7 +138,7 @@ <path android:name="ridge_1_path_0" android:pathData="M 71.7812347412,97.0507202148 c -2.27149963379,-1.31344604492 -4.71360778809,-2.07006835938 -7.56221008301,-2.07006835938 c -2.84869384766,0.0 -5.23320007324,0.779556274414 -7.34411621094,2.07006835938" - android:strokeColor="#FFF2501D" + android:strokeColor="@*android:color/system_error" android:strokeWidth="1.45" android:strokeLineCap="round" android:trimPathEnd="0" /> @@ -157,11 +157,11 @@ <path android:name="path_2" android:pathData="M -1.3705291748,4.06730651855 c 0.0,0.0 0.036376953125,-0.0102386474609 0.036376953125,-0.0102386474609 c 0.0,0.0 -0.00682067871094,0.0040283203125 -0.00682067871093,0.0040283203125 c 0.0,0.0 -0.0161437988281,0.00479125976562 -0.0161437988281,0.00479125976563 c 0.0,0.0 -0.0134124755859,0.00141906738281 -0.0134124755859,0.00141906738281 Z" - android:fillColor="#FFF2501D" /> + android:fillColor="@*android:color/system_error" /> <path android:name="path_1" android:pathData="M -1.3737487793,-6.77532958984 c 0.0,0.0 0.00604248046875,0.0166625976562 0.00604248046876,0.0166625976562 c 0.0,0.0 0.0213623046875,0.0250244140625 0.0213623046875,0.0250244140625 c 0.0,0.0 -0.00604248046875,-0.0166625976562 -0.00604248046876,-0.0166625976562 c 0.0,0.0 -0.0213623046875,-0.0250244140625 -0.0213623046875,-0.0250244140625 Z" - android:fillColor="#FFF2501D" /> + android:fillColor="@*android:color/system_error" /> </group> </group> <group @@ -176,7 +176,7 @@ <path android:name="path_3" android:pathData="M 0.0101470947266,10.8087768555 c -5.96701049805,0.0 -10.8000183105,-4.8330078125 -10.8000183105,-10.8000488281 c 0.0,-5.96691894531 4.8330078125,-10.7999267578 10.8000183105,-10.7999267578 c 5.96697998047,0.0 10.799987793,4.8330078125 10.799987793,10.7999267578 c 0.0,5.96704101562 -4.8330078125,10.8000488281 -10.799987793,10.8000488281 Z" - android:strokeColor="#FFF2501D" + android:strokeColor="@*android:color/system_error" android:strokeWidth="2" android:trimPathStart="1" /> </group> diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java index 2d1f753898b7..9415b27f7570 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java @@ -16,6 +16,7 @@ package com.android.systemui.qs.tiles; +import android.app.ActivityManager; import android.content.Intent; import android.provider.Settings; import android.widget.Switch; @@ -29,11 +30,12 @@ import com.android.systemui.qs.QSTile; public class NightDisplayTile extends QSTile<QSTile.BooleanState> implements NightDisplayController.Callback { - private final NightDisplayController mController; + private NightDisplayController mController; + private boolean mIsListening; public NightDisplayTile(Host host) { super(host); - mController = new NightDisplayController(mContext); + mController = new NightDisplayController(mContext, ActivityManager.getCurrentUser()); } @Override @@ -54,6 +56,22 @@ public class NightDisplayTile extends QSTile<QSTile.BooleanState> } @Override + protected void handleUserSwitch(int newUserId) { + // Stop listening to the old controller. + if (mIsListening) { + mController.setListener(null); + } + + // Make a new controller for the new user. + mController = new NightDisplayController(mContext, newUserId); + if (mIsListening) { + mController.setListener(this); + } + + super.handleUserSwitch(newUserId); + } + + @Override protected void handleUpdateState(BooleanState state, Object arg) { final boolean isActivated = mController.isActivated(); state.value = isActivated; @@ -79,6 +97,7 @@ public class NightDisplayTile extends QSTile<QSTile.BooleanState> @Override protected void setListening(boolean listening) { + mIsListening = listening; if (listening) { mController.setListener(this); refreshState(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java index ca59831786b4..ba31e3e835c0 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java @@ -200,6 +200,10 @@ class BackgroundTaskLoader implements Runnable { if (cachedThumbnailData.thumbnail == null) { cachedThumbnailData.thumbnail = mDefaultThumbnail; + } else { + // Kick off an early upload of the bitmap to GL so + // that this won't jank the first frame it's drawn in. + cachedThumbnailData.thumbnail.prepareToDraw(); } // When svelte, we trim the memory to just the visible thumbnails when diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java index 319375950916..c46adf15861f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java @@ -164,6 +164,7 @@ public class TaskViewThumbnail extends View { /** Sets the thumbnail to a given bitmap. */ void setThumbnail(Bitmap bm, ActivityManager.TaskThumbnailInfo thumbnailInfo) { if (bm != null) { + bm.prepareToDraw(); mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); mDrawPaint.setShader(mBitmapShader); mThumbnailRect.set(0, 0, bm.getWidth(), bm.getHeight()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java index 405ccd601ff6..10facea1e8f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java @@ -334,6 +334,9 @@ public class QSTileHost implements QSTile.Host, Tunable { || ((CustomTile) tile).getUser() == currentUser)) { if (DEBUG) Log.d(TAG, "Adding " + tile); tile.removeCallbacks(); + if (!(tile instanceof CustomTile) && mCurrentUser != currentUser) { + tile.userSwitch(currentUser); + } newTiles.put(tileSpec, tile); } else { if (DEBUG) Log.d(TAG, "Creating tile: " + tileSpec); diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 5a6614074002..5624260a2658 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -3559,19 +3559,22 @@ final class ActivityStack { final ActivityState prevState = r.state; if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to FINISHING: " + r); r.state = ActivityState.FINISHING; + final boolean finishingActivityInNonFocusedStack + = r.task.stack != mStackSupervisor.getFocusedStack() + && prevState == ActivityState.PAUSED && mode == FINISH_AFTER_VISIBLE; if (mode == FINISH_IMMEDIATELY || (prevState == ActivityState.PAUSED - && (mode == FINISH_AFTER_PAUSE || mode == FINISH_AFTER_VISIBLE - || mStackId == PINNED_STACK_ID)) + && (mode == FINISH_AFTER_PAUSE || mStackId == PINNED_STACK_ID)) + || finishingActivityInNonFocusedStack || prevState == ActivityState.STOPPED || prevState == ActivityState.INITIALIZING) { r.makeFinishingLocked(); boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm"); - if (prevState == ActivityState.PAUSED && mode == FINISH_AFTER_VISIBLE) { - // Finishing activity that was in paused state - this can happen if it was in - // not currently focused stack. Need to make something visible in its place. + if (finishingActivityInNonFocusedStack) { + // Finishing activity that was in paused state and it was in not currently focused + // stack, need to make something visible in its place. mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); } if (activityRemoved) { diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java index 2346f8547550..16a0def7d5a5 100644 --- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java +++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java @@ -574,6 +574,7 @@ public class AppLaunch extends InstrumentationTestCase { mLaunchIntent = intent; mForceStopBeforeLaunch = forceStopBeforeLaunch; mLaunchReason = launchReason; + mResult = -1L; } public Long getResult() { diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index 9976d00fa872..59da467a1cc3 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -529,6 +529,16 @@ struct FeatureGroup { int openGLESVersion; }; +static bool hasFeature(const char* name, const FeatureGroup& grp, + const KeyedVector<String8, ImpliedFeature>& implied) { + String8 name8(name); + ssize_t idx = grp.features.indexOfKey(name8); + if (idx < 0) { + idx = implied.indexOfKey(name8); + } + return idx >= 0; +} + static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures, const char* name, const char* reason, bool sdk23) { String8 name8(name); @@ -616,9 +626,16 @@ static void addParentFeatures(FeatureGroup* grp, const String8& name) { } else if (name == "android.hardware.location.gps" || name == "android.hardware.location.network") { grp->features.add(String8("android.hardware.location"), Feature(true)); + } else if (name == "android.hardware.faketouch.multitouch") { + grp->features.add(String8("android.hardware.faketouch"), Feature(true)); + } else if (name == "android.hardware.faketouch.multitouch.distinct" || + name == "android.hardware.faketouch.multitouch.jazzhands") { + grp->features.add(String8("android.hardware.faketouch.multitouch"), Feature(true)); + grp->features.add(String8("android.hardware.faketouch"), Feature(true)); } else if (name == "android.hardware.touchscreen.multitouch") { grp->features.add(String8("android.hardware.touchscreen"), Feature(true)); - } else if (name == "android.hardware.touchscreen.multitouch.distinct") { + } else if (name == "android.hardware.touchscreen.multitouch.distinct" || + name == "android.hardware.touchscreen.multitouch.jazzhands") { grp->features.add(String8("android.hardware.touchscreen.multitouch"), Feature(true)); grp->features.add(String8("android.hardware.touchscreen"), Feature(true)); } else if (name == "android.hardware.opengles.aep") { @@ -2005,8 +2022,12 @@ int doDump(Bundle* bundle) } } - addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen", - "default feature for all apps", false); + // If the app hasn't declared the touchscreen as a feature requirement (either + // directly or implied, required or not), then the faketouch feature is implied. + if (!hasFeature("android.hardware.touchscreen", commonFeatures, impliedFeatures)) { + addImpliedFeature(&impliedFeatures, "android.hardware.faketouch", + "default feature for all apps", false); + } const size_t numFeatureGroups = featureGroups.size(); if (numFeatureGroups == 0) { |