xref: /aosp_15_r20/external/angle/build/android/docs/class_verification_failures.md (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1# Class Verification Failures
2
3[TOC]
4
5## This document is obsolete
6
7While class verification failures still exist, our Java optimizer, R8, has
8solved this problem for us. Developers should not have to worry about this
9problem unless there is a bug in R8. See [this bug](http://b/138781768) for where
10they implemented this solution for us.
11
12## What's this all about?
13
14This document aims to explain class verification on Android, how this can affect
15app performance, how to identify problems, and chromium-specific solutions. For
16simplicity, this document focuses on how class verification is implemented by
17ART, the virtual machine which replaced Dalvik starting in Android Lollipop.
18
19## What is class verification?
20
21The Java language requires any virtual machine to _verify_ the class files it
22loads and executes. Generally, verification is extra work the virtual machine is
23responsible for doing, on top of the work of loading the class and performing
24[class initialization][1].
25
26A class may fail verification for a wide variety of reasons, but in practice
27it's usually because the class's code refers to unknown classes or methods. An
28example case might look like:
29
30```java
31public class WindowHelper {
32    // ...
33    public boolean isWideColorGamut() {
34        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
35            return mWindow.isWideColorGamut();
36        }
37        return false;
38    }
39}
40```
41
42### Why does that fail?
43
44In this example, `WindowHelper` is a helper class intended to help callers
45figure out wide color gamut support, even on pre-OMR1 devices. However, this
46class will fail class verification on pre-OMR1 devices, because it refers to
47[`Window#isWideColorGamut()`][2] (new-in-OMR1), which appears to be an undefined
48method.
49
50### Huh? But we have an SDK check!
51
52SDK checks are completely irrelevant for class verification. Although readers
53can see we'll never call the new-in-OMR1 API unless we're on >= OMR1 devices,
54the Oreo version of ART doesn't know `isWideColorGamut()` was added in next
55year's release. From ART's perspective, we may as well be calling
56`methodWhichDoesNotExist()`, which would clearly be unsafe.
57
58All the SDK check does is protect us from crashing at runtime if we call this
59method on Oreo or below.
60
61### Class verification on ART
62
63While the above is a mostly general description of class verification, it's
64important to understand how the Android runtime handles this.
65
66Since class verification is extra work, ART has an optimization called **AOT
67("ahead-of-time") verification**¹. Immediately after installing an app, ART will
68scan the dex files and verify as many classes as it can. If a class fails
69verification, this is usually a "soft failure" (hard failures are uncommon), and
70ART marks the class with the status `RetryVerificationAtRuntime`.
71
72`RetryVerificationAtRuntime`, as the name suggests, means ART must try again to
73verify the class at runtime. ART does so the first time you access the class
74(right before class initialization/`<clinit>()` method). However, depending on
75the class, this verification step can be very expensive (we've observed cases
76which take [several milliseconds][3]). Since apps tend to initialize most of
77their classes during startup, verification significantly increases startup time.
78
79Another minor cost to failing class verification is that ART cannot optimize
80classes which fail verification, so **all** methods in the class will perform
81slower at runtime, even after the verification step.
82
83*** aside
84¹ AOT _verification_ should not be confused with AOT _compilation_ (another ART
85feature). Unlike compilation, AOT verification happens during install time for
86every application, whereas recent versions of ART aim to apply AOT compilation
87selectively to optimize space.
88***
89
90## Chromium's solution
91
92**Note:** This section is no longer relevant as R8 has fixed this for us. We intend
93to remove these ApiHelperFor classes - see [this bug](https://crbug.com/1302156).
94
95In Chromium, we try to avoid doing class verification at runtime by
96manually out-of-lining all Android API usage like so:
97
98```java
99public class ApiHelperForOMR1 {
100    public static boolean isWideColorGamut(Window window) {
101        return window.isWideColorGamut();
102    }
103}
104
105public class WindowHelper {
106    // ...
107    public boolean isWideColorGamut() {
108        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
109            return ApiHelperForOMR1.isWideColorGamut(mWindow);
110        }
111        return false;
112    }
113}
114```
115
116This pushes the class verification failure out of `WindowHelper` and into the
117new `ApiHelperForOMR1` class. There's no magic here: `ApiHelperForOMR1` will
118fail class verification on Oreo and below, for the same reason `WindowHelper`
119did previously.
120
121The key is that, while `WindowHelper` is used on all API levels, it only calls
122into `ApiHelperForOMR1` on OMR1 and above. Because we never use
123`ApiHelperForOMR1` on Oreo and below, we never load and initialize the class,
124and thanks to ART's lazy runtime class verification, we never actually retry
125verification. **Note:** `list_class_verification_failures.py` will still list
126`ApiHelperFor*` classes in its output, although these don't cause performance
127issues.
128
129### Creating ApiHelperFor\* classes
130
131There are several examples throughout the code base, but such classes should
132look as follows:
133
134```java
135/**
136 * Utility class to use new APIs that were added in O_MR1 (API level 27).
137 * These need to exist in a separate class so that Android framework can successfully verify
138 * classes without encountering the new APIs.
139 */
140@RequiresApi(Build.VERSION_CODES.O_MR1)
141public class ApiHelperForOMR1 {
142    private ApiHelperForOMR1() {}
143
144    // ...
145}
146```
147
148* `@RequiresApi(Build.VERSION_CODES.O_MR1)`: this tells Android Lint it's OK to
149  use OMR1 APIs since this class is only used on OMR1 and above. Substitute
150  `O_MR1` for the [appropriate constant][4], depending when the APIs were
151  introduced.
152* Don't put any `SDK_INT` checks inside this class, because it must only be
153  called on >= OMR1.
154* R8 is smart enough not to inline methods where doing so would introduce
155  verification failures (b/138781768)
156
157### Out-of-lining if your method has a new type in its signature
158
159Sometimes you'll run into a situation where a class **needs** to have a method
160which either accepts a parameter which is a new type or returns a new type
161(e.g., externally-facing code, such as WebView's glue layer). Even though it's
162impossible to write such a class without referring to the new type, it's still
163possible to avoid failing class verification. ART has a useful optimization: if
164your class only moves a value between registers (i.e., it doesn't call any
165methods or fields on the value), then ART will not check for the existence of
166that value's type. This means you can write your class like so:
167
168```java
169public class FooBar {
170    // FooBar needs to have the getNewTypeInAndroidP method, but it would be
171    // expensive to fail verification. This method will only be called on >= P
172    // but other methods on the class will be used on lower OS versions (and
173    // also can't be factored into another class).
174    public NewTypeInAndroidP getNewTypeInAndroidP() {
175        assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
176        // Stores a NewTypeInAndroidP in the return register, but doesn't do
177        // anything else with it
178        return ApiHelperForP.getNewTypeInAndroidP();
179    }
180
181    // ...
182}
183
184@VerifiesOnP
185@RequiresApi(Build.VERSION_CODES.P)
186public class ApiHelperForP {
187    public static NewTypeInAndroidP getNewTypeInAndroidP() {
188        return new NewTypeInAndroidP();
189    }
190
191    // ...
192}
193```
194
195**Note:** this only works in ART (L+), not Dalvik (KitKat and earlier).
196
197## Investigating class verification failures
198
199Class verification is generally surprising and nonintuitive. Fortunately, the
200ART team have provided tools to investigate errors (and the chromium team has
201built helpful wrappers).
202
203### Listing failing classes
204
205The main starting point is to figure out which classes fail verification (those
206which ART marks as `RetryVerificationAtRuntime`). This can be done for **any
207Android app** (it doesn't have to be from the chromium project) like so:
208
209```shell
210# Install the app first. Using Chrome as an example.
211autoninja -C out/Default chrome_public_apk
212out/Default/bin/chrome_public_apk install
213
214# List all classes marked as 'RetryVerificationAtRuntime'
215build/android/list_class_verification_failures.py --package="org.chromium.chrome"
216W    0.000s Main  Skipping deobfuscation because no map file was provided.
217first.failing.Class
218second.failing.Class
219...
220```
221
222"Skipping deobfuscation because no map file was provided" is a warning, since
223many Android applications (including Chrome's release builds) are built with
224proguard (or similar tools) to obfuscate Java classes and shrink code. Although
225it's safe to ignore this warning if you don't obfuscate Java code, the script
226knows how to deobfuscate classes for you (useful for `is_debug = true` or
227`is_java_debug = true`):
228
229```shell
230build/android/list_class_verification_failures.py --package="org.chromium.chrome" \
231  --mapping=<path/to/file.mapping> # ex. out/Release/apks/ChromePublic.apk.mapping
232android.support.design.widget.AppBarLayout
233android.support.design.widget.TextInputLayout
234...
235```
236
237Googlers can also download mappings for [official
238builds](http://go/webview-official-builds).
239
240### Understanding the reason for the failure
241
242ART team also provide tooling for this. You can configure ART on a rooted device
243to log all class verification failures (during installation), at which point the
244cause is much clearer:
245
246```shell
247# Enable ART logging (requires root). Note the 2 pairs of quotes!
248adb root
249adb shell setprop dalvik.vm.dex2oat-flags '"--runtime-arg -verbose:verifier"'
250
251# Restart Android services to pick up the settings
252adb shell stop && adb shell start
253
254# Optional: clear logs which aren't relevant
255adb logcat -c
256
257# Install the app and check for ART logs
258adb install -d -r out/Default/apks/ChromePublic.apk
259adb logcat | grep 'dex2oat'
260...
261... I dex2oat : Soft verification failures in boolean org.chromium.content.browser.selection.SelectionPopupControllerImpl.b(android.view.ActionMode, android.view.Menu)
262... I dex2oat : boolean org.chromium.content.browser.selection.SelectionPopupControllerImpl.b(android.view.ActionMode, android.view.Menu): [0xF0] couldn't find method android.view.textclassifier.TextClassification.getActions ()Ljava/util/List;
263... I dex2oat : boolean org.chromium.content.browser.selection.SelectionPopupControllerImpl.b(android.view.ActionMode, android.view.Menu): [0xFA] couldn't find method android.view.textclassifier.TextClassification.getActions ()Ljava/util/List;
264...
265```
266
267*** note
268**Note:** you may want to avoid `adb` wrapper scripts (ex.
269`out/Default/bin/chrome_public_apk install`). These scripts cache the package
270manager state to optimize away idempotent installs. However in this case, we
271**do** want to trigger idempotent installs, because we want to re-trigger AOT
272verification.
273***
274
275In the above example, `SelectionPopupControllerImpl` fails verification on Oreo
276(API 26) because it refers to [`TextClassification.getActions()`][5], which was
277added in Pie (API 28). If `SelectionPopupControllerImpl` is used on pre-Pie
278devices, then `TextClassification.getActions()` must be out-of-lined.
279
280## See also
281
282* Bugs or questions? Contact [email protected]
283* ART team's Google I/O talks: [2014](https://youtu.be/EBlTzQsUoOw) and later
284  years
285* Analysis of class verification in Chrome and WebView (Google-only
286  [doc](http://go/class-verification-chromium-analysis))
287* Presentation on class verification in Chrome and WebView (Google-only
288  [slide deck](http://go/class-verification-chromium-slides))
289
290[1]: https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.5
291[2]: https://developer.android.com/reference/android/view/Window.html#isWideColorGamut()
292[3]: https://bugs.chromium.org/p/chromium/issues/detail?id=838702
293[4]: https://developer.android.com/reference/android/os/Build.VERSION_CODES
294[5]: https://developer.android.com/reference/android/view/textclassifier/TextClassification.html#getActions()
295