diff options
177 files changed, 14855 insertions, 454 deletions
diff --git a/Android.bp b/Android.bp index 431f0b9317ac..8eebcbbf77d7 100644 --- a/Android.bp +++ b/Android.bp @@ -163,6 +163,12 @@ java_library { //same purpose. "//external/robolectric:__subpackages__", "//frameworks/layoutlib:__subpackages__", + + // This is for the same purpose as robolectric -- to build "framework.jar" for host-side + // testing. + // TODO: Once Ravenwood is stable, move the host side jar targets to this directory, + // and remove this line. + "//frameworks/base/tools/hoststubgen:__subpackages__", ], } @@ -405,6 +411,7 @@ java_defaults { "audiopolicy-aidl-java", "sounddose-aidl-java", "modules-utils-expresslog", + "hoststubgen-annotations", ], } diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index 082a3361be4e..f4d783a7c2b7 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -625,12 +625,13 @@ public abstract class CameraDevice implements AutoCloseable { * <style scoped> * #rb { border-right-width: thick; } * </style> + * + * <h5>LEGACY-level guaranteed configurations</h5> + * * <p>Legacy devices ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY}) support at * least the following stream combinations: * - * <h5>LEGACY-level guaranteed configurations</h5> - * * <table> * <tr> <th colspan="2" id="rb">Target 1</th> <th colspan="2" id="rb">Target 2</th> <th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr> * <tr> <th>Type</th><th id="rb">Max size</th> <th>Type</th><th id="rb">Max size</th> <th>Type</th><th id="rb">Max size</th></tr> @@ -645,13 +646,13 @@ public abstract class CameraDevice implements AutoCloseable { * </table><br> * </p> * + * <h5>LIMITED-level additional guaranteed configurations</h5> + * * <p>Limited-level ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED}) devices * support at least the following stream combinations in addition to those for * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY} devices: * - * <h5>LIMITED-level additional guaranteed configurations</h5> - * * <table> * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr> * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr> @@ -664,13 +665,13 @@ public abstract class CameraDevice implements AutoCloseable { * </table><br> * </p> * + * <h5>FULL-level additional guaranteed configurations</h5> + * * <p>FULL-level ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}) devices * support at least the following stream combinations in addition to those for * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices: * - * <h5>FULL-level additional guaranteed configurations</h5> - * * <table> * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr> * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th> </tr> @@ -683,14 +684,14 @@ public abstract class CameraDevice implements AutoCloseable { * </table><br> * </p> * + * <h5>RAW-capability additional guaranteed configurations</h5> + * * <p>RAW-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} includes * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_RAW RAW}) devices additionally support * at least the following stream combinations on both * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL} and * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices: * - * <h5>RAW-capability additional guaranteed configurations</h5> - * * <table> * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr> * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th> </tr> @@ -705,6 +706,8 @@ public abstract class CameraDevice implements AutoCloseable { * </table><br> * </p> * + * <h5>BURST-capability additional guaranteed configurations</h5> + * * <p>BURST-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} includes * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE BURST_CAPTURE}) devices * support at least the below stream combinations in addition to those for @@ -713,8 +716,6 @@ public abstract class CameraDevice implements AutoCloseable { * list for FULL-level devices, so this table is only relevant for LIMITED-level devices that * support the BURST_CAPTURE capability. * - * <h5>BURST-capability additional guaranteed configurations</h5> - * * <table> * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th rowspan="2">Sample use case(s)</th> </tr> * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th> </tr> @@ -724,6 +725,8 @@ public abstract class CameraDevice implements AutoCloseable { * </table><br> * </p> * + * <h5>LEVEL-3 additional guaranteed configurations</h5> + * * <p>LEVEL-3 ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_3 LEVEL_3}) * support at least the following stream combinations in addition to the combinations for @@ -731,8 +734,6 @@ public abstract class CameraDevice implements AutoCloseable { * RAW capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} includes * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_RAW RAW}): * - * <h5>LEVEL-3 additional guaranteed configurations</h5> - * * <table> * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th colspan="2" id="rb">Target 4</th><th rowspan="2">Sample use case(s)</th> </tr> * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th> </tr> @@ -741,14 +742,16 @@ public abstract class CameraDevice implements AutoCloseable { * </table><br> * </p> * - *<p>BACKWARD_COMPATIBLE devices capable of streaming concurrently with other devices as described by - * {@link android.hardware.camera2.CameraManager#getConcurrentCameraIds} have the + * <h5>Concurrent stream guaranteed configurations</h5> + * + * <p>BACKWARD_COMPATIBLE devices capable of streaming concurrently with other devices as + * described by {@link android.hardware.camera2.CameraManager#getConcurrentCameraIds} have the * following guaranteed streams (when streaming concurrently with other devices)</p> + * * <p> Note: The sizes mentioned for these concurrent streams are the maximum sizes guaranteed * to be supported. Sizes smaller than these, obtained by {@link StreamConfigurationMap#getOutputSizes} for a particular format, are supported as well. </p> * - * <h5>Concurrent stream guaranteed configurations</h5> - * + * <p> * <table> * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th rowspan="2">Sample use case(s)</th> </tr> * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th> </tr> @@ -784,6 +787,8 @@ public abstract class CameraDevice implements AutoCloseable { * level and capabilities. Calling createCaptureSession with both JPEG and HEIC outputs is not * supported.</p> * + * <h5>LEGACY-level additional guaranteed combinations with multi-resolution outputs</h5> + * * <p>Devices capable of multi-resolution output for a particular format ( * {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getOutputInfo} * returns a non-empty list) support using {@link MultiResolutionImageReader} for MAXIMUM @@ -794,8 +799,6 @@ public abstract class CameraDevice implements AutoCloseable { * stream combinations ({@code MULTI_RES} in the Max size column refers to a {@link * MultiResolutionImageReader} created based on the variable max resolutions supported): * - * <h5>LEGACY-level additional guaranteed combinations with MultiResolutionoutputs</h5> - * * <table> * <tr> <th colspan="2" id="rb">Target 1</th> <th colspan="2" id="rb">Target 2</th> <th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr> * <tr> <th>Type</th><th id="rb">Max size</th> <th>Type</th><th id="rb">Max size</th> <th>Type</th><th id="rb">Max size</th></tr> @@ -804,8 +807,12 @@ public abstract class CameraDevice implements AutoCloseable { * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td colspan="2" id="rb"></td> <td>Standard still imaging.</td> </tr> * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td>Still capture plus in-app processing.</td> </tr> * </table><br> + * </p> + * + * <h5>LIMITED-level additional guaranteed configurations with multi-resolution outputs</h5> + * + * <p> * <table> - * <tr><th colspan="7">LIMITED-level additional guaranteed configurations with MultiResolutionoutputs</th></tr> * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr> * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr> * <tr> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td>Two-input in-app processing with still capture.</td> </tr> @@ -813,11 +820,11 @@ public abstract class CameraDevice implements AutoCloseable { * The same logic applies to other hardware levels and capabilities. * </p> * - * <p> Devices with the ULTRA_HIGH_RESOLUTION_SENSOR capability have some additional guarantees - * which clients can take advantage of : </p> - * * <h5>Additional guaranteed combinations for ULTRA_HIGH_RESOLUTION sensors</h5> * + * <p> Devices with the ULTRA_HIGH_RESOLUTION_SENSOR capability have some additional guarantees + * which clients can take advantage of: + * * <table> * <tr> <th colspan="3" id="rb">Target 1</th> <th colspan="3" id="rb">Target 2</th> <th colspan="3" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr> * <tr> <th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th> <th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th> <th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th></tr> @@ -825,6 +832,7 @@ public abstract class CameraDevice implements AutoCloseable { * <tr> <td>{@code YUV / JPEG / RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td id="rb">{@code PRIV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PRIV / YUV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code RECORD}</td> <td>Ultra high res still capture with preview + app based RECORD size analysis</td> </tr> * <tr> <td>{@code YUV / JPEG / RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td id="rb">{@code PRIV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code JPEG / YUV / RAW}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code MAX}</td> <td>Ultra high res still image capture with preview + default sensor pixel mode analysis stream</td> </tr> * </table><br> + * </p> * * <p> Here, SC Map, refers to the {@link StreamConfigurationMap}, the target stream sizes must * be chosen from. {@code DEFAULT} refers to the default sensor pixel mode {@link @@ -834,17 +842,17 @@ public abstract class CameraDevice implements AutoCloseable { * Note: The same capture request must not mix targets from * {@link StreamConfigurationMap}s corresponding to different sensor pixel modes. </p> * - * <p> 10-bit output capable - * {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT} - * devices support at least the following stream combinations: </p> - * * <h5>10-bit output additional guaranteed configurations</h5> * + * <p>10-bit output capable + * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT} + * devices support at least the following stream combinations: + * * <table> * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr> * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr> - * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> }</td> <td colspan="4" id="rb"></td> <td>Simple preview, GPU video processing, or no-preview video recording.</td> </tr> - * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> }</td> <td colspan="4" id="rb"></td> <td>In-application video/image processing.</td> </tr> + * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> </td> <td colspan="4" id="rb"></td> <td>Simple preview, GPU video processing, or no-preview video recording.</td> </tr> + * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> </td> <td colspan="4" id="rb"></td> <td>In-application video/image processing.</td> </tr> * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM }</td> <td colspan="2" id="rb"></td> <td>Standard still imaging.</td> </tr> * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM }</td> <td colspan="2" id="rb"></td> <td>Maximum-resolution in-app processing with preview.</td> </tr> * <tr> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM }</td> <td colspan="2" id="rb"></td> <td>Maximum-resolution two-input in-app processing.</td> </tr> @@ -852,6 +860,8 @@ public abstract class CameraDevice implements AutoCloseable { * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code RECORD }</td> <td>{@code YUV}</td><td id="rb">{@code RECORD }</td> <td>High-resolution recording with in-app snapshot.</td> </tr> * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV }</td><td id="rb">{@code RECORD }</td> <td>{@code JPEG}</td><td id="rb">{@code RECORD }</td> <td>High-resolution recording with video snapshot.</td> </tr> * </table><br> + * </p> + * * <p>Here PRIV can be either 8 or 10-bit {@link android.graphics.ImageFormat#PRIVATE} pixel * format. YUV can be either {@link android.graphics.ImageFormat#YUV_420_888} or * {@link android.graphics.ImageFormat#YCBCR_P010}. @@ -887,13 +897,13 @@ public abstract class CameraDevice implements AutoCloseable { * {@link CameraDevice#isSessionConfigurationSupported} to ensure that this particular * configuration is supported.</p> * + * <h5>STREAM_USE_CASE capability additional guaranteed configurations</h5> + * * <p>Devices with the STREAM_USE_CASE capability ({@link * CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} includes {@link * CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE}) support below additional * stream combinations: * - * <h5>STREAM_USE_CASE capability additional guaranteed configurations</h5> - * * <table> * <tr><th colspan="3" id="rb">Target 1</th><th colspan="3" id="rb">Target 2</th><th colspan="3" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr> * <tr><th>Type</th><th id="rb">Max size</th><th>Usecase</th><th>Type</th><th id="rb">Max size</th><th>Usecase</th><th>Type</th><th id="rb">Max size</th><th>Usecase</th> </tr> @@ -913,12 +923,12 @@ public abstract class CameraDevice implements AutoCloseable { * </table><br> * </p> * + * <h5>STREAM_USE_CASE_CROPPED_RAW capability additional guaranteed configurations</h5> + * * <p>Devices that include the {@link CameraMetadata#SCALER_AVAILABLE_STREAM_USE_CASES_CROPPED_RAW} * stream use-case in {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES}, * support the additional stream combinations below: * - * <h5>STREAM_USE_CASE_CROPPED_RAW capability additional guaranteed configurations</h5> - * * <table> * <tr><th colspan="3" id="rb">Target 1</th><th colspan="3" id="rb">Target 2</th><th colspan="3" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr> * <tr><th>Type</th><th id="rb">Max size</th><th>Usecase</th><th>Type</th><th id="rb">Max size</th><th>Usecase</th><th>Type</th><th id="rb">Max size</th><th>Usecase</th> </tr> @@ -926,15 +936,17 @@ public abstract class CameraDevice implements AutoCloseable { * <tr> <td>{@code PRIV / YUV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code CROPPED_RAW}</td> <td colspan="3" id="rb"></td> <td>Preview with cropped RAW still capture</td> </tr> * <tr> <td>{@code PRIV / YUV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code CROPPED_RAW}</td> <td>Preview with YUV / JPEG and cropped RAW still capture</td> </tr> * <tr> <td>{@code PRIV / YUV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV / YUV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code VIDEO_RECORD / PREVIEW}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code CROPPED_RAW}</td> <td>Video recording with preview and cropped RAW still capture</td> </tr> + * </table><br> + * </p> * + * <h5>Preview stabilization guaranteed stream configurations</h5> * - *<p> For devices where {@link CameraCharacteristics#CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES} - * includes {@link CameraMetadata#CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION}, + * <p>For devices where + * {@link CameraCharacteristics#CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES} includes + * {@link CameraMetadata#CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION}, * the following stream combinations are guaranteed, * for CaptureRequests where {@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE} is set to - * {@link CameraMetadata#CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION} <p> - * - * <h5>Preview stabilization guaranteed stream configurations</h5> + * {@link CameraMetadata#CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION} * * <table> * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th rowspan="2">Sample use case(s)</th> </tr> @@ -943,6 +955,8 @@ public abstract class CameraDevice implements AutoCloseable { * <tr> <td>{@code PRIV / YUV}</td><td id="rb">{@code s1440p}</td> <td>{@code JPEG / YUV}</td><td id="rb">{@code MAXIMUM }</td><td>Standard still imaging with stabilized preview.</td> </tr> * <tr> <td>{@code PRIV / YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV / YUV}</td><td id="rb">{@code s1440p }</td><td>High-resolution recording with stabilized preview and recording stream.</td> </tr> * </table><br> + * </p> + * * <p> * For the maximum size column, PREVIEW refers to the best size match to the device's screen * resolution, or to 1080p (1920x1080), whichever is smaller. RECORD refers to the camera diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig index 361e244ac876..e00381ff0298 100644 --- a/core/java/android/os/vibrator/flags.aconfig +++ b/core/java/android/os/vibrator/flags.aconfig @@ -1,7 +1,7 @@ package: "android.os.vibrator" flag { - namespace: "vibrator" + namespace: "haptics" name: "use_vibrator_haptic_feedback" description: "Enables performHapticFeedback to directly use the vibrator service instead of going through the window session" bug: "295459081" diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java index da094893abb9..b4d7a943ff17 100755 --- a/core/java/android/widget/SearchView.java +++ b/core/java/android/widget/SearchView.java @@ -2109,7 +2109,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { return; } - if (imm.isActive(this)) { + if (imm.hasActiveInputConnection(this)) { // This means that SearchAutoComplete is already connected to the IME. // InputMethodManager#showSoftInput() is guaranteed to pass client-side focus check. mHasPendingShowSoftInputRequest = false; diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index e2f407294458..842542f4f43b 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -4945,7 +4945,9 @@ public class AudioManager { synchronized (this) { while (!mQuit) { final long timeToWait = timeOutTime - java.lang.System.currentTimeMillis(); - if (timeToWait < 0) { break; } + if (timeToWait <= 0) { + break; + } this.wait(timeToWait); } } diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java index c4f2159a23dc..12db8c0824db 100644 --- a/media/java/android/media/RingtoneManager.java +++ b/media/java/android/media/RingtoneManager.java @@ -30,6 +30,7 @@ import android.content.ContentProvider; import android.content.ContentResolver; import android.content.ContentUris; import android.content.Context; +import android.content.Intent; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.UserInfo; import android.content.res.AssetFileDescriptor; @@ -120,6 +121,53 @@ public class RingtoneManager { public static final String ACTION_RINGTONE_PICKER = "android.intent.action.RINGTONE_PICKER"; /** + * Given to the ringtone picker as a string that represents the category of ringtone picker that + * should be used. This value should also be returned once a ringtone is selected. + * <p> + * The categories are: + * <li>{@link #CATEGORY_RINGTONE_PICKER_SOUND} + * <li>{@link #CATEGORY_RINGTONE_PICKER_VIBRATION} + * <li>{@link #CATEGORY_RINGTONE_PICKER_RINGTONE} + * <li>{@link Intent#CATEGORY_DEFAULT} + * + * <p> If the category is {@link Intent#CATEGORY_DEFAULT} or absent, then the picker will + * default to a sound-only ringtone picker. + * + * <p> If the selected category was not supported, then the returned category will be null. + * + * @hide + */ + public static final String EXTRA_RINGTONE_PICKER_CATEGORY = + "android.intent.extra.ringtone.RINGTONE_PICKER_CATEGORY"; + + /** + * A sound-only ringtone picker. + * + * @hide + * @see #EXTRA_RINGTONE_PICKER_CATEGORY + */ + public static final String CATEGORY_RINGTONE_PICKER_SOUND = + "android.net.category.RINGTONE_PICKER_SOUND"; + + /** + * A vibration-only ringtone picker. + * + * @hide + * @see #EXTRA_RINGTONE_PICKER_CATEGORY + */ + public static final String CATEGORY_RINGTONE_PICKER_VIBRATION = + "android.net.category.RINGTONE_PICKER_VIBRATION"; + + /** + * A combined sound and vibration ringtone picker. + * + * @hide + * @see #EXTRA_RINGTONE_PICKER_CATEGORY + */ + public static final String CATEGORY_RINGTONE_PICKER_RINGTONE = + "android.net.category.RINGTONE_PICKER_RINGTONE"; + + /** * Given to the ringtone picker as a boolean. Whether to show an item for * "Default". * @@ -160,6 +208,18 @@ public class RingtoneManager { */ public static final String EXTRA_RINGTONE_EXISTING_URI = "android.intent.extra.ringtone.EXISTING_URI"; + + /** + * Similar to #EXTRA_RINGTONE_EXISTING_URI but the {@link Uri} can include both sound and + * vibration. + * <p>This can include silent sound/vibration explicitly by setting that part of the URI to + * null. + * + * @hide + * @see #ACTION_RINGTONE_PICKER + */ + public static final String EXTRA_RINGTONE_EXISTING_RINGTONE_URI = + "android.intent.extra.ringtone.RINGTONE_EXISTING_RINGTONE_URI"; /** * Given to the ringtone picker as a {@link Uri}. The {@link Uri} of the diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 016936aae956..c13480672102 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -934,4 +934,12 @@ <!-- Flag controlling whether visual query attention detection has been enabled. --> <bool name="config_enableVisualQueryAttentionDetection">false</bool> + + <!-- + Whether the scene container framework is enabled. + + The scene container framework is a newer (2023) way to organize the various "scenes" between the + bouncer, lockscreen, shade, and quick settings. + --> + <bool name="config_sceneContainerFrameworkEnabled">true</bool> </resources> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index d90785dd266d..4d906c474970 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -83,6 +83,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.scene.shared.flag.SceneContainerFlags; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -123,6 +124,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final UserSwitcherController mUserSwitcherController; private final GlobalSettings mGlobalSettings; private final FeatureFlags mFeatureFlags; + private final SceneContainerFlags mSceneContainerFlags; private final SessionTracker mSessionTracker; private final Optional<SideFpsController> mSideFpsController; private final FalsingA11yDelegate mFalsingA11yDelegate; @@ -433,6 +435,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard FalsingManager falsingManager, UserSwitcherController userSwitcherController, FeatureFlags featureFlags, + SceneContainerFlags sceneContainerFlags, GlobalSettings globalSettings, SessionTracker sessionTracker, Optional<SideFpsController> sideFpsController, @@ -466,6 +469,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mFalsingManager = falsingManager; mUserSwitcherController = userSwitcherController; mFeatureFlags = featureFlags; + mSceneContainerFlags = sceneContainerFlags; mGlobalSettings = globalSettings; mSessionTracker = sessionTracker; mSideFpsController = sideFpsController; @@ -503,7 +507,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard showPrimarySecurityScreen(false); - if (mFeatureFlags.isEnabled(Flags.SCENE_CONTAINER)) { + if (mSceneContainerFlags.isEnabled()) { // When the scene framework says that the lockscreen has been dismissed, dismiss the // keyguard here, revealing the underlying app or launcher: mSceneTransitionCollectionJob = mJavaAdapter.get().alwaysCollectFlow( diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt index fc32f4c138d7..9527f327a837 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt @@ -26,9 +26,8 @@ import com.android.systemui.classifier.FalsingClassifier import com.android.systemui.classifier.domain.interactor.FalsingInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.flag.SceneContainerFlags import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.android.systemui.util.kotlin.pairwise @@ -51,7 +50,7 @@ constructor( private val repository: BouncerRepository, private val authenticationInteractor: AuthenticationInteractor, private val sceneInteractor: SceneInteractor, - featureFlags: FeatureFlags, + flags: SceneContainerFlags, private val falsingInteractor: FalsingInteractor, ) { @@ -94,7 +93,7 @@ constructor( val isPatternVisible: StateFlow<Boolean> = authenticationInteractor.isPatternVisible init { - if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { + if (flags.isEnabled()) { // Clear the message if moved from throttling to no-longer throttling. applicationScope.launch { isThrottled.pairwise().collect { (wasThrottled, currentlyThrottled) -> diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt index 5b1998d1e5f6..f6794d4f40b3 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt @@ -23,8 +23,7 @@ import com.android.systemui.authentication.domain.model.AuthenticationMethodMode import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags +import com.android.systemui.scene.shared.flag.SceneContainerFlags import javax.inject.Inject import kotlin.math.ceil import kotlinx.coroutines.CoroutineScope @@ -47,7 +46,7 @@ constructor( @Application private val applicationScope: CoroutineScope, private val bouncerInteractor: BouncerInteractor, private val authenticationInteractor: AuthenticationInteractor, - featureFlags: FeatureFlags, + flags: SceneContainerFlags, ) { private val isInputEnabled: StateFlow<Boolean> = bouncerInteractor.isThrottled @@ -102,7 +101,7 @@ constructor( ) init { - if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { + if (flags.isEnabled()) { applicationScope.launch { bouncerInteractor.isThrottled .map { isThrottled -> diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java index 3195d093a711..0a1aed65c205 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java @@ -22,8 +22,7 @@ import android.view.ViewConfiguration; import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.flags.FeatureFlagsClassic; -import com.android.systemui.flags.Flags; +import com.android.systemui.scene.shared.flag.SceneContainerFlags; import com.android.systemui.statusbar.phone.NotificationTapHelper; import dagger.Binds; @@ -53,8 +52,8 @@ public interface FalsingModule { static FalsingCollector providesFalsingCollectorLegacy( FalsingCollectorImpl impl, FalsingCollectorNoOp noOp, - FeatureFlagsClassic featureFlags) { - return featureFlags.isEnabled(Flags.SCENE_CONTAINER) ? noOp : impl; + SceneContainerFlags flags) { + return flags.isEnabled() ? noOp : impl; } /** Provides the actual {@link FalsingCollector}. */ diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt index 970b475fe702..3ff1f09cc0f1 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt @@ -31,7 +31,7 @@ constructor( defaultCommunalWidgetSection: DefaultCommunalWidgetSection, ) : KeyguardBlueprint { override val id: String = COMMUNAL - override val sections: Array<KeyguardSection> = arrayOf(defaultCommunalWidgetSection) + override val sections: Set<KeyguardSection> = setOf(defaultCommunalWidgetSection) companion object { const val COMMUNAL = "communal" diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt index 4fb9384630a5..8640c97bb1e1 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt @@ -42,9 +42,12 @@ constructor( private val communalWidgetViewModel: CommunalWidgetViewModel, private val communalWidgetViewAdapter: CommunalWidgetViewAdapter, private val keyguardBlueprintInteractor: Lazy<KeyguardBlueprintInteractor>, -) : KeyguardSection { +) : KeyguardSection() { private val widgetAreaViewId = R.id.communal_widget_wrapper - override fun addViews(constraintLayout: ConstraintLayout) { + + override fun addViews(constraintLayout: ConstraintLayout) {} + + override fun bindData(constraintLayout: ConstraintLayout) { if (!featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)) { return } @@ -65,4 +68,6 @@ constructor( connect(widgetAreaViewId, END, PARENT_ID, END) } } + + override fun removeViews(constraintLayout: ConstraintLayout) {} } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index ea2568ddba8d..b0d73c9b7cf7 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -115,7 +115,7 @@ object Flags { val BUILDER_EXTRAS_OVERRIDE = sysPropBooleanFlag( "persist.sysui.notification.builder_extras_override", - default = true + default = false ) /** Only notify group expansion listeners when a change happens. */ @@ -643,7 +643,22 @@ object Flags { */ // TODO(b/283300105): Tracking Bug @JvmField val SCENE_CONTAINER_ENABLED = false - @JvmField val SCENE_CONTAINER = unreleasedFlag("scene_container") + @Deprecated( + message = """ + Do not use this flag directly. Please use + [com.android.systemui.scene.shared.flag.SceneContainerFlags#isEnabled]. + + (Not really deprecated but using this as a simple way to bring attention to the above). + """, + replaceWith = ReplaceWith( + "com.android.systemui.scene.shared.flag.SceneContainerFlags#isEnabled", + ), + level = DeprecationLevel.WARNING, + ) + @JvmField val SCENE_CONTAINER = resourceBooleanFlag( + R.bool.config_sceneContainerFrameworkEnabled, + "scene_container", + ) // 1900 @JvmField val NOTE_TASKS = releasedFlag("keycode_flag") diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt index e374549b9101..572785721b4f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt @@ -45,6 +45,7 @@ import com.android.systemui.keyguard.shared.model.ScreenModel import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.WakefulnessModel import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.flag.SceneContainerFlags import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.statusbar.CommandQueue @@ -77,6 +78,7 @@ constructor( private val repository: KeyguardRepository, private val commandQueue: CommandQueue, featureFlags: FeatureFlags, + sceneContainerFlags: SceneContainerFlags, bouncerRepository: KeyguardBouncerRepository, configurationRepository: ConfigurationRepository, shadeRepository: ShadeRepository, @@ -249,7 +251,7 @@ constructor( /** Whether to animate the next doze mode transition. */ val animateDozingTransitions: Flow<Boolean> by lazy { - if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { + if (sceneContainerFlags.isEnabled()) { sceneInteractorProvider .get() .transitioningTo diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt index 659c5f3007e8..35a9aaecc8cd 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt @@ -22,17 +22,34 @@ import androidx.constraintlayout.widget.ConstraintSet /** Determines the constraints for the ConstraintSet in the lockscreen root view. */ interface KeyguardBlueprint { val id: String - val sections: Array<KeyguardSection> + val sections: Set<KeyguardSection> - fun addViews(constraintLayout: ConstraintLayout) { - sections.forEach { it.addViews(constraintLayout) } + /** + * Add views to new blueprint. + * + * Finds sections that did not exist in the previous blueprint and add the corresponding views. + * + * @param previousBluePrint: KeyguardBlueprint the blueprint we are transitioning from. + */ + fun addViews(previousBlueprint: KeyguardBlueprint?, constraintLayout: ConstraintLayout) { + sections.subtract((previousBlueprint?.sections ?: setOf()).toSet()).forEach { + it.addViews(constraintLayout) + it.bindData(constraintLayout) + } } - fun applyConstraints(constraintSet: ConstraintSet) { - sections.forEach { it.applyConstraints(constraintSet) } + /** + * Remove views of old blueprint. + * + * Finds sections that are no longer in the next blueprint and remove the corresponding views. + * + * @param nextBluePrint: KeyguardBlueprint the blueprint we will transition to. + */ + fun removeViews(nextBlueprint: KeyguardBlueprint, constraintLayout: ConstraintLayout) { + sections.subtract(nextBlueprint.sections).forEach { it.removeViews(constraintLayout) } } - fun onDestroy() { - sections.forEach { it.onDestroy() } + fun applyConstraints(constraintSet: ConstraintSet) { + sections.forEach { it.applyConstraints(constraintSet) } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt index 19f50dec3032..48a214615733 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt @@ -23,8 +23,34 @@ import androidx.constraintlayout.widget.ConstraintSet * Lower level modules that determine constraints for a particular section in the lockscreen root * view. */ -interface KeyguardSection { - fun addViews(constraintLayout: ConstraintLayout) - fun applyConstraints(constraintSet: ConstraintSet) - fun onDestroy() {} +abstract class KeyguardSection { + /** Adds the views to the root view. */ + abstract fun addViews(constraintLayout: ConstraintLayout) + /** Binds the views to data. */ + abstract fun bindData(constraintLayout: ConstraintLayout) + /** Applies layout constraints to the view in respect to the root view. */ + abstract fun applyConstraints(constraintSet: ConstraintSet) + /** Removes views and does any data binding destruction. */ + abstract fun removeViews(constraintLayout: ConstraintLayout) + + /** + * Defines equality as same class. + * + * This is to enable set operations to be done as an optimization to blueprint transitions. + */ + override fun equals(other: Any?): Boolean { + other?.let { other -> + return this::class == other::class + } + return false + } + + /** + * Defines hashcode as class. + * + * This is to enable set operations to be done as an optimization to blueprint transitions. + */ + override fun hashCode(): Int { + return this::class.hashCode() + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt index c340e5df498b..78b72a9e5e6a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt @@ -21,7 +21,6 @@ import android.os.Trace import android.util.Log import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet -import androidx.core.view.children import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel @@ -37,26 +36,20 @@ class KeyguardBlueprintViewBinder { repeatOnLifecycle(Lifecycle.State.CREATED) { launch { viewModel.blueprint.collect { blueprint -> + val prevBluePrint = viewModel.currentBluePrint Trace.beginSection("KeyguardBlueprint#applyBlueprint") Log.d(TAG, "applying blueprint: $blueprint") - if (blueprint != viewModel.currentBluePrint) { - viewModel.currentBluePrint?.onDestroy() + // Add and remove views of sections that are not contained by the other. + prevBluePrint?.removeViews(blueprint, constraintLayout) + blueprint.addViews(prevBluePrint, constraintLayout) + + ConstraintSet().apply { + clone(constraintLayout) + val emptyLayout = ConstraintSet.Layout() + knownIds.forEach { getConstraint(it).layout.copyFrom(emptyLayout) } + blueprint.applyConstraints(this) + applyTo(constraintLayout) } - val constraintSet = - ConstraintSet().apply { - clone(constraintLayout) - val emptyLayout = ConstraintSet.Layout() - knownIds.forEach { - getConstraint(it).layout.copyFrom(emptyLayout) - } - blueprint.addViews(constraintLayout) - blueprint.applyConstraints(this) - applyTo(constraintLayout) - } - // Remove all unconstrained views. - constraintLayout.children - .filterNot { constraintSet.knownIds.contains(it.id) } - .forEach { constraintLayout.removeView(it) } viewModel.currentBluePrint = blueprint Trace.endSection() diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt index 5a15fc236a0a..85b2b82cd44a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt @@ -55,7 +55,7 @@ constructor( override val id: String = DEFAULT override val sections = - arrayOf( + setOf( defaultIndicationAreaSection, defaultLockIconSection, defaultShortcutsSection, @@ -66,9 +66,12 @@ constructor( splitShadeGuidelines, ) - override fun addViews(constraintLayout: ConstraintLayout) { + override fun addViews( + previousBlueprint: KeyguardBlueprint?, + constraintLayout: ConstraintLayout + ) { if (featureFlags.isEnabled(Flags.LAZY_INFLATE_KEYGUARD)) { - super.addViews(constraintLayout) + super.addViews(previousBlueprint, constraintLayout) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt index 5ef625e62d00..bb3af6cc86a2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt @@ -46,7 +46,7 @@ constructor( override val id: String = SHORTCUTS_BESIDE_UDFPS override val sections = - arrayOf( + setOf( defaultIndicationAreaSection, defaultLockIconSection, defaultAmbientIndicationAreaSection, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt index 587c6b7cb4ae..79b715799d31 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt @@ -18,6 +18,8 @@ package com.android.systemui.keyguard.ui.view.layout.sections import android.content.res.Resources +import android.view.View +import android.widget.ImageView import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.constraintlayout.widget.ConstraintSet.BOTTOM @@ -25,7 +27,9 @@ import androidx.constraintlayout.widget.ConstraintSet.LEFT import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.RIGHT import androidx.constraintlayout.widget.ConstraintSet.TOP +import androidx.core.content.res.ResourcesCompat import com.android.systemui.R +import com.android.systemui.animation.view.LaunchableImageView import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags @@ -49,12 +53,19 @@ constructor( private val falsingManager: FalsingManager, private val indicationController: KeyguardIndicationController, private val vibratorHelper: VibratorHelper, -) : BaseShortcutsSection(), KeyguardSection { +) : KeyguardSection() { + private var leftShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null + private var rightShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null override fun addViews(constraintLayout: ConstraintLayout) { if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { addLeftShortcut(constraintLayout) addRightShortcut(constraintLayout) + } + } + + override fun bindData(constraintLayout: ConstraintLayout) { + if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { leftShortcutHandle = KeyguardQuickAffordanceViewBinder.bind( constraintLayout.requireViewById(R.id.start_button), @@ -98,4 +109,67 @@ constructor( connect(R.id.end_button, BOTTOM, R.id.lock_icon_view, BOTTOM) } } + + override fun removeViews(constraintLayout: ConstraintLayout) { + leftShortcutHandle?.destroy() + rightShortcutHandle?.destroy() + constraintLayout.removeView(R.id.start_button) + constraintLayout.removeView(R.id.end_button) + } + + private fun addLeftShortcut(constraintLayout: ConstraintLayout) { + val padding = + constraintLayout.resources.getDimensionPixelSize( + R.dimen.keyguard_affordance_fixed_padding + ) + val view = + LaunchableImageView(constraintLayout.context, null).apply { + id = R.id.start_button + scaleType = ImageView.ScaleType.FIT_CENTER + background = + ResourcesCompat.getDrawable( + context.resources, + R.drawable.keyguard_bottom_affordance_bg, + context.theme + ) + foreground = + ResourcesCompat.getDrawable( + context.resources, + R.drawable.keyguard_bottom_affordance_selected_border, + context.theme + ) + visibility = View.INVISIBLE + setPadding(padding, padding, padding, padding) + } + constraintLayout.addView(view) + } + + private fun addRightShortcut(constraintLayout: ConstraintLayout) { + if (constraintLayout.findViewById<View>(R.id.end_button) != null) return + + val padding = + constraintLayout.resources.getDimensionPixelSize( + R.dimen.keyguard_affordance_fixed_padding + ) + val view = + LaunchableImageView(constraintLayout.context, null).apply { + id = R.id.end_button + scaleType = ImageView.ScaleType.FIT_CENTER + background = + ResourcesCompat.getDrawable( + context.resources, + R.drawable.keyguard_bottom_affordance_bg, + context.theme + ) + foreground = + ResourcesCompat.getDrawable( + context.resources, + R.drawable.keyguard_bottom_affordance_selected_border, + context.theme + ) + visibility = View.INVISIBLE + setPadding(padding, padding, padding, padding) + } + constraintLayout.addView(view) + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/BaseShortcutsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/BaseShortcutsSection.kt deleted file mode 100644 index db0cf5ad3000..000000000000 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/BaseShortcutsSection.kt +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2023 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. - * - */ - -package com.android.systemui.keyguard.ui.view.layout.sections - -import android.view.View -import android.widget.ImageView -import androidx.constraintlayout.widget.ConstraintLayout -import androidx.constraintlayout.widget.ConstraintSet -import androidx.core.content.res.ResourcesCompat -import com.android.systemui.R -import com.android.systemui.animation.view.LaunchableImageView -import com.android.systemui.keyguard.shared.model.KeyguardSection -import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder - -/** Base class for sections that add lockscreen shortcuts. */ -abstract class BaseShortcutsSection : KeyguardSection { - protected open var leftShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null - protected open var rightShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null - - override fun addViews(constraintLayout: ConstraintLayout) {} - - override fun applyConstraints(constraintSet: ConstraintSet) {} - - override fun onDestroy() { - leftShortcutHandle?.destroy() - rightShortcutHandle?.destroy() - } - - protected open fun addLeftShortcut(constraintLayout: ConstraintLayout) { - if (constraintLayout.findViewById<View>(R.id.start_button) != null) return - - val padding = - constraintLayout.resources.getDimensionPixelSize( - R.dimen.keyguard_affordance_fixed_padding - ) - val view = - LaunchableImageView(constraintLayout.context, null).apply { - id = R.id.start_button - scaleType = ImageView.ScaleType.FIT_CENTER - background = - ResourcesCompat.getDrawable( - context.resources, - R.drawable.keyguard_bottom_affordance_bg, - context.theme - ) - foreground = - ResourcesCompat.getDrawable( - context.resources, - R.drawable.keyguard_bottom_affordance_selected_border, - context.theme - ) - visibility = View.INVISIBLE - setPadding(padding, padding, padding, padding) - } - constraintLayout.addView(view) - } - - protected open fun addRightShortcut(constraintLayout: ConstraintLayout) { - if (constraintLayout.findViewById<View>(R.id.end_button) != null) return - - val padding = - constraintLayout.resources.getDimensionPixelSize( - R.dimen.keyguard_affordance_fixed_padding - ) - val view = - LaunchableImageView(constraintLayout.context, null).apply { - id = R.id.end_button - scaleType = ImageView.ScaleType.FIT_CENTER - background = - ResourcesCompat.getDrawable( - context.resources, - R.drawable.keyguard_bottom_affordance_bg, - context.theme - ) - foreground = - ResourcesCompat.getDrawable( - context.resources, - R.drawable.keyguard_bottom_affordance_selected_border, - context.theme - ) - visibility = View.INVISIBLE - setPadding(padding, padding, padding, padding) - } - constraintLayout.addView(view) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt index f8455c53a51f..ce86e9784180 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.view.layout.sections import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup.LayoutParams.MATCH_PARENT import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet @@ -46,19 +45,21 @@ constructor( private val featureFlags: FeatureFlags, private val keyguardAmbientIndicationViewModel: KeyguardAmbientIndicationViewModel, private val keyguardRootViewModel: KeyguardRootViewModel, -) : KeyguardSection { +) : KeyguardSection() { private var ambientIndicationAreaHandle: KeyguardAmbientIndicationAreaViewBinder.Binding? = null override fun addViews(constraintLayout: ConstraintLayout) { if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { - if (constraintLayout.findViewById<View>(R.id.ambient_indication_container) == null) { - val view = - LayoutInflater.from(constraintLayout.context) - .inflate(R.layout.ambient_indication, constraintLayout, false) + val view = + LayoutInflater.from(constraintLayout.context) + .inflate(R.layout.ambient_indication, constraintLayout, false) - constraintLayout.addView(view) - } + constraintLayout.addView(view) + } + } + override fun bindData(constraintLayout: ConstraintLayout) { + if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { ambientIndicationAreaHandle = KeyguardAmbientIndicationAreaViewBinder.bind( constraintLayout, @@ -94,7 +95,9 @@ constructor( } } - override fun onDestroy() { + override fun removeViews(constraintLayout: ConstraintLayout) { ambientIndicationAreaHandle?.destroy() + + constraintLayout.removeView(R.id.ambient_indication_container) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt index f04bfc675f1f..a45223cbd449 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.view.layout.sections import android.content.Context -import android.view.View import android.view.ViewGroup import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet @@ -42,17 +41,19 @@ constructor( private val keyguardRootViewModel: KeyguardRootViewModel, private val indicationController: KeyguardIndicationController, private val featureFlags: FeatureFlags, -) : KeyguardSection { +) : KeyguardSection() { private val indicationAreaViewId = R.id.keyguard_indication_area private var indicationAreaHandle: DisposableHandle? = null override fun addViews(constraintLayout: ConstraintLayout) { if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { - if (constraintLayout.findViewById<View>(indicationAreaViewId) == null) { - val view = KeyguardIndicationArea(context, null) - constraintLayout.addView(view) - } + val view = KeyguardIndicationArea(context, null) + constraintLayout.addView(view) + } + } + override fun bindData(constraintLayout: ConstraintLayout) { + if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { indicationAreaHandle = KeyguardIndicationAreaBinder.bind( constraintLayout, @@ -90,7 +91,8 @@ constructor( } } - override fun onDestroy() { + override fun removeViews(constraintLayout: ConstraintLayout) { indicationAreaHandle?.dispose() + constraintLayout.removeView(indicationAreaViewId) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt index 3d62f3f1f985..3e91d9336b13 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt @@ -47,19 +47,23 @@ constructor( private val notificationPanelView: NotificationPanelView, private val featureFlags: FeatureFlags, private val lockIconViewController: LockIconViewController, -) : KeyguardSection { +) : KeyguardSection() { private val lockIconViewId = R.id.lock_icon_view override fun addViews(constraintLayout: ConstraintLayout) { - if (featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) { - notificationPanelView.findViewById<View>(R.id.lock_icon_view).let { - notificationPanelView.removeView(it) - } - if (constraintLayout.findViewById<View>(R.id.lock_icon_view) == null) { - val view = LockIconView(context, null).apply { id = R.id.lock_icon_view } - constraintLayout.addView(view) - lockIconViewController.setLockIconView(view) - } + if (!featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) { + return + } + notificationPanelView.findViewById<View>(R.id.lock_icon_view).let { + notificationPanelView.removeView(it) + } + val view = LockIconView(context, null).apply { id = R.id.lock_icon_view } + constraintLayout.addView(view) + } + + override fun bindData(constraintLayout: ConstraintLayout) { + constraintLayout.findViewById<LockIconView?>(R.id.lock_icon_view)?.let { + lockIconViewController.setLockIconView(it) } } @@ -92,6 +96,10 @@ constructor( } } + override fun removeViews(constraintLayout: ConstraintLayout) { + constraintLayout.removeView(R.id.lock_icon_view) + } + @VisibleForTesting internal fun centerLockIcon(center: Point, radius: Float, constraintSet: ConstraintSet) { val sensorRect = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt index a203e41db86e..59c5d78bfce8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt @@ -40,22 +40,30 @@ constructor( private val sharedNotificationContainer: SharedNotificationContainer, private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel, private val controller: NotificationStackScrollLayoutController, -) : KeyguardSection { +) : KeyguardSection() { override fun addViews(constraintLayout: ConstraintLayout) { + if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) { + return + } + // This moves the existing NSSL view to a different parent, as the controller is a + // singleton and recreating it has other bad side effects + notificationPanelView.findViewById<View?>(R.id.notification_stack_scroller)?.let { + (it.parent as ViewGroup).removeView(it) + sharedNotificationContainer.addNotificationStackScrollLayout(it) + } + } + + override fun bindData(constraintLayout: ConstraintLayout) { if (featureFlags.isEnabled(Flags.MIGRATE_NSSL)) { - // This moves the existing NSSL view to a different parent, as the controller is a - // singleton and recreating it has other bad side effects - notificationPanelView.findViewById<View?>(R.id.notification_stack_scroller)?.let { - (it.parent as ViewGroup).removeView(it) - sharedNotificationContainer.addNotificationStackScrollLayout(it) - SharedNotificationContainerBinder.bind( - sharedNotificationContainer, - sharedNotificationContainerViewModel, - controller, - ) - } + SharedNotificationContainerBinder.bind( + sharedNotificationContainer, + sharedNotificationContainerViewModel, + controller, + ) } } override fun applyConstraints(constraintSet: ConstraintSet) {} + + override fun removeViews(constraintLayout: ConstraintLayout) {} } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt index 660cc96f21e6..b25f9afc0d1e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt @@ -49,23 +49,26 @@ constructor( private val keyguardSettingsMenuViewModel: KeyguardSettingsMenuViewModel, private val vibratorHelper: VibratorHelper, private val activityStarter: ActivityStarter, -) : KeyguardSection { +) : KeyguardSection() { private var settingsPopupMenuHandle: DisposableHandle? = null override fun addViews(constraintLayout: ConstraintLayout) { - if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { - if (constraintLayout.findViewById<View?>(R.id.keyguard_settings_button) == null) { - val view = - LayoutInflater.from(constraintLayout.context) - .inflate(R.layout.keyguard_settings_popup_menu, constraintLayout, false) - .apply { - id = R.id.keyguard_settings_button - isVisible = false - alpha = 0f - } as LaunchableLinearLayout - constraintLayout.addView(view) - } + if (!featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { + return + } + val view = + LayoutInflater.from(constraintLayout.context) + .inflate(R.layout.keyguard_settings_popup_menu, constraintLayout, false) + .apply { + id = R.id.keyguard_settings_button + isVisible = false + alpha = 0f + } as LaunchableLinearLayout + constraintLayout.addView(view) + } + override fun bindData(constraintLayout: ConstraintLayout) { + if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { settingsPopupMenuHandle = KeyguardSettingsViewBinder.bind( constraintLayout.requireViewById<View>(R.id.keyguard_settings_button), @@ -100,7 +103,8 @@ constructor( } } - override fun onDestroy() { + override fun removeViews(constraintLayout: ConstraintLayout) { settingsPopupMenuHandle?.dispose() + constraintLayout.removeView(R.id.keyguard_settings_button) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt index 965910a1be66..c4980557c454 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt @@ -18,13 +18,17 @@ package com.android.systemui.keyguard.ui.view.layout.sections import android.content.res.Resources +import android.view.View +import android.widget.ImageView import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.constraintlayout.widget.ConstraintSet.BOTTOM import androidx.constraintlayout.widget.ConstraintSet.LEFT import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.RIGHT +import androidx.core.content.res.ResourcesCompat import com.android.systemui.R +import com.android.systemui.animation.view.LaunchableImageView import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags @@ -48,12 +52,19 @@ constructor( private val falsingManager: FalsingManager, private val indicationController: KeyguardIndicationController, private val vibratorHelper: VibratorHelper, -) : BaseShortcutsSection(), KeyguardSection { +) : KeyguardSection() { + private var leftShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null + private var rightShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null override fun addViews(constraintLayout: ConstraintLayout) { if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { addLeftShortcut(constraintLayout) addRightShortcut(constraintLayout) + } + } + + override fun bindData(constraintLayout: ConstraintLayout) { + if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { leftShortcutHandle = KeyguardQuickAffordanceViewBinder.bind( constraintLayout.requireViewById(R.id.start_button), @@ -98,6 +109,69 @@ constructor( } } + override fun removeViews(constraintLayout: ConstraintLayout) { + leftShortcutHandle?.destroy() + rightShortcutHandle?.destroy() + constraintLayout.removeView(R.id.start_button) + constraintLayout.removeView(R.id.end_button) + } + + private fun addLeftShortcut(constraintLayout: ConstraintLayout) { + val padding = + constraintLayout.resources.getDimensionPixelSize( + R.dimen.keyguard_affordance_fixed_padding + ) + val view = + LaunchableImageView(constraintLayout.context, null).apply { + id = R.id.start_button + scaleType = ImageView.ScaleType.FIT_CENTER + background = + ResourcesCompat.getDrawable( + context.resources, + R.drawable.keyguard_bottom_affordance_bg, + context.theme + ) + foreground = + ResourcesCompat.getDrawable( + context.resources, + R.drawable.keyguard_bottom_affordance_selected_border, + context.theme + ) + visibility = View.INVISIBLE + setPadding(padding, padding, padding, padding) + } + constraintLayout.addView(view) + } + + private fun addRightShortcut(constraintLayout: ConstraintLayout) { + if (constraintLayout.findViewById<View>(R.id.end_button) != null) return + + val padding = + constraintLayout.resources.getDimensionPixelSize( + R.dimen.keyguard_affordance_fixed_padding + ) + val view = + LaunchableImageView(constraintLayout.context, null).apply { + id = R.id.end_button + scaleType = ImageView.ScaleType.FIT_CENTER + background = + ResourcesCompat.getDrawable( + context.resources, + R.drawable.keyguard_bottom_affordance_bg, + context.theme + ) + foreground = + ResourcesCompat.getDrawable( + context.resources, + R.drawable.keyguard_bottom_affordance_selected_border, + context.theme + ) + visibility = View.INVISIBLE + setPadding(padding, padding, padding, padding) + } + constraintLayout.addView(view) + } + /** Method to add shortcuts without applying any data binding. */ fun addShortcutViews(constraintLayout: ConstraintLayout) { addLeftShortcut(constraintLayout) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt index 321d7a70b087..b144f7ae6906 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt @@ -55,31 +55,34 @@ constructor( private val keyguardViewConfigurator: Lazy<KeyguardViewConfigurator>, private val notificationPanelViewController: Lazy<NotificationPanelViewController>, private val keyguardMediaController: KeyguardMediaController, -) : KeyguardSection { +) : KeyguardSection() { private val statusViewId = R.id.keyguard_status_view - @OptIn(ExperimentalCoroutinesApi::class) override fun addViews(constraintLayout: ConstraintLayout) { + if (!featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) { + return + } // At startup, 2 views with the ID `R.id.keyguard_status_view` will be available. // Disable one of them - if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) { - notificationPanelView.findViewById<View>(statusViewId)?.let { - notificationPanelView.removeView(it) - } - if (constraintLayout.findViewById<View>(statusViewId) == null) { - val keyguardStatusView = - (LayoutInflater.from(context) - .inflate(R.layout.keyguard_status_view, constraintLayout, false) - as KeyguardStatusView) - .apply { clipChildren = false } + notificationPanelView.findViewById<View>(statusViewId)?.let { + notificationPanelView.removeView(it) + } + val keyguardStatusView = + (LayoutInflater.from(context) + .inflate(R.layout.keyguard_status_view, constraintLayout, false) + as KeyguardStatusView) + .apply { clipChildren = false } + constraintLayout.addView(keyguardStatusView) + } - val statusViewComponent = - keyguardStatusViewComponentFactory.build(keyguardStatusView) + override fun bindData(constraintLayout: ConstraintLayout) { + if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) { + constraintLayout.findViewById<KeyguardStatusView?>(R.id.keyguard_status_view)?.let { + val statusViewComponent = keyguardStatusViewComponentFactory.build(it) val controller = statusViewComponent.keyguardStatusViewController controller.init() - constraintLayout.addView(keyguardStatusView) keyguardMediaController.attachSplitShadeContainer( - keyguardStatusView.requireViewById<ViewGroup>(R.id.status_view_media_container) + it.requireViewById<ViewGroup>(R.id.status_view_media_container) ) keyguardViewConfigurator.get().keyguardStatusViewController = controller notificationPanelViewController.get().updateStatusBarViewController() @@ -107,7 +110,8 @@ constructor( } @OptIn(ExperimentalCoroutinesApi::class) - override fun onDestroy() { + override fun removeViews(constraintLayout: ConstraintLayout) { + constraintLayout.removeView(statusViewId) keyguardViewConfigurator.get().keyguardStatusViewController = null } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/Extensions.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/Extensions.kt new file mode 100644 index 000000000000..94332d21f499 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/Extensions.kt @@ -0,0 +1,8 @@ +package com.android.systemui.keyguard.ui.view.layout.sections + +import android.view.View +import androidx.constraintlayout.widget.ConstraintLayout + +internal fun ConstraintLayout.removeView(viewId: Int) { + findViewById<View?>(viewId)?.let { removeView(it) } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt index bd629d512614..5e3ea059ddd5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt @@ -17,7 +17,6 @@ package com.android.systemui.keyguard.ui.view.layout.sections -import android.content.Context import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.constraintlayout.widget.ConstraintSet.VERTICAL @@ -25,9 +24,11 @@ import com.android.systemui.R import com.android.systemui.keyguard.shared.model.KeyguardSection import javax.inject.Inject -class SplitShadeGuidelines @Inject constructor(private val context: Context) : KeyguardSection { +class SplitShadeGuidelines @Inject constructor() : KeyguardSection() { override fun addViews(constraintLayout: ConstraintLayout) {} + override fun bindData(constraintLayout: ConstraintLayout) {} + override fun applyConstraints(constraintSet: ConstraintSet) { constraintSet.apply { // For use on large screens, it will provide a guideline vertically in the center to @@ -36,4 +37,6 @@ class SplitShadeGuidelines @Inject constructor(private val context: Context) : K setGuidelinePercent(R.id.split_shade_guideline, 0.5f) } } + + override fun removeViews(constraintLayout: ConstraintLayout) {} } diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 3a64a6a989f2..d23bedace06f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -85,7 +85,6 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.model.SysUiState; @@ -96,6 +95,7 @@ import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.navigationbar.buttons.KeyButtonView; import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; import com.android.systemui.scene.domain.interactor.SceneInteractor; +import com.android.systemui.scene.shared.flag.SceneContainerFlags; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.ShadeViewController; @@ -143,6 +143,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis private final Context mContext; private final FeatureFlags mFeatureFlags; + private final SceneContainerFlags mSceneContainerFlags; private final Executor mMainExecutor; private final ShellInterface mShellInterface; private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy; @@ -218,7 +219,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis // If scene framework is enabled, set the scene container window to // visible and let the touch "slip" into that window. - if (mFeatureFlags.isEnabled(Flags.SCENE_CONTAINER)) { + if (mSceneContainerFlags.isEnabled()) { mSceneInteractor.get().setVisible(true, "swipe down on launcher"); } else { centralSurfaces.onInputFocusTransfer( @@ -229,7 +230,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis if (action == ACTION_UP || action == ACTION_CANCEL) { mInputFocusTransferStarted = false; - if (!mFeatureFlags.isEnabled(Flags.SCENE_CONTAINER)) { + if (!mSceneContainerFlags.isEnabled()) { float velocity = (event.getY() - mInputFocusTransferStartY) / (event.getEventTime() - mInputFocusTransferStartMillis); centralSurfaces.onInputFocusTransfer(mInputFocusTransferStarted, @@ -582,6 +583,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis KeyguardUnlockAnimationController sysuiUnlockAnimationController, AssistUtils assistUtils, FeatureFlags featureFlags, + SceneContainerFlags sceneContainerFlags, DumpManager dumpManager, Optional<UnfoldTransitionProgressForwarder> unfoldTransitionProgressForwarder ) { @@ -592,6 +594,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis mContext = context; mFeatureFlags = featureFlags; + mSceneContainerFlags = sceneContainerFlags; mMainExecutor = mainExecutor; mShellInterface = shellInterface; mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy; diff --git a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt index c5fbf7b96fff..fcbe9a6675ab 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt @@ -16,6 +16,7 @@ package com.android.systemui.scene +import com.android.systemui.scene.shared.flag.SceneContainerFlagsModule import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.SceneKey import dagger.Module @@ -28,6 +29,7 @@ import dagger.Provides EmptySceneModule::class, GoneSceneModule::class, QuickSettingsSceneModule::class, + SceneContainerFlagsModule::class, ShadeSceneModule::class, ], ) diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt index 85ef21aa9542..b36ec3274083 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt @@ -19,6 +19,7 @@ package com.android.systemui.scene import com.android.systemui.CoreStartable import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor import com.android.systemui.scene.domain.startable.SceneContainerStartable +import com.android.systemui.scene.shared.flag.SceneContainerFlagsModule import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.SceneKey import dagger.Binds @@ -36,6 +37,7 @@ import dagger.multibindings.IntoMap GoneSceneModule::class, LockscreenSceneModule::class, QuickSettingsSceneModule::class, + SceneContainerFlagsModule::class, ShadeSceneModule::class, ], ) diff --git a/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt index 5fda9b1a323b..c10e51b68ba2 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt @@ -16,6 +16,7 @@ package com.android.systemui.scene +import com.android.systemui.scene.shared.flag.SceneContainerFlagsModule import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.SceneKey import dagger.Module @@ -29,6 +30,7 @@ import dagger.Provides EmptySceneModule::class, GoneSceneModule::class, LockscreenSceneModule::class, + SceneContainerFlagsModule::class, ], ) object ShadelessSceneContainerFrameworkModule { diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt index 7f77acc1789a..722d3661d0ae 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt @@ -26,13 +26,12 @@ import com.android.systemui.classifier.FalsingCollectorActual import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.DisplayId -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.model.SysUiState import com.android.systemui.model.updateFlags import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.flag.SceneContainerFlags import com.android.systemui.scene.shared.logger.SceneLogger import com.android.systemui.scene.shared.model.ObservableTransitionState import com.android.systemui.scene.shared.model.SceneKey @@ -66,7 +65,7 @@ constructor( private val sceneInteractor: SceneInteractor, private val authenticationInteractor: AuthenticationInteractor, private val keyguardInteractor: KeyguardInteractor, - private val featureFlags: FeatureFlags, + private val flags: SceneContainerFlags, private val sysUiState: SysUiState, @DisplayId private val displayId: Int, private val sceneLogger: SceneLogger, @@ -74,14 +73,17 @@ constructor( ) : CoreStartable { override fun start() { - if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { + if (flags.isEnabled()) { sceneLogger.logFrameworkEnabled(isEnabled = true) hydrateVisibility() automaticallySwitchScenes() hydrateSystemUiState() collectFalsingSignals() } else { - sceneLogger.logFrameworkEnabled(isEnabled = false) + sceneLogger.logFrameworkEnabled( + isEnabled = false, + reason = flags.requirementDescription(), + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt new file mode 100644 index 000000000000..83fb723eda82 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt @@ -0,0 +1,136 @@ +/* + * Copyright 2023 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. + */ + +package com.android.systemui.scene.shared.flag + +import androidx.annotation.VisibleForTesting +import com.android.systemui.compose.ComposeFacade +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.flags.FeatureFlagsClassic +import com.android.systemui.flags.Flag +import com.android.systemui.flags.Flags +import com.android.systemui.flags.ReleasedFlag +import com.android.systemui.flags.ResourceBooleanFlag +import com.android.systemui.flags.UnreleasedFlag +import dagger.Module +import dagger.Provides +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject + +/** + * Defines interface for classes that can check whether the scene container framework feature is + * enabled. + */ +interface SceneContainerFlags { + + /** Returns `true` if the Scene Container Framework is enabled; `false` otherwise. */ + fun isEnabled(): Boolean + + /** Returns a developer-readable string that describes the current requirement list. */ + fun requirementDescription(): String +} + +class SceneContainerFlagsImpl +@AssistedInject +constructor( + private val featureFlags: FeatureFlagsClassic, + @Assisted private val isComposeAvailable: Boolean, +) : SceneContainerFlags { + + companion object { + @VisibleForTesting + val flags: List<Flag<Boolean>> = + listOf( + Flags.SCENE_CONTAINER, + Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA, + Flags.MIGRATE_LOCK_ICON, + Flags.MIGRATE_NSSL, + Flags.MIGRATE_KEYGUARD_STATUS_VIEW, + ) + } + + /** The list of requirements, all must be met for the feature to be enabled. */ + private val requirements = + flags.map { FlagMustBeEnabled(it) } + + listOf(ComposeMustBeAvailable(), CompileTimeFlagMustBeEnabled()) + + override fun isEnabled(): Boolean { + return requirements.all { it.isMet() } + } + + override fun requirementDescription(): String { + return buildString { + requirements.forEach { requirement -> + append('\n') + append(if (requirement.isMet()) " [MET]" else "[NOT MET]") + append(" ${requirement.name}") + } + } + } + + private interface Requirement { + val name: String + + fun isMet(): Boolean + } + + private inner class ComposeMustBeAvailable : Requirement { + override val name = "Jetpack Compose must be available" + + override fun isMet(): Boolean { + return isComposeAvailable + } + } + + private inner class CompileTimeFlagMustBeEnabled : Requirement { + override val name = "Flags.SCENE_CONTAINER_ENABLED must be enabled in code" + + override fun isMet(): Boolean { + return Flags.SCENE_CONTAINER_ENABLED + } + } + + private inner class FlagMustBeEnabled<FlagType : Flag<*>>( + private val flag: FlagType, + ) : Requirement { + override val name = "Flag ${flag.name} must be enabled" + + override fun isMet(): Boolean { + return when (flag) { + is ResourceBooleanFlag -> featureFlags.isEnabled(flag) + is ReleasedFlag -> featureFlags.isEnabled(flag) + is UnreleasedFlag -> featureFlags.isEnabled(flag) + else -> error("Unsupported flag type ${flag.javaClass}") + } + } + } + + @AssistedFactory + interface Factory { + fun create(isComposeAvailable: Boolean): SceneContainerFlagsImpl + } +} + +@Module +object SceneContainerFlagsModule { + + @Provides + @SysUISingleton + fun impl(factory: SceneContainerFlagsImpl.Factory): SceneContainerFlags { + return factory.create(ComposeFacade.isComposeAvailable()) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt index 62136dcd8e1d..c2c2e04990c2 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt @@ -24,16 +24,21 @@ import javax.inject.Inject class SceneLogger @Inject constructor(@SceneFrameworkLog private val logBuffer: LogBuffer) { - fun logFrameworkEnabled(isEnabled: Boolean) { + fun logFrameworkEnabled(isEnabled: Boolean, reason: String? = null) { fun asWord(isEnabled: Boolean): String { return if (isEnabled) "enabled" else "disabled" } logBuffer.log( tag = TAG, - level = LogLevel.INFO, - messageInitializer = { bool1 = isEnabled }, - messagePrinter = { "Scene framework is ${asWord(bool1)}" } + level = if (isEnabled) LogLevel.INFO else LogLevel.WARNING, + messageInitializer = { + bool1 = isEnabled + str1 = reason + }, + messagePrinter = { + "Scene framework is ${asWord(bool1)}${if (str1 != null) " $str1" else ""}" + } ) } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt index ed719a651978..3f7512a25732 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt @@ -26,13 +26,13 @@ import com.android.systemui.R import com.android.systemui.battery.BatteryMeterView import com.android.systemui.battery.BatteryMeterViewController import com.android.systemui.biometrics.AuthRippleView -import com.android.systemui.compose.ComposeFacade import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.ui.view.KeyguardRootView import com.android.systemui.privacy.OngoingPrivacyChip +import com.android.systemui.scene.shared.flag.SceneContainerFlags import com.android.systemui.scene.shared.model.Scene import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.ui.view.SceneWindowRootView @@ -70,17 +70,13 @@ abstract class ShadeViewProviderModule { @SysUISingleton fun providesWindowRootView( layoutInflater: LayoutInflater, - featureFlags: FeatureFlags, + sceneContainerFlags: SceneContainerFlags, viewModelProvider: Provider<SceneContainerViewModel>, containerConfigProvider: Provider<SceneContainerConfig>, scenesProvider: Provider<Set<@JvmSuppressWildcards Scene>>, layoutInsetController: NotificationInsetsController, ): WindowRootView { - return if ( - Flags.SCENE_CONTAINER_ENABLED && - featureFlags.isEnabled(Flags.SCENE_CONTAINER) && - ComposeFacade.isComposeAvailable() - ) { + return if (sceneContainerFlags.isEnabled()) { val sceneWindowRootView = layoutInflater.inflate(R.layout.scene_window_root, null) as SceneWindowRootView sceneWindowRootView.init( @@ -104,9 +100,9 @@ abstract class ShadeViewProviderModule { @SysUISingleton fun providesNotificationShadeWindowView( root: WindowRootView, - featureFlags: FeatureFlags, + sceneContainerFlags: SceneContainerFlags, ): NotificationShadeWindowView { - if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { + if (sceneContainerFlags.isEnabled()) { return root.requireViewById(R.id.legacy_window_root) } return root as NotificationShadeWindowView? diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt index cc41bf843565..23b0ee0ec9bc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt @@ -27,6 +27,7 @@ import com.android.systemui.Gefingerpoken import com.android.systemui.R import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.scene.shared.flag.SceneContainerFlags import com.android.systemui.scene.ui.view.WindowRootView import com.android.systemui.shade.ShadeController import com.android.systemui.shade.ShadeLogger @@ -48,8 +49,9 @@ import javax.inject.Provider private const val TAG = "PhoneStatusBarViewController" -/** Controller for [PhoneStatusBarView]. */ -class PhoneStatusBarViewController private constructor( +/** Controller for [PhoneStatusBarView]. */ +class PhoneStatusBarViewController +private constructor( view: PhoneStatusBarView, @Named(UNFOLD_STATUS_BAR) private val progressProvider: ScopedUnfoldTransitionProgressProvider?, private val centralSurfaces: CentralSurfaces, @@ -61,42 +63,42 @@ class PhoneStatusBarViewController private constructor( private val moveFromCenterAnimationController: StatusBarMoveFromCenterAnimationController?, private val userChipViewModel: StatusBarUserChipViewModel, private val viewUtil: ViewUtil, - private val featureFlags: FeatureFlags, + private val sceneContainerFlags: SceneContainerFlags, private val configurationController: ConfigurationController, private val statusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory, ) : ViewController<PhoneStatusBarView>(view) { private lateinit var statusContainer: View - private val configurationListener = object : ConfigurationController.ConfigurationListener { - override fun onConfigChanged(newConfig: Configuration?) { - mView.updateResources() + private val configurationListener = + object : ConfigurationController.ConfigurationListener { + override fun onConfigChanged(newConfig: Configuration?) { + mView.updateResources() + } } - } override fun onViewAttached() { statusContainer = mView.requireViewById(R.id.system_icons) statusContainer.setOnHoverListener( - statusOverlayHoverListenerFactory.createDarkAwareListener(statusContainer)) + statusOverlayHoverListenerFactory.createDarkAwareListener(statusContainer) + ) if (moveFromCenterAnimationController == null) return val statusBarLeftSide: View = - mView.requireViewById(R.id.status_bar_start_side_except_heads_up) + mView.requireViewById(R.id.status_bar_start_side_except_heads_up) val systemIconArea: ViewGroup = mView.requireViewById(R.id.status_bar_end_side_content) - val viewsToAnimate = arrayOf( - statusBarLeftSide, - systemIconArea - ) + val viewsToAnimate = arrayOf(statusBarLeftSide, systemIconArea) - mView.viewTreeObserver.addOnPreDrawListener(object : - ViewTreeObserver.OnPreDrawListener { - override fun onPreDraw(): Boolean { - moveFromCenterAnimationController.onViewsReady(viewsToAnimate) - mView.viewTreeObserver.removeOnPreDrawListener(this) - return true + mView.viewTreeObserver.addOnPreDrawListener( + object : ViewTreeObserver.OnPreDrawListener { + override fun onPreDraw(): Boolean { + moveFromCenterAnimationController.onViewsReady(viewsToAnimate) + mView.viewTreeObserver.removeOnPreDrawListener(this) + return true + } } - }) + ) mView.addOnLayoutChangeListener { _, left, _, right, _, oldLeft, _, oldRight, _ -> val widthChanged = right - left != oldRight - oldLeft @@ -121,8 +123,7 @@ class PhoneStatusBarViewController private constructor( mView.init(userChipViewModel) } - override fun onInit() { - } + override fun onInit() {} fun setImportantForAccessibility(mode: Int) { mView.importantForAccessibility = mode @@ -151,10 +152,11 @@ class PhoneStatusBarViewController private constructor( fun onTouch(event: MotionEvent) { if (statusBarWindowStateController.windowIsShowing()) { val upOrCancel = - event.action == MotionEvent.ACTION_UP || - event.action == MotionEvent.ACTION_CANCEL - centralSurfaces.setInteracting(WINDOW_STATUS_BAR, - !upOrCancel || shadeController.isExpandedVisible) + event.action == MotionEvent.ACTION_UP || event.action == MotionEvent.ACTION_CANCEL + centralSurfaces.setInteracting( + WINDOW_STATUS_BAR, + !upOrCancel || shadeController.isExpandedVisible + ) } } @@ -171,15 +173,20 @@ class PhoneStatusBarViewController private constructor( // panel view. if (!centralSurfaces.commandQueuePanelsEnabled) { if (event.action == MotionEvent.ACTION_DOWN) { - Log.v(TAG, String.format("onTouchForwardedFromStatusBar: panel disabled, " + - "ignoring touch at (${event.x.toInt()},${event.y.toInt()})")) + Log.v( + TAG, + String.format( + "onTouchForwardedFromStatusBar: panel disabled, " + + "ignoring touch at (${event.x.toInt()},${event.y.toInt()})" + ) + ) } return false } // If scene framework is enabled, route the touch to it and // ignore the rest of the gesture. - if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { + if (sceneContainerFlags.isEnabled()) { windowRootView.get().dispatchTouchEvent(event) return true } @@ -188,12 +195,13 @@ class PhoneStatusBarViewController private constructor( // If the view that would receive the touch is disabled, just have status // bar eat the gesture. if (!shadeViewController.isViewEnabled) { - shadeLogger.logMotionEvent(event, - "onTouchForwardedFromStatusBar: panel view disabled") + shadeLogger.logMotionEvent( + event, + "onTouchForwardedFromStatusBar: panel view disabled" + ) return true } - if (shadeViewController.isFullyCollapsed && - event.y < 1f) { + if (shadeViewController.isFullyCollapsed && event.y < 1f) { // b/235889526 Eat events on the top edge of the phone when collapsed shadeLogger.logMotionEvent(event, "top edge touch ignored") return true @@ -218,9 +226,7 @@ class PhoneStatusBarViewController private constructor( else -> super.getViewCenter(view, outPoint) } - /** - * Returns start or end (based on [isStart]) center point of the view - */ + /** Returns start or end (based on [isStart]) center point of the view */ private fun getViewEdgeCenter(view: View, outPoint: Point, isStart: Boolean) { val isRtl = view.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL val isLeftEdge = isRtl xor isStart @@ -236,11 +242,14 @@ class PhoneStatusBarViewController private constructor( } } - class Factory @Inject constructor( + class Factory + @Inject + constructor( private val unfoldComponent: Optional<SysUIUnfoldComponent>, @Named(UNFOLD_STATUS_BAR) private val progressProvider: Optional<ScopedUnfoldTransitionProgressProvider>, private val featureFlags: FeatureFlags, + private val sceneContainerFlags: SceneContainerFlags, private val userChipViewModel: StatusBarUserChipViewModel, private val centralSurfaces: CentralSurfaces, private val statusBarWindowStateController: StatusBarWindowStateController, @@ -252,9 +261,7 @@ class PhoneStatusBarViewController private constructor( private val configurationController: ConfigurationController, private val statusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory, ) { - fun create( - view: PhoneStatusBarView - ): PhoneStatusBarViewController { + fun create(view: PhoneStatusBarView): PhoneStatusBarViewController { val statusBarMoveFromCenterAnimationController = if (featureFlags.isEnabled(Flags.ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS)) { unfoldComponent.getOrNull()?.getStatusBarMoveFromCenterAnimationController() @@ -274,10 +281,10 @@ class PhoneStatusBarViewController private constructor( statusBarMoveFromCenterAnimationController, userChipViewModel, viewUtil, - featureFlags, + sceneContainerFlags, configurationController, statusOverlayHoverListenerFactory, ) } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java index fa9b9d2c571a..5773612d55c7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java @@ -34,9 +34,8 @@ import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.ScreenDecorations; import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.scene.domain.interactor.SceneInteractor; +import com.android.systemui.scene.shared.flag.SceneContainerFlags; import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -85,7 +84,7 @@ public final class StatusBarTouchableRegionManager implements Dumpable { ShadeExpansionStateManager shadeExpansionStateManager, Provider<SceneInteractor> sceneInteractor, Provider<JavaAdapter> javaAdapter, - FeatureFlags featureFlags, + SceneContainerFlags sceneContainerFlags, UnlockedScreenOffAnimationController unlockedScreenOffAnimationController ) { mContext = context; @@ -123,7 +122,7 @@ public final class StatusBarTouchableRegionManager implements Dumpable { mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; shadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged); - if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { + if (sceneContainerFlags.isEnabled()) { javaAdapter.get().alwaysCollectFlow( sceneInteractor.get().isVisible(), this::onShadeExpansionFullyChanged); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt index 6bff4ce4aad9..02b9bf06fc2a 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt @@ -191,7 +191,6 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { featureFlags = FakeFeatureFlags() featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true) - featureFlags.set(Flags.SCENE_CONTAINER, false) featureFlags.set(Flags.BOUNCER_USER_SWITCHER, false) featureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false) @@ -244,6 +243,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { falsingManager, userSwitcherController, featureFlags, + sceneTestUtils.sceneContainerFlags, globalSettings, sessionTracker, Optional.of(sideFpsController), @@ -802,8 +802,6 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { @Test fun dismissesKeyguard_whenSceneChangesToGone() = sceneTestUtils.testScope.runTest { - featureFlags.set(Flags.SCENE_CONTAINER, true) - // Upon init, we have never dismisses the keyguard. underTest.onInit() runCurrent() diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt index be9f6ca0fc33..09cb9291f6b2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt @@ -33,7 +33,7 @@ class DefaultCommunalBlueprintTest : SysuiTestCase() { @Test fun addView() { val constraintLayout = ConstraintLayout(context, null) - blueprint.addViews(constraintLayout) + blueprint.addViews(null, constraintLayout) verify(widgetSection).addViews(constraintLayout) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt index e2362f64b885..f62137c62c65 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt @@ -175,7 +175,6 @@ class CustomizationProviderTest : SysuiTestCase() { set(Flags.REVAMPED_WALLPAPER_UI, true) set(Flags.WALLPAPER_FULLSCREEN_PREVIEW, true) set(Flags.FACE_AUTH_REFACTOR, true) - set(Flags.SCENE_CONTAINER, false) } underTest.interactor = KeyguardQuickAffordanceInteractor( diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt index ce280d7b221b..14cdf2f58a7d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt @@ -29,7 +29,6 @@ import com.android.systemui.common.ui.data.repository.FakeConfigurationRepositor import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR -import com.android.systemui.flags.Flags.SCENE_CONTAINER import com.android.systemui.keyguard.data.repository.FakeCommandQueue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel @@ -70,11 +69,7 @@ class KeyguardInteractorTest : SysuiTestCase() { @Before fun setUp() { MockitoAnnotations.initMocks(this) - featureFlags = - FakeFeatureFlags().apply { - set(FACE_AUTH_REFACTOR, true) - set(SCENE_CONTAINER, true) - } + featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) } commandQueue = FakeCommandQueue() val sceneTestUtils = SceneTestUtils(this) testScope = sceneTestUtils.testScope @@ -90,6 +85,7 @@ class KeyguardInteractorTest : SysuiTestCase() { repository = repository, commandQueue = commandQueue, featureFlags = featureFlags, + sceneContainerFlags = sceneTestUtils.sceneContainerFlags, bouncerRepository = bouncerRepository, configurationRepository = configurationRepository, shadeRepository = shadeRepository, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt index 499a6362ce05..bdcb9abba745 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt @@ -104,7 +104,6 @@ class OccludingAppDeviceEntryInteractorTest : SysuiTestCase() { FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) set(Flags.DELAY_BOUNCER, false) - set(Flags.SCENE_CONTAINER, false) } trustRepository = FakeTrustRepository() powerRepository = FakePowerRepository() diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt index 3b4eab2a5fe1..bf57ecb41091 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt @@ -25,6 +25,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.shared.model.KeyguardBlueprint import com.android.systemui.keyguard.ui.view.KeyguardRootView import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection @@ -34,10 +35,12 @@ import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopu import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines +import com.android.systemui.util.mockito.whenever import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock +import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -81,7 +84,7 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() { @Test fun addViews() { val constraintLayout = ConstraintLayout(context, null) - underTest.addViews(constraintLayout) + underTest.addViews(null, constraintLayout) underTest.sections.forEach { verify(it, never()).addViews(constraintLayout) } } @@ -89,11 +92,35 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() { fun addViews_lazyInflateFlagOn() { featureFlags.set(Flags.LAZY_INFLATE_KEYGUARD, true) val constraintLayout = ConstraintLayout(context, null) - underTest.addViews(constraintLayout) + underTest.addViews(null, constraintLayout) underTest.sections.forEach { verify(it).addViews(constraintLayout) } } @Test + fun addViews_withPrevBlueprint() { + val prevBlueprint = mock(KeyguardBlueprint::class.java) + whenever(prevBlueprint.sections) + .thenReturn(underTest.sections.minus(defaultLockIconSection)) + featureFlags.set(Flags.LAZY_INFLATE_KEYGUARD, true) + val constraintLayout = ConstraintLayout(context, null) + underTest.addViews(prevBlueprint, constraintLayout) + underTest.sections.minus(defaultLockIconSection).forEach { + verify(it, never()).addViews(constraintLayout) + } + + verify(defaultLockIconSection).addViews(constraintLayout) + } + + @Test + fun addViews_withNextBlueprint() { + val nextBlueprint = mock(KeyguardBlueprint::class.java) + whenever(nextBlueprint.sections).thenReturn(setOf()) + val constraintLayout = ConstraintLayout(context, null) + underTest.removeViews(nextBlueprint, constraintLayout) + underTest.sections.forEach { verify(it).removeViews(constraintLayout) } + } + + @Test fun applyConstraints() { val cs = ConstraintSet() underTest.applyConstraints(cs) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt index 798b23e4a074..4e31af22f771 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt @@ -18,14 +18,17 @@ package com.android.systemui.keyguard.ui.view.layout.sections import android.view.ViewGroup +import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel import com.android.systemui.statusbar.KeyguardIndicationController +import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test @@ -58,7 +61,23 @@ class DefaultIndicationAreaSectionTest : SysuiTestCase() { } @Test - fun apply() { + fun addViewsConditionally() { + whenever(featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)).thenReturn(true) + val constraintLayout = ConstraintLayout(context, null) + underTest.addViews(constraintLayout) + assertThat(constraintLayout.childCount).isGreaterThan(0) + } + + @Test + fun addViewsConditionally_migrateFlagOff() { + whenever(featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)).thenReturn(false) + val constraintLayout = ConstraintLayout(context, null) + underTest.addViews(constraintLayout) + assertThat(constraintLayout.childCount).isEqualTo(0) + } + + @Test + fun applyConstraints() { val cs = ConstraintSet() underTest.applyConstraints(cs) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt index 1192a8017dc4..1c3b5e616ddf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.ui.view.layout.sections import android.graphics.Point import android.view.WindowManager +import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardUpdateMonitor @@ -27,7 +28,9 @@ import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.AuthController import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.shade.NotificationPanelView +import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test @@ -64,7 +67,23 @@ class DefaultLockIconSectionTest : SysuiTestCase() { } @Test - fun apply() { + fun addViewsConditionally() { + whenever(featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)).thenReturn(true) + val constraintLayout = ConstraintLayout(context, null) + underTest.addViews(constraintLayout) + assertThat(constraintLayout.childCount).isGreaterThan(0) + } + + @Test + fun addViewsConditionally_migrateFlagOff() { + whenever(featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)).thenReturn(false) + val constraintLayout = ConstraintLayout(context, null) + underTest.addViews(constraintLayout) + assertThat(constraintLayout.childCount).isEqualTo(0) + } + + @Test + fun applyConstraints() { val cs = ConstraintSet() underTest.applyConstraints(cs) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt index 7fecfc234392..b935e1d3c65a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt @@ -129,7 +129,6 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { set(Flags.FACE_AUTH_REFACTOR, true) set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, false) set(Flags.LOCK_SCREEN_LONG_PRESS_DIRECT_TO_WPP, false) - set(Flags.SCENE_CONTAINER, false) } val withDeps = KeyguardInteractorFactory.create(featureFlags = featureFlags) diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt index ef07fab70bb0..e353a53af752 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt @@ -36,6 +36,7 @@ import com.android.systemui.model.SysUiState import com.android.systemui.navigationbar.NavigationBarController import com.android.systemui.navigationbar.NavigationModeController import com.android.systemui.recents.OverviewProxyService.ACTION_QUICKSTEP +import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags import com.android.systemui.settings.FakeDisplayTracker import com.android.systemui.settings.UserTracker import com.android.systemui.shade.ShadeViewController @@ -146,6 +147,7 @@ class OverviewProxyServiceTest : SysuiTestCase() { sysuiUnlockAnimationController, assistUtils, featureFlags, + FakeSceneContainerFlags(), dumpManager, unfoldTransitionProgressForwarder ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt index 6006cd42424a..2f26a53afe7c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt @@ -25,8 +25,6 @@ import com.android.systemui.authentication.domain.model.AuthenticationMethodMode import com.android.systemui.authentication.domain.model.AuthenticationMethodModel import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel @@ -140,8 +138,6 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { @Before fun setUp() { - val featureFlags = FakeFeatureFlags().apply { set(Flags.SCENE_CONTAINER, true) } - authenticationRepository.setUnlocked(false) val displayTracker = FakeDisplayTracker(context) @@ -152,7 +148,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { sceneInteractor = sceneInteractor, authenticationInteractor = authenticationInteractor, keyguardInteractor = keyguardInteractor, - featureFlags = featureFlags, + flags = utils.sceneContainerFlags, sysUiState = sysUiState, displayId = displayTracker.defaultDisplayId, sceneLogger = mock(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt index 771c3e330e8a..145629a65f68 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt @@ -42,6 +42,7 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest +import org.junit.Assume.assumeTrue import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 @@ -57,7 +58,7 @@ class SceneContainerStartableTest : SysuiTestCase() { private val utils = SceneTestUtils(this) private val testScope = utils.testScope private val sceneInteractor = utils.sceneInteractor() - private val featureFlags = utils.featureFlags + private val sceneContainerFlags = utils.sceneContainerFlags private val authenticationRepository = utils.authenticationRepository() private val authenticationInteractor = utils.authenticationInteractor( @@ -78,7 +79,7 @@ class SceneContainerStartableTest : SysuiTestCase() { sceneInteractor = sceneInteractor, authenticationInteractor = authenticationInteractor, keyguardInteractor = keyguardInteractor, - featureFlags = featureFlags, + flags = sceneContainerFlags, sysUiState = sysUiState, displayId = Display.DEFAULT_DISPLAY, sceneLogger = mock(), @@ -516,7 +517,8 @@ class SceneContainerStartableTest : SysuiTestCase() { initialSceneKey: SceneKey? = null, authenticationMethod: AuthenticationMethodModel? = null, ): MutableStateFlow<ObservableTransitionState> { - featureFlags.set(Flags.SCENE_CONTAINER, true) + assumeTrue(Flags.SCENE_CONTAINER_ENABLED) + sceneContainerFlags.enabled = true authenticationRepository.setUnlocked(isDeviceUnlocked) keyguardRepository.setBypassEnabled(isBypassEnabled) val transitionStateFlow = diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt new file mode 100644 index 000000000000..17ee3a1cc906 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt @@ -0,0 +1,96 @@ +/* + * Copyright 2023 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. + */ + +package com.android.systemui.scene.shared.flag + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.flags.FakeFeatureFlagsClassic +import com.android.systemui.flags.Flags +import com.android.systemui.flags.ReleasedFlag +import com.android.systemui.flags.ResourceBooleanFlag +import com.android.systemui.flags.UnreleasedFlag +import com.google.common.truth.Truth.assertThat +import org.junit.Assume.assumeTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +@SmallTest +@RunWith(Parameterized::class) +internal class SceneContainerFlagsTest( + private val testCase: TestCase, +) : SysuiTestCase() { + + private lateinit var underTest: SceneContainerFlags + + @Before + fun setUp() { + val featureFlags = + FakeFeatureFlagsClassic().apply { + SceneContainerFlagsImpl.flags.forEach { flag -> + when (flag) { + is ResourceBooleanFlag -> set(flag, testCase.areAllFlagsSet) + is ReleasedFlag -> set(flag, testCase.areAllFlagsSet) + is UnreleasedFlag -> set(flag, testCase.areAllFlagsSet) + else -> error("Unsupported flag type ${flag.javaClass}") + } + } + } + underTest = SceneContainerFlagsImpl(featureFlags, testCase.isComposeAvailable) + } + + @Test + fun isEnabled() { + assumeTrue(Flags.SCENE_CONTAINER_ENABLED) + assertThat(underTest.isEnabled()).isEqualTo(testCase.expectedEnabled) + } + + internal data class TestCase( + val isComposeAvailable: Boolean, + val areAllFlagsSet: Boolean, + val expectedEnabled: Boolean, + ) { + override fun toString(): String { + return """ + (compose=$isComposeAvailable + flags=$areAllFlagsSet) -> expected=$expectedEnabled + """ + .trimIndent() + } + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun testCases() = buildList { + repeat(4) { combination -> + val isComposeAvailable = combination and 0b100 != 0 + val areAllFlagsSet = combination and 0b001 != 0 + + val expectedEnabled = isComposeAvailable && areAllFlagsSet + + add( + TestCase( + isComposeAvailable = isComposeAvailable, + areAllFlagsSet = areAllFlagsSet, + expectedEnabled = expectedEnabled, + ) + ) + } + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt index 4c3c3f9d8da5..4349d738b0f3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt @@ -31,6 +31,7 @@ import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags import com.android.systemui.scene.ui.view.WindowRootView import com.android.systemui.shade.ShadeControllerImpl import com.android.systemui.shade.ShadeLogger @@ -47,6 +48,8 @@ import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.whenever import com.android.systemui.util.view.ViewUtil import com.google.common.truth.Truth.assertThat +import java.util.Optional +import javax.inject.Provider import org.junit.Before import org.junit.Test import org.mockito.ArgumentCaptor @@ -57,40 +60,24 @@ import org.mockito.Mockito.spy import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations -import java.util.Optional -import javax.inject.Provider @SmallTest class PhoneStatusBarViewControllerTest : SysuiTestCase() { - @Mock - private lateinit var shadeViewController: ShadeViewController - @Mock - private lateinit var featureFlags: FeatureFlags - @Mock - private lateinit var moveFromCenterAnimation: StatusBarMoveFromCenterAnimationController - @Mock - private lateinit var sysuiUnfoldComponent: SysUIUnfoldComponent - @Mock - private lateinit var progressProvider: ScopedUnfoldTransitionProgressProvider - @Mock - private lateinit var configurationController: ConfigurationController - @Mock - private lateinit var mStatusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory - @Mock - private lateinit var userChipViewModel: StatusBarUserChipViewModel - @Mock - private lateinit var centralSurfacesImpl: CentralSurfacesImpl - @Mock - private lateinit var commandQueue: CommandQueue - @Mock - private lateinit var shadeControllerImpl: ShadeControllerImpl - @Mock - private lateinit var windowRootView: Provider<WindowRootView> - @Mock - private lateinit var shadeLogger: ShadeLogger - @Mock - private lateinit var viewUtil: ViewUtil + @Mock private lateinit var shadeViewController: ShadeViewController + @Mock private lateinit var featureFlags: FeatureFlags + @Mock private lateinit var moveFromCenterAnimation: StatusBarMoveFromCenterAnimationController + @Mock private lateinit var sysuiUnfoldComponent: SysUIUnfoldComponent + @Mock private lateinit var progressProvider: ScopedUnfoldTransitionProgressProvider + @Mock private lateinit var configurationController: ConfigurationController + @Mock private lateinit var mStatusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory + @Mock private lateinit var userChipViewModel: StatusBarUserChipViewModel + @Mock private lateinit var centralSurfacesImpl: CentralSurfacesImpl + @Mock private lateinit var commandQueue: CommandQueue + @Mock private lateinit var shadeControllerImpl: ShadeControllerImpl + @Mock private lateinit var windowRootView: Provider<WindowRootView> + @Mock private lateinit var shadeLogger: ShadeLogger + @Mock private lateinit var viewUtil: ViewUtil private lateinit var statusBarWindowStateController: StatusBarWindowStateController private lateinit var view: PhoneStatusBarView @@ -109,16 +96,16 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { // create the view and controller on main thread as it requires main looper InstrumentationRegistry.getInstrumentation().runOnMainSync { val parent = FrameLayout(mContext) // add parent to keep layout params - view = LayoutInflater.from(mContext) - .inflate(R.layout.status_bar, parent, false) as PhoneStatusBarView + view = + LayoutInflater.from(mContext).inflate(R.layout.status_bar, parent, false) + as PhoneStatusBarView controller = createAndInitController(view) } } @Test fun onViewAttachedAndDrawn_moveFromCenterAnimationEnabled_moveFromCenterAnimationInitialized() { - whenever(featureFlags.isEnabled(Flags.ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS)) - .thenReturn(true) + whenever(featureFlags.isEnabled(Flags.ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS)).thenReturn(true) val view = createViewMock() val argumentCaptor = ArgumentCaptor.forClass(OnPreDrawListener::class.java) unfoldConfig.isEnabled = true @@ -136,7 +123,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { @Test fun onViewAttachedAndDrawn_statusBarAnimationDisabled_animationNotInitialized() { whenever(featureFlags.isEnabled(Flags.ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS)) - .thenReturn(false) + .thenReturn(false) val view = createViewMock() unfoldConfig.isEnabled = true // create the controller on main thread as it requires main looper @@ -150,8 +137,8 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { @Test fun handleTouchEventFromStatusBar_panelsNotEnabled_returnsFalseAndNoViewEvent() { `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(false) - val returnVal = view.onTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)) + val returnVal = + view.onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)) assertThat(returnVal).isFalse() verify(shadeViewController, never()).handleExternalTouch(any()) } @@ -160,8 +147,8 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { fun handleTouchEventFromStatusBar_viewNotEnabled_returnsTrueAndNoViewEvent() { `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true) `when`(shadeViewController.isViewEnabled).thenReturn(false) - val returnVal = view.onTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)) + val returnVal = + view.onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)) assertThat(returnVal).isTrue() verify(shadeViewController, never()).handleExternalTouch(any()) } @@ -245,22 +232,23 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { private fun createAndInitController(view: PhoneStatusBarView): PhoneStatusBarViewController { return PhoneStatusBarViewController.Factory( - Optional.of(sysuiUnfoldComponent), - Optional.of(progressProvider), - featureFlags, - userChipViewModel, - centralSurfacesImpl, - statusBarWindowStateController, - shadeControllerImpl, - shadeViewController, - windowRootView, - shadeLogger, - viewUtil, - configurationController, - mStatusOverlayHoverListenerFactory - ).create(view).also { - it.init() - } + Optional.of(sysuiUnfoldComponent), + Optional.of(progressProvider), + featureFlags, + FakeSceneContainerFlags(), + userChipViewModel, + centralSurfacesImpl, + statusBarWindowStateController, + shadeControllerImpl, + shadeViewController, + windowRootView, + shadeLogger, + viewUtil, + configurationController, + mStatusOverlayHoverListenerFactory + ) + .create(view) + .also { it.init() } } private class UnfoldConfig : UnfoldTransitionConfig { diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java index 525624547528..65cac6efad1c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java @@ -84,7 +84,7 @@ import java.util.function.Predicate; @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper +@TestableLooper.RunWithLooper(setAsMainLooper = true) public class VolumeDialogImplTest extends SysuiTestCase { VolumeDialogImpl mDialog; View mActiveRinger; @@ -141,6 +141,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { getContext().addMockSystemService(KeyguardManager.class, mKeyguard); mTestableLooper = TestableLooper.get(this); + allowTestableLooperAsMainThread(); when(mPostureController.getDevicePosture()) .thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java index 28b7d4171df1..aa88a46d670c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java @@ -166,7 +166,9 @@ public abstract class SysuiTestCase { } disallowTestableLooperAsMainThread(); mContext.cleanUpReceivers(this.getClass().getSimpleName()); - mFakeBroadcastDispatcher.cleanUpReceivers(this.getClass().getSimpleName()); + if (mFakeBroadcastDispatcher != null) { + mFakeBroadcastDispatcher.cleanUpReceivers(this.getClass().getSimpleName()); + } } @AfterClass diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt index 11ea513c53e7..2e3bb2b545d8 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt @@ -24,6 +24,8 @@ import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.FakeCommandQueue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags +import com.android.systemui.scene.shared.flag.SceneContainerFlags import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.util.mockito.mock @@ -37,6 +39,7 @@ object KeyguardInteractorFactory { @JvmStatic fun create( featureFlags: FakeFeatureFlags = createFakeFeatureFlags(), + sceneContainerFlags: SceneContainerFlags = FakeSceneContainerFlags(), repository: FakeKeyguardRepository = FakeKeyguardRepository(), commandQueue: FakeCommandQueue = FakeCommandQueue(), bouncerRepository: FakeKeyguardBouncerRepository = FakeKeyguardBouncerRepository(), @@ -48,6 +51,7 @@ object KeyguardInteractorFactory { repository = repository, commandQueue = commandQueue, featureFlags = featureFlags, + sceneContainerFlags = sceneContainerFlags, bouncerRepository = bouncerRepository, configurationRepository = configurationRepository, shadeRepository = shadeRepository, @@ -55,6 +59,7 @@ object KeyguardInteractorFactory { repository = repository, commandQueue = commandQueue, featureFlags = featureFlags, + sceneContainerFlags = sceneContainerFlags, bouncerRepository = bouncerRepository, configurationRepository = configurationRepository, shadeRepository = shadeRepository, @@ -72,6 +77,7 @@ object KeyguardInteractorFactory { val repository: FakeKeyguardRepository, val commandQueue: FakeCommandQueue, val featureFlags: FakeFeatureFlags, + val sceneContainerFlags: SceneContainerFlags, val bouncerRepository: FakeKeyguardBouncerRepository, val configurationRepository: FakeConfigurationRepository, val shadeRepository: FakeShadeRepository, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt index 282d79833baa..9dea0a0c26d5 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt @@ -31,7 +31,7 @@ import com.android.systemui.classifier.FalsingCollector import com.android.systemui.classifier.FalsingCollectorFake import com.android.systemui.classifier.domain.interactor.FalsingInteractor import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository -import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.FakeCommandQueue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository @@ -43,6 +43,7 @@ import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.power.data.repository.FakePowerRepository import com.android.systemui.scene.data.repository.SceneContainerRepository import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.shade.data.repository.FakeShadeRepository @@ -67,11 +68,8 @@ class SceneTestUtils( ) { val testDispatcher = StandardTestDispatcher() val testScope = TestScope(testDispatcher) - val featureFlags = - FakeFeatureFlags().apply { - set(Flags.SCENE_CONTAINER, true) - set(Flags.FACE_AUTH_REFACTOR, false) - } + val featureFlags = FakeFeatureFlagsClassic().apply { set(Flags.FACE_AUTH_REFACTOR, false) } + val sceneContainerFlags = FakeSceneContainerFlags().apply { enabled = true } private val userRepository: UserRepository by lazy { FakeUserRepository().apply { val users = listOf(UserInfo(/* id= */ 0, "name", /* flags= */ 0)) @@ -164,6 +162,7 @@ class SceneTestUtils( repository = repository, commandQueue = FakeCommandQueue(), featureFlags = featureFlags, + sceneContainerFlags = sceneContainerFlags, bouncerRepository = FakeKeyguardBouncerRepository(), configurationRepository = FakeConfigurationRepository(), shadeRepository = FakeShadeRepository(), @@ -181,7 +180,7 @@ class SceneTestUtils( repository = BouncerRepository(), authenticationInteractor = authenticationInteractor, sceneInteractor = sceneInteractor, - featureFlags = featureFlags, + flags = sceneContainerFlags, falsingInteractor = falsingInteractor(), ) } @@ -195,7 +194,7 @@ class SceneTestUtils( applicationScope = applicationScope(), bouncerInteractor = bouncerInteractor, authenticationInteractor = authenticationInteractor, - featureFlags = featureFlags, + flags = sceneContainerFlags, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/FakeSceneContainerFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/FakeSceneContainerFlags.kt new file mode 100644 index 000000000000..01a1ecea9997 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/FakeSceneContainerFlags.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2023 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. + */ + +package com.android.systemui.scene.shared.flag + +class FakeSceneContainerFlags( + var enabled: Boolean = false, +) : SceneContainerFlags { + + override fun isEnabled(): Boolean { + return enabled + } + + override fun requirementDescription(): String { + return "" + } +} diff --git a/services/autofill/Android.bp b/services/autofill/Android.bp index eb23f2f9b435..d43a219e6205 100644 --- a/services/autofill/Android.bp +++ b/services/autofill/Android.bp @@ -19,4 +19,19 @@ java_library_static { defaults: ["platform_service_defaults"], srcs: [":services.autofill-sources"], libs: ["services.core"], + static_libs: ["autofill_flags_java_lib"], +} + +aconfig_declarations { + name: "autofill_flags", + package: "android.service.autofill", + srcs: [ + "bugfixes.aconfig", + "features.aconfig", + ], +} + +java_aconfig_library { + name: "autofill_flags_java_lib", + aconfig_declarations: "autofill_flags", } diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig new file mode 100644 index 000000000000..ef237546378e --- /dev/null +++ b/services/autofill/bugfixes.aconfig @@ -0,0 +1,8 @@ +package: "android.service.autofill" + +flag { + name: "test" + namespace: "autofill" + description: "Test flag " + bug: "297380045" +}
\ No newline at end of file diff --git a/services/autofill/features.aconfig b/services/autofill/features.aconfig new file mode 100644 index 000000000000..1e44de61bdd7 --- /dev/null +++ b/services/autofill/features.aconfig @@ -0,0 +1 @@ +package: "android.service.autofill"
\ No newline at end of file diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java index c66fb81f51c6..39756dfee7bf 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java @@ -26,11 +26,14 @@ import android.os.RemoteCallback; import android.os.ShellCommand; import android.os.UserHandle; import android.service.autofill.AutofillFieldClassificationService.Scores; +import android.service.autofill.Flags; import android.view.autofill.AutofillManager; import com.android.internal.os.IResultReceiver; import java.io.PrintWriter; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -60,6 +63,8 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand { return requestGet(pw); case "set": return requestSet(pw); + case "flags": + return requestFlags(pw); default: return handleDefaultCommands(cmd); } @@ -67,7 +72,7 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand { @Override public void onHelp() { - try (final PrintWriter pw = getOutPrintWriter();) { + try (final PrintWriter pw = getOutPrintWriter(); ) { pw.println("AutoFill Service (autofill) commands:"); pw.println(" help"); pw.println(" Prints this help text."); @@ -109,21 +114,24 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand { pw.println(" Sets whether binding to services provided by instant apps is allowed"); pw.println(""); pw.println(" set temporary-augmented-service USER_ID [COMPONENT_NAME DURATION]"); - pw.println(" Temporarily (for DURATION ms) changes the augmented autofill service " - + "implementation."); + pw.println( + " Temporarily (for DURATION ms) changes the augmented autofill service " + + "implementation."); pw.println(" To reset, call with just the USER_ID argument."); pw.println(""); pw.println(" set default-augmented-service-enabled USER_ID [true|false]"); pw.println(" Enable / disable the default augmented autofill service for the user."); pw.println(""); pw.println(" set temporary-detection-service USER_ID [COMPONENT_NAME DURATION]"); - pw.println(" Temporarily (for DURATION ms) changes the autofill detection service " - + "implementation."); + pw.println( + " Temporarily (for DURATION ms) changes the autofill detection service " + + "implementation."); pw.println(" To reset, call with [COMPONENT_NAME 0]."); pw.println(""); pw.println(" get default-augmented-service-enabled USER_ID"); - pw.println(" Checks whether the default augmented autofill service is enabled for " - + "the user."); + pw.println( + " Checks whether the default augmented autofill service is enabled for " + + "the user."); pw.println(""); pw.println(" list sessions [--user USER_ID]"); pw.println(" Lists all pending sessions."); @@ -134,7 +142,34 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand { pw.println(" reset"); pw.println(" Resets all pending sessions and cached service connections."); pw.println(""); + pw.println(" flags"); + pw.println(" Prints out all autofill related flags."); + pw.println(""); + } + } + + private int requestFlags(PrintWriter pw) { + + if (Flags.test()) { + pw.println("Hello Flag World!"); + pw.println(""); + } + + try { + Method[] flagMethods = Flags.class.getMethods(); + // For some reason, unreferenced flags do not show up here + // Maybe compiler optomized them out of bytecode? + for (Method method : flagMethods) { + if (Modifier.isPublic(method.getModifiers())) { + pw.println(method.getName() + ": " + method.invoke(null)); + } + } + } catch (Exception ex) { + pw.println(ex); + return -1; } + + return 0; } private int requestGet(PrintWriter pw) { diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 1ec8b10813cd..131eec36f403 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -116,6 +116,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.proto.ProtoOutputStream; +import android.view.IWindowManager; import android.view.InputChannel; import android.view.InputDevice; import android.view.MotionEvent; @@ -123,6 +124,7 @@ import android.view.WindowManager; import android.view.WindowManager.DisplayImePolicy; import android.view.WindowManager.LayoutParams; import android.view.WindowManager.LayoutParams.SoftInputModeFlags; +import android.view.WindowManagerGlobal; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.ImeTracker; import android.view.inputmethod.InputBinding; @@ -2988,7 +2990,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub "Waiting for the lazy init of mImeDrawsImeNavBarRes"); } final boolean canImeDrawsImeNavBar = - mImeDrawsImeNavBarRes != null && mImeDrawsImeNavBarRes.get(); + mImeDrawsImeNavBarRes != null && mImeDrawsImeNavBarRes.get() + && hasNavigationBarOnCurrentDisplay(); final boolean shouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherLocked( InputMethodService.IME_ACTIVE | InputMethodService.IME_VISIBLE); return (canImeDrawsImeNavBar ? InputMethodNavButtonFlags.IME_DRAWS_IME_NAV_BAR : 0) @@ -2996,6 +2999,21 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub ? InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN : 0); } + /** + * Whether the current display has a navigation bar. When this is {@code false} (e.g. emulator), + * the IME should <em>not</em> draw the IME navigation bar. + */ + @GuardedBy("ImfLock.class") + private boolean hasNavigationBarOnCurrentDisplay() { + final IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); + try { + return wm.hasNavigationBar(mCurTokenDisplayId != INVALID_DISPLAY + ? mCurTokenDisplayId : DEFAULT_DISPLAY); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + @GuardedBy("ImfLock.class") private boolean shouldShowImeSwitcherLocked(int visibility) { if (!mShowOngoingImeSwitcherForPhones) return false; diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java index b8feb4d044bf..967998a790ac 100644 --- a/services/core/java/com/android/server/pm/DeletePackageHelper.java +++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java @@ -435,6 +435,12 @@ final class DeletePackageHelper { } final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier(); + if (outInfo != null) { + // Remember which users are affected, before the installed states are modified + outInfo.mRemovedUsers = (systemApp || userId == UserHandle.USER_ALL) + ? ps.queryInstalledUsers(allUserHandles, /* installed= */true) + : new int[]{userId}; + } if ((!systemApp || (flags & PackageManager.DELETE_SYSTEM_APP) != 0) && userId != UserHandle.USER_ALL) { @@ -630,7 +636,6 @@ final class DeletePackageHelper { // Preserve data by setting flag flags |= PackageManager.DELETE_KEEP_DATA; } - synchronized (mPm.mInstallLock) { deleteInstalledPackageLIF(deletedPs, true, flags, allUserHandles, outInfo, writeSettings); diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index dd043406f653..04abbecbf95c 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -3556,7 +3556,7 @@ final class InstallPackageHelper { logCriticalInfo(Log.WARN, "System package " + packageName + " no longer exists; its data will be wiped"); mInjector.getHandler().post( - () -> mRemovePackageHelper.removePackageData(ps, userIds, null, 0, false)); + () -> mRemovePackageHelper.removePackageData(ps, userIds)); expectingBetter.put(ps.getPackageName(), ps.getPath()); } else { // we still have a disabled system package, but, it still might have @@ -3631,7 +3631,7 @@ final class InstallPackageHelper { // partition], completely remove the package data. final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName); if (ps != null && mPm.mPackages.get(packageName) == null) { - mRemovePackageHelper.removePackageData(ps, userIds, null, 0, false); + mRemovePackageHelper.removePackageData(ps, userIds); } logCriticalInfo(Log.WARN, msg); } diff --git a/services/core/java/com/android/server/pm/PackageRemovedInfo.java b/services/core/java/com/android/server/pm/PackageRemovedInfo.java index c762fd3f9648..5f4452884fc9 100644 --- a/services/core/java/com/android/server/pm/PackageRemovedInfo.java +++ b/services/core/java/com/android/server/pm/PackageRemovedInfo.java @@ -164,8 +164,7 @@ final class PackageRemovedInfo { } } - public void populateUsers(int[] userIds, PackageSetting deletedPackageSetting) { - mRemovedUsers = userIds; + public void populateBroadcastUsers(PackageSetting deletedPackageSetting) { if (mRemovedUsers == null) { mBroadcastUsers = null; return; @@ -173,8 +172,8 @@ final class PackageRemovedInfo { mBroadcastUsers = EMPTY_INT_ARRAY; mInstantUserIds = EMPTY_INT_ARRAY; - for (int i = userIds.length - 1; i >= 0; --i) { - final int userId = userIds[i]; + for (int i = mRemovedUsers.length - 1; i >= 0; --i) { + final int userId = mRemovedUsers[i]; if (deletedPackageSetting.getInstantApp(userId)) { mInstantUserIds = ArrayUtils.appendInt(mInstantUserIds, userId); } else { diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java index d4f30fed6967..2aedf0d8960c 100644 --- a/services/core/java/com/android/server/pm/RemovePackageHelper.java +++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java @@ -295,25 +295,24 @@ final class RemovePackageHelper { outInfo.mInstallerPackageName = ps.getInstallSource().mInstallerPackageName; outInfo.mIsStaticSharedLib = pkg != null && pkg.getStaticSharedLibraryName() != null; outInfo.mRemovedAppId = ps.getAppId(); - outInfo.mRemovedUsers = userIds; - outInfo.mBroadcastUsers = userIds; + outInfo.mBroadcastUsers = outInfo.mRemovedUsers; outInfo.mIsExternal = ps.isExternalStorage(); outInfo.mRemovedPackageVersionCode = ps.getVersionCode(); } } // Called to clean up disabled system packages - public void removePackageData(final PackageSetting deletedPs, @NonNull int[] allUserHandles, - PackageRemovedInfo outInfo, int flags, boolean writeSettings) { + public void removePackageData(final PackageSetting deletedPs, @NonNull int[] allUserHandles) { synchronized (mPm.mInstallLock) { - removePackageDataLIF(deletedPs, allUserHandles, outInfo, flags, writeSettings); + removePackageDataLIF(deletedPs, allUserHandles, /* outInfo= */ null, + /* flags= */ 0, /* writeSettings= */ false); } } /* * This method deletes the package from internal data structures. If the DELETE_KEEP_DATA * flag is not set, the data directory is removed as well. - * make sure this flag is set for partially installed apps. If not its meaningless to + * make sure this flag is set for partially installed apps. If not it's meaningless to * delete a partially installed application. */ @GuardedBy("mPm.mInstallLock") @@ -328,8 +327,7 @@ final class RemovePackageHelper { outInfo.mInstallerPackageName = deletedPs.getInstallSource().mInstallerPackageName; outInfo.mIsStaticSharedLib = deletedPkg != null && deletedPkg.getStaticSharedLibraryName() != null; - outInfo.populateUsers(deletedPs.queryInstalledUsers( - mUserManagerInternal.getUserIds(), true), deletedPs); + outInfo.populateBroadcastUsers(deletedPs); outInfo.mIsExternal = deletedPs.isExternalStorage(); outInfo.mRemovedPackageVersionCode = deletedPs.getVersionCode(); } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 6eace6a920aa..1137681de5cf 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -27,7 +27,6 @@ import static android.content.pm.PackageManager.UNINSTALL_REASON_USER_TYPE; import static android.os.Process.INVALID_UID; import static android.os.Process.PACKAGE_INFO_GID; import static android.os.Process.SYSTEM_UID; - import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import static com.android.server.pm.PackageManagerService.WRITE_USER_PACKAGE_RESTRICTIONS; import static com.android.server.pm.SharedUidMigration.BEST_EFFORT; @@ -4896,8 +4895,6 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile pw.print("]"); } pw.println(); - File dataDir = PackageInfoUtils.getDataDir(ps, UserHandle.myUserId()); - pw.print(prefix); pw.print(" dataDir="); pw.println(dataDir.getAbsolutePath()); if (pkg != null) { pw.print(prefix); pw.print(" versionName="); pw.println(pkg.getVersionName()); pw.print(prefix); pw.print(" usesNonSdkApi="); pw.println(pkg.isNonSdkApiRequested()); @@ -5198,6 +5195,10 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile pw.print(" installReason="); pw.println(userState.getInstallReason()); + File dataDir = PackageInfoUtils.getDataDir(ps, user.id); + pw.print(" dataDir="); + pw.println(dataDir.getAbsolutePath()); + final PackageUserStateInternal pus = ps.readUserState(user.id); pw.print(" firstInstallTime="); date.setTime(pus.getFirstInstallTimeMillis()); diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java index e8acb067f625..6ff7b2601b79 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java @@ -16,15 +16,20 @@ package com.android.inputmethodservice; +import static android.view.WindowInsets.Type.captionBar; + import static com.android.compatibility.common.util.SystemUtil.eventually; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; +import static org.junit.Assume.assumeFalse; import android.app.Instrumentation; import android.content.Context; import android.content.res.Configuration; +import android.graphics.Insets; import android.os.RemoteException; import android.provider.Settings; import android.support.test.uiautomator.By; @@ -32,6 +37,7 @@ import android.support.test.uiautomator.UiDevice; import android.support.test.uiautomator.UiObject2; import android.support.test.uiautomator.Until; import android.util.Log; +import android.view.WindowManagerGlobal; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; @@ -592,6 +598,20 @@ public class InputMethodServiceTest { false /* orientationPortrait */); } + /** + * This checks that when the system navigation bar is not created (e.g. emulator), + * then the IME caption bar is also not created. + */ + @Test + public void testNoNavigationBar_thenImeNoCaptionBar() throws Exception { + boolean hasNavigationBar = WindowManagerGlobal.getWindowManagerService() + .hasNavigationBar(mInputMethodService.getDisplayId()); + assumeFalse("Must not have a navigation bar", hasNavigationBar); + + assertEquals(Insets.NONE, mInputMethodService.getWindow().getWindow().getDecorView() + .getRootWindowInsets().getInsetsIgnoringVisibility(captionBar())); + } + private void verifyInputViewStatus( Runnable runnable, boolean expected, boolean inputViewStarted) throws InterruptedException { diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java index a805e5c9f87e..bea654305a9f 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java @@ -125,7 +125,7 @@ public class PackageManagerServiceTest { Assert.assertNull(pri.mBroadcastUsers); // populateUsers with nothing leaves nothing - pri.populateUsers(null, setting); + pri.populateBroadcastUsers(setting); Assert.assertNull(pri.mBroadcastUsers); // Create a real (non-null) PackageSetting and confirm that the removed @@ -139,9 +139,10 @@ public class PackageManagerServiceTest { .setSecondaryCpuAbiString("secondaryCpuAbiString") .setCpuAbiOverrideString("cpuAbiOverrideString") .build(); - pri.populateUsers(new int[]{ + pri.mRemovedUsers = new int[]{ 1, 2, 3, 4, 5 - }, setting); + }; + pri.populateBroadcastUsers(setting); Assert.assertNotNull(pri.mBroadcastUsers); Assert.assertEquals(5, pri.mBroadcastUsers.length); Assert.assertNotNull(pri.mInstantUserIds); @@ -151,9 +152,10 @@ public class PackageManagerServiceTest { pri.mBroadcastUsers = null; final int EXCLUDED_USER_ID = 4; setting.setInstantApp(true, EXCLUDED_USER_ID); - pri.populateUsers(new int[]{ + pri.mRemovedUsers = new int[]{ 1, 2, 3, EXCLUDED_USER_ID, 5 - }, setting); + }; + pri.populateBroadcastUsers(setting); Assert.assertNotNull(pri.mBroadcastUsers); Assert.assertEquals(4, pri.mBroadcastUsers.length); Assert.assertNotNull(pri.mInstantUserIds); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index ac8200ad420e..0c8603b339d3 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -9894,9 +9894,15 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_USE_ONLY_DIALED_SIM_ECC_LIST_BOOL, false); sDefaults.putString(KEY_CARRIER_NETWORK_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING, ""); sDefaults.putString(KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING, ""); + sDefaults.putString(KEY_CARRIER_NETWORK_SERVICE_WLAN_CLASS_OVERRIDE_STRING, ""); + sDefaults.putString(KEY_CARRIER_NETWORK_SERVICE_WWAN_CLASS_OVERRIDE_STRING, ""); sDefaults.putString(KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_PACKAGE_OVERRIDE_STRING, ""); + sDefaults.putString(KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_CLASS_OVERRIDE_STRING, ""); sDefaults.putString(KEY_CARRIER_DATA_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING, ""); sDefaults.putString(KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING, ""); + sDefaults.putString(KEY_CARRIER_DATA_SERVICE_WWAN_CLASS_OVERRIDE_STRING, ""); + sDefaults.putString(KEY_CARRIER_DATA_SERVICE_WLAN_CLASS_OVERRIDE_STRING, ""); + sDefaults.putString(KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING, ""); sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING, ""); sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING, ""); sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING, ""); diff --git a/tools/hoststubgen/.gitignore b/tools/hoststubgen/.gitignore new file mode 100644 index 000000000000..6453bdef8cee --- /dev/null +++ b/tools/hoststubgen/.gitignore @@ -0,0 +1,3 @@ +out/ +*-out/ +*.log diff --git a/tools/hoststubgen/README.md b/tools/hoststubgen/README.md new file mode 100644 index 000000000000..b0a126292063 --- /dev/null +++ b/tools/hoststubgen/README.md @@ -0,0 +1,76 @@ +# HostStubGen + +## Overview + +This directory contains tools / sample code / investigation for host side test support. + + +## Directories and files + +- `hoststubgen/` + Contains source code of the "hoststubgen" tool and relevant code + + - `framework-policy-override.txt` + This file contains "policy overrides", which allows to control what goes to stub/impl without + having to touch the target java files. This allows quicker iteration, because you can skip + having to rebuild framework.jar. + + - `src/` + + HostStubGen tool source code. + + - `annotations-src/` See `Android.bp`. + - `helper-framework-buildtime-src/` See `Android.bp`. + - `helper-framework-runtime-src/` See `Android.bp`. + - `helper-runtime-src/` See `Android.bp`. + + - `test-tiny-framework/` See `README.md` in it. + + - `test-framework` See `README.md` in it. + +- `scripts` + - `run-host-test.sh` + + Run a host side test. Use it instead of `atest` for now. (`atest` works "mostly" but it has + problems.) + + - `dump-jar.sh` + + A script to dump the content of `*.class` and `*.jar` files. + + - `run-all-tests.sh` + + Run all tests. Many tests may fail, but at least this should run til the end. + (It should print `run-all-tests.sh finished` at the end) + +## Build and run + +### Building `HostStubGen` binary + +``` +m hoststubgen +``` + +### Run the tests + +- Run all relevant tests and test scripts. Some of thests are still expected to fail, + but this should print "finished, with no unexpected failures" at the end. + + However, because some of the script it executes depend on internal file paths to Soong's + intermediate directory, some of it might fail when something changes in the build system. + + We need proper build system integration to fix them. +``` +$ ./scripts/run-all-tests.sh +``` + +- See also `README.md` in `test-*` directories. + +## TODOs, etc + + - Make sure the parent's visibility is not smaller than the member's. + +- @HostSideTestNativeSubstitutionClass should automatically add class-keep to the substitute class. + (or at least check it.) + + - The `HostStubGenTest-framework-test-host-test-lib` jar somehow contain all ASM classes? Figure out where the dependency is coming from. diff --git a/tools/hoststubgen/TEST_MAPPING b/tools/hoststubgen/TEST_MAPPING new file mode 100644 index 000000000000..970362611f53 --- /dev/null +++ b/tools/hoststubgen/TEST_MAPPING @@ -0,0 +1,6 @@ +{ + // TODO: Change to presubmit. + "postsubmit": [ + { "name": "tiny-framework-dump-test" } + ] +} diff --git a/tools/hoststubgen/common.sh b/tools/hoststubgen/common.sh new file mode 100644 index 000000000000..b49ee39a3142 --- /dev/null +++ b/tools/hoststubgen/common.sh @@ -0,0 +1,116 @@ +# Copyright (C) 2023 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. + +set -e # Exit at failure +shopt -s globstar # Enable double-star wildcards (**) + +cd "${0%/*}" # Move to the script dir + +fail() { + echo "Error: $*" 1>&2 + exit 1 +} + +# Print the arguments and then execute. +run() { + echo "Running: $*" 1>&2 + "$@" +} + +# Concatenate the second and subsequent args with the first arg as a separator. +# e.g. `join : a b c` -> prints `a:b:c` +join() { + local IFS="$1" + shift + echo "$*" +} + +abspath() { + for name in "${@}"; do + readlink -f $name + done +} + +m() { + if (( $SKIP_BUILD )) ; then + echo "Skipping build: $*" 1>&2 + return 0 + fi + run ${ANDROID_BUILD_TOP}/build/soong/soong_ui.bash --make-mode "$@" +} + +# Extract given jar files +extract() { + for f in "${@}"; do + local out=$f.ext + run rm -fr $out + run mkdir -p $out + + # It's too noisy, so only show the first few lines. + { + # Hmm unzipping kotlin jar files may produce a warning? Let's just add `|| true`... + run unzip $f -d $out || true + } |& sed -e '5,$d' + echo ' (omitting remaining output)' + + done +} + +# Find all *.java files in $1, and print them as Java class names. +# For example, if there's a file `src/com/android/test/Test.java`, and you run +# `list_all_classes_under_dir src`, then it'll print `com.android.test.Test`. +list_all_classes_under_dir() { + local dir="$1" + ( # Use a subshell, so we won't change the current directory on the caller side. + cd "$dir" + + # List the java files, but replace the slashes with dots, and remove the `.java` suffix. + ls **/*.java | sed -e 's!/!.!g' -e 's!.java$!!' + ) +} + +checkenv() { + # Make sure $ANDROID_BUILD_TOP is set. + : ${ANDROID_BUILD_TOP:?} + + # Make sure ANDROID_BUILD_TOP doesn't contain whitespace. + set ${ANDROID_BUILD_TOP} + if [[ $# != 1 ]] ; then + fail "\$ANDROID_BUILD_TOP cannot contain whitespace." + fi +} + +checkenv + +JAVAC=${JAVAC:-javac} +JAVA=${JAVA:-java} +JAR=${JAR:-jar} + +JAVAC_OPTS=${JAVAC_OPTS:--Xmaxerrs 99999 -Xlint:none} + +SOONG_INT=$ANDROID_BUILD_TOP/out/soong/.intermediates + +JUNIT_TEST_MAIN_CLASS=com.android.hoststubgen.hosthelper.HostTestSuite + +run_junit_test_jar() { + local jar="$1" + echo "Starting test: $jar ..." + run cd "${jar%/*}" + + run $JAVA $JAVA_OPTS \ + -cp $jar \ + org.junit.runner.JUnitCore \ + $main_class || return 1 + return 0 +} diff --git a/tools/hoststubgen/hoststubgen/.gitignore b/tools/hoststubgen/hoststubgen/.gitignore new file mode 100644 index 000000000000..0f384074ed7f --- /dev/null +++ b/tools/hoststubgen/hoststubgen/.gitignore @@ -0,0 +1 @@ +framework-all-stub-out
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/tools/hoststubgen/hoststubgen/Android.bp new file mode 100644 index 000000000000..a617876a6da0 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/Android.bp @@ -0,0 +1,303 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +// This library contains the standard hoststubgen annotations. +java_library { + name: "hoststubgen-annotations", + srcs: [ + "annotations-src/**/*.java", + ], + host_supported: true, + + // Seems like we need it to avoid circular deps. + // Copied it from "app-compat-annotations". + sdk_version: "core_current", + visibility: ["//visibility:public"], +} + +// This library contains helper classes used in the host side test environment at runtime. +// This library is _not_ specific to Android APIs. +java_library_host { + name: "hoststubgen-helper-runtime", + srcs: [ + "helper-runtime-src/**/*.java", + ], + libs: [ + "junit", + "ow2-asm", + "ow2-asm-analysis", + "ow2-asm-commons", + "ow2-asm-tree", + "ow2-asm-util", + ], + static_libs: [ + "guava", + ], + jarjar_rules: "jarjar-rules.txt", + visibility: ["//visibility:public"], +} + +// Host-side stub generator tool. +java_binary_host { + name: "hoststubgen", + main_class: "com.android.hoststubgen.Main", + srcs: ["src/**/*.kt"], + static_libs: [ + "hoststubgen-helper-runtime", + "ow2-asm", + "ow2-asm-analysis", + "ow2-asm-commons", + "ow2-asm-tree", + "ow2-asm-util", + ], + visibility: ["//visibility:public"], +} + +// File that contains the standard command line argumetns to hoststubgen. +filegroup { + name: "hoststubgen-standard-options", + srcs: [ + "hoststubgen-standard-options.txt", + ], + visibility: ["//visibility:public"], +} + +hoststubgen_common_options = "$(location hoststubgen) " + + // "--in-jar $(location :framework-all) " + + // "--policy-override-file $(location framework-policy-override.txt) " + + "@$(location :hoststubgen-standard-options) " + + + "--out-stub-jar $(location host_stub.jar) " + + "--out-impl-jar $(location host_impl.jar) " + + + // "--keep-all-classes " + // Used it for an experiment. See KeepAllClassesFilter. + "--gen-keep-all-file $(location hoststubgen_keep_all.txt) " + + "--gen-input-dump-file $(location hoststubgen_dump.txt) " + + "" + +// Common defaults for stub generation. +// This one is not specific to Android APIs. +genrule_defaults { + name: "hoststubgen-command-defaults", + tools: ["hoststubgen"], + srcs: [ + ":hoststubgen-standard-options", + ], + // Create two jar files. + out: [ + "host_stub.jar", + "host_impl.jar", + + // Following files are created just as FYI. + "hoststubgen_keep_all.txt", + "hoststubgen_dump.txt", + ], + // visibility: ["//visibility:public"], +} + +// Generate the stub/impl from framework-all, with hidden APIs. +java_genrule_host { + name: "framework-all-hidden-api-host", + defaults: ["hoststubgen-command-defaults"], + cmd: hoststubgen_common_options + + "--in-jar $(location :framework-all) " + + "--policy-override-file $(location framework-policy-override.txt) ", + srcs: [ + ":framework-all", + "framework-policy-override.txt", + ], + visibility: ["//visibility:private"], +} + +// Extract the stub jar from "framework-all-host" for subsequent build rules. +java_genrule_host { + name: "framework-all-hidden-api-host-stub", + cmd: "cp $(in) $(out)", + srcs: [ + ":framework-all-hidden-api-host{host_stub.jar}", + ], + out: [ + "host_stub.jar", + ], + visibility: ["//visibility:public"], +} + +// Extract the impl jar from "framework-all-host" for subsequent build rules. +java_genrule_host { + name: "framework-all-hidden-api-host-impl", + cmd: "cp $(in) $(out)", + srcs: [ + ":framework-all-hidden-api-host{host_impl.jar}", + ], + out: [ + "host_impl.jar", + ], + visibility: ["//visibility:public"], +} + +// Generate the stub/impl from framework-all, with only public/system/test APIs, without +// hidden APIs. +java_genrule_host { + name: "framework-all-test-api-host", + defaults: ["hoststubgen-command-defaults"], + cmd: hoststubgen_common_options + + "--intersect-stub-jar $(location :android_test_stubs_current{.jar}) " + + "--in-jar $(location :framework-all) " + + "--policy-override-file $(location framework-policy-override.txt) ", + srcs: [ + ":framework-all", + ":android_test_stubs_current{.jar}", + "framework-policy-override.txt", + ], + visibility: ["//visibility:private"], +} + +// Extract the stub jar from "framework-all-test-api-host" for subsequent build rules. +java_genrule_host { + name: "framework-all-test-api-host-stub", + cmd: "cp $(in) $(out)", + srcs: [ + ":framework-all-test-api-host{host_stub.jar}", + ], + out: [ + "host_stub.jar", + ], + visibility: ["//visibility:public"], +} + +// Extract the impl jar from "framework-all-test-api-host" for subsequent build rules. +java_genrule_host { + name: "framework-all-test-api-host-impl", + cmd: "cp $(in) $(out)", + srcs: [ + ":framework-all-test-api-host{host_impl.jar}", + ], + out: [ + "host_impl.jar", + ], + visibility: ["//visibility:public"], +} + +// This library contains helper classes to build hostside tests/targets. +// This essentially contains dependencies from tests that we can't actually use the real ones. +// For example, the actual AndroidTestCase and AndroidJUnit4 don't run on the host side (yet), +// so we pup "fake" implementations here. +// Ideally this library should be empty. +java_library_host { + name: "hoststubgen-helper-framework-buildtime", + srcs: [ + "helper-framework-buildtime-src/**/*.java", + ], + libs: [ + // We need it to pull in some of the framework classes used in this library, + // such as Context.java. + "framework-all-hidden-api-host-impl", + "junit", + ], + visibility: ["//visibility:public"], +} + +// This module contains "fake" libcore/dalvik classes, framework native substitution, etc, +// that are needed at runtime. +java_library_host { + name: "hoststubgen-helper-framework-runtime", + srcs: [ + "helper-framework-runtime-src/**/*.java", + ], + libs: [ + "hoststubgen-helper-runtime", + "framework-all-hidden-api-host-impl", + ], + visibility: ["//visibility:public"], +} + +// Defaults for host side test modules. +// We need two rules for each test. +// 1. A "-test-lib" jar, which compiles the test against the stub jar. +// This one is only used by the second rule, so it should be "private. +// 2. A "-test" jar, which includes 1 + the runtime (impl) jars. + +// This and next ones are for tests using framework-app, with hidden APIs. +java_defaults { + name: "hosttest-with-framework-all-hidden-api-test-lib-defaults", + installable: false, + libs: [ + "framework-all-hidden-api-host-stub", + ], + static_libs: [ + "hoststubgen-helper-framework-buildtime", + "framework-annotations-lib", + ], + visibility: ["//visibility:private"], +} + +// Default rules to include `libandroid_runtime`. For now, it's empty, but we'll use it +// once we start using JNI. +java_defaults { + name: "hosttest-with-libandroid_runtime", + jni_libs: [ + // "libandroid_runtime", + + // TODO: Figure out how to build them automatically. + // Following ones are depended by libandroid_runtime. + // Without listing them here, not only we won't get them under + // $ANDROID_HOST_OUT/testcases/*/lib64, but also not under + // $ANDROID_HOST_OUT/lib64, so we'd fail to load them at runtime. + // ($ANDROID_HOST_OUT/lib/ gets all of them though.) + // "libcutils", + // "libharfbuzz_ng", + // "libminikin", + // "libz", + // "libbinder", + // "libhidlbase", + // "libvintf", + // "libicu", + // "libutils", + // "libtinyxml2", + ], +} + +java_defaults { + name: "hosttest-with-framework-all-hidden-api-test-defaults", + defaults: ["hosttest-with-libandroid_runtime"], + installable: false, + test_config: "AndroidTest-host.xml", + static_libs: [ + "hoststubgen-helper-runtime", + "hoststubgen-helper-framework-runtime", + "framework-all-hidden-api-host-impl", + ], +} + +// This and next ones are for tests using framework-app, with public/system/test APIs, +// without hidden APIs. +java_defaults { + name: "hosttest-with-framework-all-test-api-test-lib-defaults", + installable: false, + libs: [ + "framework-all-test-api-host-stub", + ], + static_libs: [ + "hoststubgen-helper-framework-buildtime", + "framework-annotations-lib", + ], + visibility: ["//visibility:private"], +} + +java_defaults { + name: "hosttest-with-framework-all-test-api-test-defaults", + defaults: ["hosttest-with-libandroid_runtime"], + installable: false, + test_config: "AndroidTest-host.xml", + static_libs: [ + "hoststubgen-helper-runtime", + "hoststubgen-helper-framework-runtime", + "framework-all-test-api-host-impl", + ], +} diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java new file mode 100644 index 000000000000..a774336a897c --- /dev/null +++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2023 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. + */ +package android.hosttest.annotation; + +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * Add this with a fully-specified method name (e.g. {@code "com.package.Class.methodName"}) + * of a callback to get a callback at the class load time. + * + * The method must be {@code public static} with a single argument that takes + * {@link java.lang.Class}. + */ +@Target({TYPE}) +@Retention(RetentionPolicy.CLASS) +public @interface HostSideTestClassLoadHook { + String value(); +} diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java new file mode 100644 index 000000000000..06ad1c266a14 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 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. + */ +package android.hosttest.annotation; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * Mark a class, field or a method as "Stub", meaning tests can _not_ see the APIs, but they + * can indirectly be used on the host side. + * When applied to a class, it will _not_ affect the visibility of its members. They need to be + * individually marked. + * + * <p>In order to expose a class and all its members, use {@link HostSideTestWholeClassStub} + * instead. + * @hide + */ +@Target({TYPE, FIELD, METHOD, CONSTRUCTOR}) +@Retention(RetentionPolicy.CLASS) +public @interface HostSideTestKeep { +} diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestNativeSubstitutionClass.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestNativeSubstitutionClass.java new file mode 100644 index 000000000000..9c8138351eb5 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestNativeSubstitutionClass.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 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. + */ +package android.hosttest.annotation; + +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * If a class has this annotation, all its native methods will be delegated to another class. + * (See {@link android.os.Parcel} as an example.) + */ +@Target({TYPE}) +@Retention(RetentionPolicy.CLASS) +public @interface HostSideTestNativeSubstitutionClass { + String value(); +} diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java new file mode 100644 index 000000000000..46e5078fb05d --- /dev/null +++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 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. + */ +package android.hosttest.annotation; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * Mark an item as "remove", so this cannot be used on the host side even indirectly. + * This is the default behavior. + * + * @hide + */ +@Target({TYPE, FIELD, METHOD, CONSTRUCTOR}) +@Retention(RetentionPolicy.CLASS) +public @interface HostSideTestRemove { +} diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStub.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStub.java new file mode 100644 index 000000000000..cabdfe0eeb77 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStub.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 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. + */ +package android.hosttest.annotation; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * Mark a class, field or a method as "Stub", meaning tests can see the APIs. + * When applied to a class, it will _not_ affect the visibility of its members. They need to be + * individually marked. + * + * <p>In order to expose a class and all its members, use {@link HostSideTestWholeClassStub} + * instead. + * + * @hide + */ +@Target({TYPE, FIELD, METHOD, CONSTRUCTOR}) +@Retention(RetentionPolicy.CLASS) +public @interface HostSideTestStub { +} diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java new file mode 100644 index 000000000000..510a67e0aaed --- /dev/null +++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2023 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. + */ +package android.hosttest.annotation; + +import static java.lang.annotation.ElementType.METHOD; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * If a method has this annotation, we'll replace it with another method on the host side. + * + * See {@link android.util.LruCache#getEldest()} and its substitution. + * + * @hide + */ +@Target({METHOD}) +@Retention(RetentionPolicy.CLASS) +public @interface HostSideTestSubstitute { + // TODO We should add "_host" as default. We're not doing it yet, because extractign the default + // value with ASM doesn't seem trivial. (? not sure.) + String suffix(); +} diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java new file mode 100644 index 000000000000..cd1bef4be505 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2023 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. + */ +package android.hosttest.annotation; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * If a method has this annotation, it will throw on the host side. + * + * @hide + */ +@Target({METHOD, CONSTRUCTOR}) +@Retention(RetentionPolicy.CLASS) +public @interface HostSideTestThrow { +} diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java new file mode 100644 index 000000000000..3d1ddea2cbb7 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 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. + */ +package android.hosttest.annotation; + +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * Same as {@link HostSideTestKeep} but it'll change the visibility of all its members too. + * @hide + */ +@Target({TYPE}) +@Retention(RetentionPolicy.CLASS) +public @interface HostSideTestWholeClassKeep { +} diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassStub.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassStub.java new file mode 100644 index 000000000000..1824f6f01516 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassStub.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 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. + */ +package android.hosttest.annotation; + +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY + * QUESTIONS ABOUT IT. + * + * Same as {@link HostSideTestStub} but it'll change the visibility of all its members too. + * + * @hide + */ +@Target({TYPE}) +@Retention(RetentionPolicy.CLASS) +public @interface HostSideTestWholeClassStub { +} diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java new file mode 100644 index 000000000000..b10f0ff1a4b1 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023 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. + */ +package android.hosttest.annotation.tests; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Target; + +/** + * Use this annotation to skip certain tests for host side tests. + * + * TODO: Actually use it in the test runner. + */ +@Target({TYPE, FIELD, METHOD}) +public @interface HostSideTestSuppress { +} diff --git a/tools/hoststubgen/hoststubgen/framework-policy-override.txt b/tools/hoststubgen/hoststubgen/framework-policy-override.txt new file mode 100644 index 000000000000..295498da02e4 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/framework-policy-override.txt @@ -0,0 +1,98 @@ +# -------------------------------------------------------------------------------------------------- +# This file contains rules to process `framework-all.jar` to generate the host side test "stub" and +# "impl" jars, without using Java annotations. +# +# Useful when: +# - The class is auto-generated and annotations can't be added. +# (We need to figure out what to do on auto-generated classes.) +# - Want to quickly change filter rules without having to rebuild framework.jar. +# +# Using this file, one can control the visibility of APIs on a per-class, per-field and per-method +# basis, but in most cases, per-class directives would be sufficient. That is: +# +# - To put the entire class, including its members and nested classes, in the "stub" jar, +# so that the test / target code can use the API, use `stubclass`. +# +# class package.class stubclass +# +# - To put the entire class, including its members and nested classes, in the "impl" jar, +# but not in the "stub" jar, use `keepclass`. Use this when you don't want to expose an API to +# tests/target directly, but it's still needed at runtime, because it's used by other "stub" APIs +# directly or indirectly. +# +# class package.class keepclass +# +# All other classes will be removed from both the stub jar and impl jar. +# +# -------------------------------------------------------------------------------------------------- + +# -------------------------------------------------------------------------------------------------- +# Directions on auto-generated classes, where we can't use Java annotations (yet). +# -------------------------------------------------------------------------------------------------- +class android.Manifest stubclass +class android.R stubclass +class android.os.PersistableBundleProto keepclass + +# This is in module-utils, where using a HostStubGen annotation would be complicated, so we +# add a direction here rather than using a java annotation. +# The build file says it's deprecated, anyway...? Figure out what to do with it. +class com.android.internal.util.Preconditions keepclass + +# -------------------------------------------------------------------------------------------------- +# Actual framework classes +# -------------------------------------------------------------------------------------------------- + +# Put basic exception classes in the "impl" jar. +# We don't put them in stub yet (until something actually needs them). +class android.os.DeadObjectException keepclass +class android.os.DeadSystemRuntimeException keepclass +class android.os.NetworkOnMainThreadException keepclass +class android.os.RemoteException keepclass +class android.os.ServiceSpecificException keepclass +class android.util.AndroidException keepclass +class android.util.AndroidRuntimeException keepclass +class android.os.DeadSystemException keepclass + + +# For now, we only want to expose ArrayMap and Log, but they and their tests depend on +# more classes. + +class android.util.ArrayMap stubclass + +# Used by ArrayMap. No need to put them in the stub, but we need them in impl. +class android.util.MapCollections keepclass +class android.util.ContainerHelpers keepclass +class com.android.internal.util.XmlUtils keepclass +class com.android.internal.util.FastMath keepclass +class android.util.MathUtils keepclass + + +class android.util.Log stubclass +class android.util.Slog stubclass +# We don't use Log's native code, yet. Instead, the following line enables the Java substitution. +# Comment it out to disable Java substitution of Log's native methods. +class android.util.Log !com.android.hoststubgen.nativesubstitution.Log_host + +# Used by log +class com.android.internal.util.FastPrintWriter keepclass +class com.android.internal.util.LineBreakBufferedWriter keepclass + + +# Expose Context because it's referred to by AndroidTestCase, but don't need to expose any of +# its members. +class android.content.Context keep + +# Expose Parcel, Parcel and there relevant classes, which are used by ArrayMapTets. +class android.os.Parcelable StubClass +class android.os.Parcel StubClass +class android.os.Parcel !com.android.hoststubgen.nativesubstitution.Parcel_host + +class android.os.IBinder stubClass +class android.os.IInterface stubclass + +class android.os.BadParcelableException stubclass +class android.os.BadTypeParcelableException stubclass + +class android.os.BaseBundle stubclass +class android.os.Bundle stubclass +class android.os.PersistableBundle stubclass diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/android/test/AndroidTestCase.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/android/test/AndroidTestCase.java new file mode 100644 index 000000000000..e6d38668335a --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/android/test/AndroidTestCase.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2023 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. + */ +package android.test; + +import android.content.Context; + +import junit.framework.TestCase; + +public class AndroidTestCase extends TestCase { + protected Context mContext; + public Context getContext() { + throw new RuntimeException("[ravenwood] Class Context is not supported yet."); + } +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/annotation/NonNull.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/annotation/NonNull.java new file mode 100644 index 000000000000..51c5d9a05e52 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/annotation/NonNull.java @@ -0,0 +1,40 @@ +/* + * 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. + */ +package androidx.annotation; + +// [ravenwood] TODO: Find the actual androidx jar containing it.s + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Denotes that a parameter, field or method return value can never be null. + * <p> + * This is a marker annotation and it has no specific attributes. + * + * @paramDoc This value cannot be {@code null}. + * @returnDoc This value cannot be {@code null}. + * @hide + */ +@Retention(SOURCE) +@Target({METHOD, PARAMETER, FIELD}) +public @interface NonNull { +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/annotation/Nullable.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/annotation/Nullable.java new file mode 100644 index 000000000000..f1f0e8b43f16 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/annotation/Nullable.java @@ -0,0 +1,47 @@ +/* + * 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. + */ +package androidx.annotation; + +// [ravenwood] TODO: Find the actual androidx jar containing it.s + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Denotes that a parameter, field or method return value can be null. + * <p> + * When decorating a method call parameter, this denotes that the parameter can + * legitimately be null and the method will gracefully deal with it. Typically + * used on optional parameters. + * <p> + * When decorating a method, this denotes the method might legitimately return + * null. + * <p> + * This is a marker annotation and it has no specific attributes. + * + * @paramDoc This value may be {@code null}. + * @returnDoc This value may be {@code null}. + * @hide + */ +@Retention(SOURCE) +@Target({METHOD, PARAMETER, FIELD}) +public @interface Nullable { +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/ext/junit/runners/AndroidJUnit4.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/ext/junit/runners/AndroidJUnit4.java new file mode 100644 index 000000000000..0c82e4e268d3 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/ext/junit/runners/AndroidJUnit4.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 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. + */ + +package androidx.test.ext.junit.runners; + +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.InitializationError; + +// TODO: We need to simulate the androidx test runner. +// https://source.corp.google.com/piper///depot/google3/third_party/android/androidx_test/ext/junit/java/androidx/test/ext/junit/runners/AndroidJUnit4.java +// https://source.corp.google.com/piper///depot/google3/third_party/android/androidx_test/runner/android_junit_runner/java/androidx/test/internal/runner/junit4/AndroidJUnit4ClassRunner.java + +public class AndroidJUnit4 extends BlockJUnit4ClassRunner { + public AndroidJUnit4(Class<?> testClass) throws InitializationError { + super(testClass); + } +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/FlakyTest.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/FlakyTest.java new file mode 100644 index 000000000000..2470d8390f5d --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/FlakyTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014 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. + */ +package androidx.test.filters; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Designates a test as being flaky (non-deterministic). + * + * <p>Can then be used to filter tests on execution using -e annotation or -e notAnnotation as + * desired. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface FlakyTest { + /** + * An optional bug number associated with the test. -1 Means that no bug number is associated with + * the flaky annotation. + * + * @return int + */ + int bugId() default -1; + + /** + * Details, such as the reason of why the test is flaky. + * + * @return String + */ + String detail() default ""; +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/LargeTest.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/LargeTest.java new file mode 100644 index 000000000000..578d7dc73647 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/LargeTest.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016 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. + */ + +package androidx.test.filters; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to assign a large test size qualifier to a test. This annotation can be used at a + * method or class level. + * + * <p>Test size qualifiers are a great way to structure test code and are used to assign a test to a + * test suite of similar run time. + * + * <p>Execution time: >1000ms + * + * <p>Large tests should be focused on testing integration of all application components. These + * tests fully participate in the system and may make use of all resources such as databases, file + * systems and network. As a rule of thumb most functional UI tests are large tests. + * + * <p>Note: This class replaces the deprecated Android platform size qualifier <a + * href="{@docRoot}reference/android/test/suitebuilder/annotation/LargeTest.html"><code> + * android.test.suitebuilder.annotation.LargeTest</code></a> and is the recommended way to annotate + * tests written with the AndroidX Test Library. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface LargeTest {} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/MediumTest.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/MediumTest.java new file mode 100644 index 000000000000..dfdaa53ee6ac --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/MediumTest.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2016 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. + */ + +package androidx.test.filters; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to assign a medium test size qualifier to a test. This annotation can be used at a + * method or class level. + * + * <p>Test size qualifiers are a great way to structure test code and are used to assign a test to a + * test suite of similar run time. + * + * <p>Execution time: <1000ms + * + * <p>Medium tests should be focused on a very limited subset of components or a single component. + * Resource access to the file system through well defined interfaces like databases, + * ContentProviders, or Context is permitted. Network access should be restricted, (long-running) + * blocking operations should be avoided and use mock objects instead. + * + * <p>Note: This class replaces the deprecated Android platform size qualifier <a + * href="{@docRoot}reference/android/test/suitebuilder/annotation/MediumTest.html"><code> + * android.test.suitebuilder.annotation.MediumTest</code></a> and is the recommended way to annotate + * tests written with the AndroidX Test Library. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface MediumTest {} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/RequiresDevice.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/RequiresDevice.java new file mode 100644 index 000000000000..3d3ee3318bfa --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/RequiresDevice.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014 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. + */ +package androidx.test.filters; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates that a specific test should not be run on emulator. + * + * <p>It will be executed only if the test is running on the physical android device. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +public @interface RequiresDevice {} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/SdkSuppress.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/SdkSuppress.java new file mode 100644 index 000000000000..dd65ddb382dc --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/SdkSuppress.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 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. + */ +package androidx.test.filters; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates that a specific test or class requires a minimum or maximum API Level to execute. + * + * <p>Test(s) will be skipped when executed on android platforms less/more than specified level + * (inclusive). + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +public @interface SdkSuppress { + /** The minimum API level to execute (inclusive) */ + int minSdkVersion() default 1; + /** The maximum API level to execute (inclusive) */ + int maxSdkVersion() default Integer.MAX_VALUE; + /** + * The {@link android.os.Build.VERSION.CODENAME} to execute on. This is intended to be used to run + * on a pre-release SDK, where the {@link android.os.Build.VERSION.SDK_INT} has not yet been + * finalized. This is treated as an OR operation with respect to the minSdkVersion and + * maxSdkVersion attributes. + * + * <p>For example, to filter a test so it runs on only the prerelease R SDK: <code> + * {@literal @}SdkSuppress(minSdkVersion = Build.VERSION_CODES.R, codeName = "R") + * </code> + */ + String codeName() default "unset"; +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/SmallTest.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/SmallTest.java new file mode 100644 index 000000000000..dd32df44effe --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/SmallTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016 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. + */ + +package androidx.test.filters; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to assign a small test size qualifier to a test. This annotation can be used at a + * method or class level. + * + * <p>Test size qualifiers are a great way to structure test code and are used to assign a test to a + * test suite of similar run time. + * + * <p>Execution time: <200ms + * + * <p>Small tests should be run very frequently. Focused on units of code to verify specific logical + * conditions. These tests should runs in an isolated environment and use mock objects for external + * dependencies. Resource access (such as file system, network, or databases) are not permitted. + * Tests that interact with hardware, make binder calls, or that facilitate android instrumentation + * should not use this annotation. + * + * <p>Note: This class replaces the deprecated Android platform size qualifier <a + * href="http://developer.android.com/reference/android/test/suitebuilder/annotation/SmallTest.html"> + * android.test.suitebuilder.annotation.SmallTest</a> and is the recommended way to annotate tests + * written with the AndroidX Test Library. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface SmallTest {} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/Suppress.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/Suppress.java new file mode 100644 index 000000000000..88e636c2dd77 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/Suppress.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 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. + */ + +package androidx.test.filters; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Use this annotation on test classes or test methods that should not be included in a test suite. + * If the annotation appears on the class then no tests in that class will be included. If the + * annotation appears only on a test method then only that method will be excluded. + * + * <p>Note: This class replaces the deprecated Android platform annotation <a + * href="http://developer.android.com/reference/android/test/suitebuilder/annotation/Suppress.html"> + * android.test.suitebuilder.annotation.Suppress</a> and is the recommended way to suppress tests + * written with the AndroidX Test Library. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface Suppress {} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/runner/AndroidJUnit4.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/runner/AndroidJUnit4.java new file mode 100644 index 000000000000..e1379390f98b --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/runner/AndroidJUnit4.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 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. + */ +package androidx.test.runner; + +import org.junit.runners.model.InitializationError; + +public class AndroidJUnit4 extends androidx.test.ext.junit.runners.AndroidJUnit4 { + public AndroidJUnit4(Class<?> testClass) throws InitializationError { + super(testClass); + } +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Log_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Log_host.java new file mode 100644 index 000000000000..ee55c7ac6ae2 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Log_host.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.nativesubstitution; + +import android.util.Log; +import android.util.Log.Level; + +import java.io.PrintStream; + +public class Log_host { + + public static boolean isLoggable(String tag, @Level int level) { + return true; + } + + public static int println_native(int bufID, int priority, String tag, String msg) { + final PrintStream out = System.out; + final String buffer; + switch (bufID) { + case Log.LOG_ID_MAIN: buffer = "main"; break; + case Log.LOG_ID_RADIO: buffer = "radio"; break; + case Log.LOG_ID_EVENTS: buffer = "event"; break; + case Log.LOG_ID_SYSTEM: buffer = "system"; break; + case Log.LOG_ID_CRASH: buffer = "crash"; break; + default: buffer = "buf:" + bufID; break; + }; + + final String prio; + switch (priority) { + case Log.VERBOSE: prio = "V"; break; + case Log.DEBUG: prio = "D"; break; + case Log.INFO: prio = "I"; break; + case Log.WARN: prio = "W"; break; + case Log.ERROR: prio = "E"; break; + case Log.ASSERT: prio = "A"; break; + default: prio = "prio:" + priority; break; + }; + + for (String s : msg.split("\\n")) { + out.println(String.format("logd: [%s] %s %s: %s", buffer, prio, tag, s)); + } + return msg.length(); + } + + public static int logger_entry_max_payload_native() { + return 4068; // [ravenwood] This is what people use in various places. + } +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java new file mode 100644 index 000000000000..d749f076f3b5 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.nativesubstitution; + +import android.os.IBinder; + +import java.io.FileDescriptor; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Tentative, partial implementation of the Parcel native methods, using Java's + * {@link ByteBuffer}. It turned out there's enough semantics differences between Parcel + * and {@link ByteBuffer}, so it didn't actually work. + * (e.g. Parcel seems to allow moving the data position to be beyond its size? Which + * {@link ByteBuffer} wouldn't allow...) + */ +public class Parcel_host { + private Parcel_host() { + } + + private static final AtomicLong sNextId = new AtomicLong(0); + + private static final Map<Long, Parcel_host> sInstances = new ConcurrentHashMap<>(); + + private boolean mDeleted = false; + + private byte[] mBuffer; + private int mSize; + private int mPos; + + private boolean mSensitive; + private boolean mAllowFds; + + // TODO Use the actual value from Parcel.java. + private static final int OK = 0; + + private void validate() { + if (mDeleted) { + // TODO: Put more info + throw new RuntimeException("Parcel already destroyed"); + } + } + + private static Parcel_host getInstance(long id) { + Parcel_host p = sInstances.get(id); + if (p == null) { + // TODO: Put more info + throw new RuntimeException("Parcel doesn't exist with id=" + id); + } + p.validate(); + return p; + } + + public static long nativeCreate() { + final long id = sNextId.getAndIncrement(); + final Parcel_host p = new Parcel_host(); + sInstances.put(id, p); + p.init(); + return id; + } + + private void init() { + mBuffer = new byte[0]; + mSize = 0; + mPos = 0; + mSensitive = false; + mAllowFds = false; + } + + private void updateSize() { + if (mSize < mPos) { + mSize = mPos; + } + } + + public static void nativeDestroy(long nativePtr) { + getInstance(nativePtr).mDeleted = true; + sInstances.remove(nativePtr); + } + + public static void nativeFreeBuffer(long nativePtr) { + getInstance(nativePtr).freeBuffer(); + } + + public void freeBuffer() { + init(); + } + + private int getCapacity() { + return mBuffer.length; + } + + private void ensureMoreCapacity(int size) { + ensureCapacity(mPos + size); + } + + private void ensureCapacity(int targetSize) { + if (targetSize <= getCapacity()) { + return; + } + var newSize = getCapacity() * 2; + if (newSize < targetSize) { + newSize = targetSize; + } + forceSetCapacity(newSize); + } + + private void forceSetCapacity(int newSize) { + var newBuf = new byte[newSize]; + + // Copy + System.arraycopy(mBuffer, 0, newBuf, 0, Math.min(newSize, getCapacity())); + + this.mBuffer = newBuf; + } + + private void ensureDataAvailable(int requestSize) { + if (mSize - mPos < requestSize) { + throw new RuntimeException(String.format( + "Pacel data underflow. size=%d, pos=%d, request=%d", mSize, mPos, requestSize)); + } + } + + public static void nativeMarkSensitive(long nativePtr) { + getInstance(nativePtr).mSensitive = true; + } + public static void nativeMarkForBinder(long nativePtr, IBinder binder) { + throw new RuntimeException("Not implemented yet"); + } + public static boolean nativeIsForRpc(long nativePtr) { + throw new RuntimeException("Not implemented yet"); + } + public static int nativeDataSize(long nativePtr) { + return getInstance(nativePtr).mSize; + } + public static int nativeDataAvail(long nativePtr) { + var p = getInstance(nativePtr); + return p.mSize - p.mPos; + } + public static int nativeDataPosition(long nativePtr) { + return getInstance(nativePtr).mPos; + } + public static int nativeDataCapacity(long nativePtr) { + return getInstance(nativePtr).mBuffer.length; + } + public static void nativeSetDataSize(long nativePtr, int size) { + var p = getInstance(nativePtr); + p.ensureCapacity(size); + getInstance(nativePtr).mSize = size; + } + public static void nativeSetDataPosition(long nativePtr, int pos) { + var p = getInstance(nativePtr); + // TODO: Should this change the size or the capacity?? + p.mPos = pos; + } + public static void nativeSetDataCapacity(long nativePtr, int size) { + var p = getInstance(nativePtr); + if (p.getCapacity() < size) { + p.forceSetCapacity(size); + } + } + + public static boolean nativePushAllowFds(long nativePtr, boolean allowFds) { + var p = getInstance(nativePtr); + var prev = p.mAllowFds; + p.mAllowFds = allowFds; + return prev; + } + public static void nativeRestoreAllowFds(long nativePtr, boolean lastValue) { + getInstance(nativePtr).mAllowFds = lastValue; + } + + public static void nativeWriteByteArray(long nativePtr, byte[] b, int offset, int len) { + nativeWriteBlob(nativePtr, b, offset, len); + } + + public static void nativeWriteBlob(long nativePtr, byte[] b, int offset, int len) { + var p = getInstance(nativePtr); + + if (b == null) { + nativeWriteInt(nativePtr, -1); + } else { + final var alignedSize = align4(b.length); + + nativeWriteInt(nativePtr, b.length); + + p.ensureMoreCapacity(alignedSize); + + System.arraycopy(b, offset, p.mBuffer, p.mPos, len); + p.mPos += alignedSize; + p.updateSize(); + } + } + + public static int nativeWriteInt(long nativePtr, int value) { + var p = getInstance(nativePtr); + p.ensureMoreCapacity(Integer.BYTES); + + p.mBuffer[p.mPos++] = (byte) ((value >> 24) & 0xff); + p.mBuffer[p.mPos++] = (byte) ((value >> 16) & 0xff); + p.mBuffer[p.mPos++] = (byte) ((value >> 8) & 0xff); + p.mBuffer[p.mPos++] = (byte) ((value >> 0) & 0xff); + + p.updateSize(); + + return OK; + } + + public static int nativeWriteLong(long nativePtr, long value) { + nativeWriteInt(nativePtr, (int) (value >>> 32)); + nativeWriteInt(nativePtr, (int) (value)); + return OK; + } + public static int nativeWriteFloat(long nativePtr, float val) { + return nativeWriteInt(nativePtr, Float.floatToIntBits(val)); + } + public static int nativeWriteDouble(long nativePtr, double val) { + return nativeWriteLong(nativePtr, Double.doubleToLongBits(val)); + } + public static void nativeSignalExceptionForError(int error) { + throw new RuntimeException("Not implemented yet"); + } + + private static int align4(int val) { + return ((val + 3) / 4) * 4; + } + + public static void nativeWriteString8(long nativePtr, String val) { + if (val == null) { + nativeWriteBlob(nativePtr, null, 0, 0); + } else { + var bytes = val.getBytes(StandardCharsets.UTF_8); + nativeWriteBlob(nativePtr, bytes, 0, bytes.length); + } + } + public static void nativeWriteString16(long nativePtr, String val) { + // Just reuse String8 + nativeWriteString8(nativePtr, val); + } + public static void nativeWriteStrongBinder(long nativePtr, IBinder val) { + throw new RuntimeException("Not implemented yet"); + } + public static void nativeWriteFileDescriptor(long nativePtr, FileDescriptor val) { + throw new RuntimeException("Not implemented yet"); + } + + public static byte[] nativeCreateByteArray(long nativePtr) { + return nativeReadBlob(nativePtr); + } + + public static boolean nativeReadByteArray(long nativePtr, byte[] dest, int destLen) { + if (dest == null) { + return false; + } + var data = nativeReadBlob(nativePtr); + if (data == null) { + System.err.println("Percel has NULL, which is unexpected."); // TODO: Is this correct? + return false; + } + // TODO: Make sure the check logic is correct. + if (data.length != destLen) { + System.err.println("Byte array size mismatch: expected=" + + data.length + " given=" + destLen); + return false; + } + return true; + } + + public static byte[] nativeReadBlob(long nativePtr) { + final var size = nativeReadInt(nativePtr); + if (size == -1) { + return null; + } + var p = getInstance(nativePtr); + p.ensureDataAvailable(size); + + var bytes = new byte[size]; + System.arraycopy(p.mBuffer, p.mPos, bytes, 0, size); + + p.mPos += align4(size); + + return bytes; + } + public static int nativeReadInt(long nativePtr) { + var p = getInstance(nativePtr); + + p.ensureDataAvailable(Integer.BYTES); + + var ret = (((p.mBuffer[p.mPos++] & 0xff) << 24) + | ((p.mBuffer[p.mPos++] & 0xff) << 16) + | ((p.mBuffer[p.mPos++] & 0xff) << 8) + | ((p.mBuffer[p.mPos++] & 0xff) << 0)); + + return ret; + } + public static long nativeReadLong(long nativePtr) { + return (((long) nativeReadInt(nativePtr)) << 32) + | (((long) nativeReadInt(nativePtr)) & 0xffff_ffffL); + } + + public static float nativeReadFloat(long nativePtr) { + return Float.intBitsToFloat(nativeReadInt(nativePtr)); + } + + public static double nativeReadDouble(long nativePtr) { + return Double.longBitsToDouble(nativeReadLong(nativePtr)); + } + + public static String nativeReadString8(long nativePtr) { + final var bytes = nativeReadBlob(nativePtr); + if (bytes == null) { + return null; + } + return new String(bytes, StandardCharsets.UTF_8); + } + public static String nativeReadString16(long nativePtr) { + return nativeReadString8(nativePtr); + } + public static IBinder nativeReadStrongBinder(long nativePtr) { + throw new RuntimeException("Not implemented yet"); + } + public static FileDescriptor nativeReadFileDescriptor(long nativePtr) { + throw new RuntimeException("Not implemented yet"); + } + + public static byte[] nativeMarshall(long nativePtr) { + throw new RuntimeException("Not implemented yet"); + } + public static void nativeUnmarshall( + long nativePtr, byte[] data, int offset, int length) { + throw new RuntimeException("Not implemented yet"); + } + public static int nativeCompareData(long thisNativePtr, long otherNativePtr) { + throw new RuntimeException("Not implemented yet"); + } + public static boolean nativeCompareDataInRange( + long ptrA, int offsetA, long ptrB, int offsetB, int length) { + throw new RuntimeException("Not implemented yet"); + } + public static void nativeAppendFrom( + long thisNativePtr, long otherNativePtr, int srcOffset, int length) { + var dst = getInstance(thisNativePtr); + var src = getInstance(otherNativePtr); + + dst.ensureMoreCapacity(length); + + System.arraycopy(src.mBuffer, srcOffset, dst.mBuffer, dst.mPos, length); + dst.mPos += length; // TODO: 4 byte align? + dst.updateSize(); + + // TODO: Update the other's position? + } + + public static boolean nativeHasFileDescriptors(long nativePtr) { + // Assume false for now, because we don't support writing FDs yet. + return false; + } + public static boolean nativeHasFileDescriptorsInRange( + long nativePtr, int offset, int length) { + // Assume false for now, because we don't support writing FDs yet. + return false; + } + public static void nativeWriteInterfaceToken(long nativePtr, String interfaceName) { + throw new RuntimeException("Not implemented yet"); + } + public static void nativeEnforceInterface(long nativePtr, String interfaceName) { + throw new RuntimeException("Not implemented yet"); + } + + public static boolean nativeReplaceCallingWorkSourceUid( + long nativePtr, int workSourceUid) { + throw new RuntimeException("Not implemented yet"); + } + public static int nativeReadCallingWorkSourceUid(long nativePtr) { + throw new RuntimeException("Not implemented yet"); + } + + public static long nativeGetOpenAshmemSize(long nativePtr) { + throw new RuntimeException("Not implemented yet"); + } + public static long getGlobalAllocSize() { + throw new RuntimeException("Not implemented yet"); + } + public static long getGlobalAllocCount() { + throw new RuntimeException("Not implemented yet"); + } +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/SystemProperties_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/SystemProperties_host.java new file mode 100644 index 000000000000..1ec1d5f307e1 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/SystemProperties_host.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.nativesubstitution; + +public class SystemProperties_host { + public static String native_get(String key, String def) { + throw new RuntimeException("Not implemented yet"); + } + public static int native_get_int(String key, int def) { + throw new RuntimeException("Not implemented yet"); + } + public static long native_get_long(String key, long def) { + throw new RuntimeException("Not implemented yet"); + } + public static boolean native_get_boolean(String key, boolean def) { + throw new RuntimeException("Not implemented yet"); + } + + public static long native_find(String name) { + throw new RuntimeException("Not implemented yet"); + } + public static String native_get(long handle) { + throw new RuntimeException("Not implemented yet"); + } + public static int native_get_int(long handle, int def) { + throw new RuntimeException("Not implemented yet"); + } + public static long native_get_long(long handle, long def) { + throw new RuntimeException("Not implemented yet"); + } + public static boolean native_get_boolean(long handle, boolean def) { + throw new RuntimeException("Not implemented yet"); + } + public static void native_set(String key, String def) { + throw new RuntimeException("Not implemented yet"); + } + public static void native_add_change_callback() { + throw new RuntimeException("Not implemented yet"); + } + public static void native_report_sysprop_change() { + throw new RuntimeException("Not implemented yet"); + } +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/runtimehelper/ClassLoadHook.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/runtimehelper/ClassLoadHook.java new file mode 100644 index 000000000000..4c2d3c404b76 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/runtimehelper/ClassLoadHook.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.runtimehelper; + +import com.android.hoststubgen.hosthelper.HostTestException; + +import java.io.File; +import java.io.PrintStream; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Standard class to handle class load hook. + * + * We use this to initialize the environment necessary for some classes. (e.g. load native libs.) + */ +public class ClassLoadHook { + private static PrintStream out = System.out; + + /** + * If true, we won't load `libandroid_runtime` + * + * <p>Looks like there's some complexity in running a host test with JNI with `atest`, + * so we need a way to remove the dependency. + */ + private static final boolean SKIP_LOADING_LIBANDROID = "1".equals(System.getenv( + "HOSTTEST_SKIP_LOADING_LIBANDROID")); + + public static final String CORE_NATIVE_CLASSES = "core_native_classes"; + public static final String ICU_DATA_PATH = "icu.data.path"; + public static final String KEYBOARD_PATHS = "keyboard_paths"; + public static final String GRAPHICS_NATIVE_CLASSES = "graphics_native_classes"; + + public static final String VALUE_N_A = "**n/a**"; + public static final String LIBANDROID_RUNTIME_NAME = "libandroid_runtime"; + + private static String sInitialDir = new File("").getAbsolutePath(); + + static { + log("Initialized. Current dir=" + sInitialDir); + } + + private ClassLoadHook() { + } + + /** + * Called when classes with + * {@code @HostSideTestClassLoadHook("com.android.hoststubgen.runtimehelper.ClassLoadHook.onClassLoaded") } + * are loaded. + */ + public static void onClassLoaded(Class<?> clazz) { + System.out.println("Framework class loaded: " + clazz.getCanonicalName()); + + if (android.util.Log.class == clazz) { + loadFrameworkNativeCode(); + } + } + + private static void log(String message) { + out.println("ClassLoadHook: " + message); + } + + private static void log(String fmt, Object... args) { + log(String.format(fmt, args)); + } + + private static void ensurePropertyNotSet(String key) { + if (System.getProperty(key) != null) { + throw new HostTestException("System property \"" + key + "\" is set unexpectedly"); + } + } + + private static void setProperty(String key, String value) { + System.setProperty(key, value); + log("Property set: %s=\"%s\"", key, value); + } + + private static void dumpSystemProperties() { + for (var prop : System.getProperties().entrySet()) { + log(" %s=\"%s\"", prop.getKey(), prop.getValue()); + } + } + + private static void loadJniLibrary(String name) { + final String path = sInitialDir + "/lib64/" + name + ".so"; + System.out.println("Loading " + path + " ..."); + System.load(path); + System.out.println("Done loading " + path); + } + + private static boolean sLoadFrameworkNativeCodeCalled = false; + + /** + * Load `libandroid_runtime` if needed. + */ + private static void loadFrameworkNativeCode() { + // This is called from class-initializers, so no synchronization is needed. + if (sLoadFrameworkNativeCodeCalled) { + // This method has already been called before.s + return; + } + sLoadFrameworkNativeCodeCalled = true; + + // libandroid_runtime uses Java's system properties to decide what JNI methods to set up. + // Set up these properties for host-side tests. + + if ("1".equals(System.getenv("HOSTTEST_DUMP_PROPERTIES"))) { + log("Java system properties:"); + dumpSystemProperties(); + } + + if (SKIP_LOADING_LIBANDROID) { + log("Skip loading " + LIBANDROID_RUNTIME_NAME); + } + + // Make sure these properties are not set. + ensurePropertyNotSet(CORE_NATIVE_CLASSES); + ensurePropertyNotSet(ICU_DATA_PATH); + ensurePropertyNotSet(KEYBOARD_PATHS); + ensurePropertyNotSet(GRAPHICS_NATIVE_CLASSES); + + // Tell libandroid what JNI to use. + final var jniClasses = getCoreNativeClassesToUse(); + if (jniClasses.isEmpty()) { + log("No classes require JNI methods, skip loading " + LIBANDROID_RUNTIME_NAME); + return; + } + setProperty(CORE_NATIVE_CLASSES, jniClasses); + setProperty(GRAPHICS_NATIVE_CLASSES, ""); + setProperty(ICU_DATA_PATH, VALUE_N_A); + setProperty(KEYBOARD_PATHS, VALUE_N_A); + + loadJniLibrary(LIBANDROID_RUNTIME_NAME); + } + + /** + * @return if a given method is a native method or not. + */ + private static boolean isNativeMethod(Class<?> clazz, String methodName, Class<?>... argTypes) { + try { + final var method = clazz.getMethod(methodName, argTypes); + return Modifier.isNative(method.getModifiers()); + } catch (NoSuchMethodException e) { + throw new HostTestException(String.format( + "Class %s doesn't have method %s with args %s", + clazz.getCanonicalName(), + methodName, + Arrays.toString(argTypes)), e); + } + } + + /** + * Create a list of classes as comma-separated that require JNI methods to be set up. + * + * <p>This list is used by frameworks/base/core/jni/LayoutlibLoader.cpp to decide + * what JNI methods to set up. + */ + private static String getCoreNativeClassesToUse() { + final var coreNativeClassesToLoad = new ArrayList<String>(); + + if (isNativeMethod(android.util.Log.class, "isLoggable", + String.class, int.class)) { + coreNativeClassesToLoad.add("android.util.Log"); + } + + return String.join(",", coreNativeClassesToLoad); + } +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/dalvik/system/VMRuntime.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/dalvik/system/VMRuntime.java new file mode 100644 index 000000000000..7d2b00d9420d --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/dalvik/system/VMRuntime.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2023 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. + */ +package dalvik.system; + +// [ravenwood] It's in libart, so until we get ART to work, we need to use a fake. +// The original is here: +// $ANDROID_BUILD_TOP/libcore/libart/src/main/java/dalvik/system/VMRuntime.java + +import java.lang.reflect.Array; + +public class VMRuntime { + private static final VMRuntime THE_ONE = new VMRuntime(); + + private VMRuntime() { + } + + public static VMRuntime getRuntime() { + return THE_ONE; + } + + public boolean is64Bit() { + return true; + } + + public static boolean is64BitAbi(String abi) { + return true; + } + + public Object newUnpaddedArray(Class<?> componentType, int minLength) { + return Array.newInstance(componentType, minLength); + } +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/EmptyArray.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/EmptyArray.java new file mode 100644 index 000000000000..a1ae35a88656 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/EmptyArray.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2023 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. + */ +package libcore.util; + +import java.lang.annotation.Annotation; + +// [ravenwood] Copied from libcore. TODO: Figure out what to do with libcore. +public class EmptyArray { + private EmptyArray() {} + + public static final boolean[] BOOLEAN = new boolean[0]; + + public static final byte[] BYTE = new byte[0]; + + public static final char[] CHAR = new char[0]; + + public static final double[] DOUBLE = new double[0]; + + public static final float[] FLOAT = new float[0]; + + public static final int[] INT = new int[0]; + + public static final long[] LONG = new long[0]; + + public static final Class<?>[] CLASS = new Class[0]; + + public static final Object[] OBJECT = new Object[0]; + + public static final String[] STRING = new String[0]; + + public static final Throwable[] THROWABLE = new Throwable[0]; + + public static final StackTraceElement[] STACK_TRACE_ELEMENT = new StackTraceElement[0]; + + public static final java.lang.reflect.Type[] TYPE = new java.lang.reflect.Type[0]; + + public static final java.lang.reflect.TypeVariable[] TYPE_VARIABLE = + new java.lang.reflect.TypeVariable[0]; + public static final Annotation[] ANNOTATION = new Annotation[0]; + +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/SneakyThrow.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/SneakyThrow.java new file mode 100644 index 000000000000..e142c46bc311 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/SneakyThrow.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2015 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. + */ + +package libcore.util; + +// [ravenwood] Copied from libcore. TODO: Figure out what to do with libcore. + +public class SneakyThrow { + + private SneakyThrow() { + } + + public static void sneakyThrow(Throwable t) { + SneakyThrow.<RuntimeException>sneakyThrow_(t); + } + + private static <T extends Throwable> void sneakyThrow_(Throwable t) throws T { + throw (T) t; + } +} diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedKeepClass.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedKeepClass.java new file mode 100644 index 000000000000..4c37579ac917 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedKeepClass.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.hosthelper; + +import static java.lang.annotation.ElementType.TYPE; + +import org.objectweb.asm.Type; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation added to all "stub" classes generated by HostStubGen. + */ +@Target({TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface HostStubGenProcessedKeepClass { + String CLASS_INTERNAL_NAME = Type.getInternalName(HostStubGenProcessedKeepClass.class); + String CLASS_DESCRIPTOR = "L" + CLASS_INTERNAL_NAME + ";"; +} diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedStubClass.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedStubClass.java new file mode 100644 index 000000000000..34e0030f548a --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedStubClass.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.hosthelper; + +import static java.lang.annotation.ElementType.TYPE; + +import org.objectweb.asm.Type; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation added to all "stub" classes generated by HostStubGen. + */ +@Target({TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface HostStubGenProcessedStubClass { + String CLASS_INTERNAL_NAME = Type.getInternalName(HostStubGenProcessedStubClass.class); + String CLASS_DESCRIPTOR = "L" + CLASS_INTERNAL_NAME + ";"; +} diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java new file mode 100644 index 000000000000..c54c2c111229 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.hosthelper; + +public class HostTestException extends RuntimeException { + public HostTestException(String message) { + super(message); + } + + public HostTestException(String message, Throwable inner) { + super(message, inner); + } +} diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java new file mode 100644 index 000000000000..29f7be008eef --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.hosthelper; + +import com.google.common.reflect.ClassPath; +import com.google.common.reflect.ClassPath.ClassInfo; + +import junit.framework.JUnit4TestAdapter; +import junit.framework.TestSuite; + +import java.util.regex.Pattern; + +/** + * A very simple Junit {@link TestSuite} builder that finds all classes that look like test classes. + * + * We use it to run ravenwood test jars from the command line. + */ +public class HostTestSuite { + private static final String CLASS_NAME_REGEX_ENV = "HOST_TEST_CLASS_NAME_REGEX"; + + /** + * Called by junit, and return all test-looking classes as a suite. + */ + public static TestSuite suite() { + TestSuite suite = new TestSuite(); + + final Pattern classNamePattern; + final var filterRegex = System.getenv(CLASS_NAME_REGEX_ENV); + if (filterRegex == null) { + classNamePattern = Pattern.compile(""); + } else { + classNamePattern = Pattern.compile(filterRegex); + } + try { + // We use guava to list all classes. + ClassPath cp = ClassPath.from(HostTestSuite.class.getClassLoader()); + + for (var classInfo : cp.getAllClasses()) { + Class<?> clazz = asTestClass(classInfo); + if (clazz != null) { + if (classNamePattern.matcher(clazz.getSimpleName()).find()) { + System.out.println("Test class found " + clazz.getName()); + } else { + System.out.println("Skipping test class (for $" + + CLASS_NAME_REGEX_ENV + "): " + clazz.getName()); + } + suite.addTest(new JUnit4TestAdapter(clazz)); + } + } + } catch (Exception e) { + throw new RuntimeException(e); + } + return suite; + } + + /** + * Decide whether a class looks like a test class or not, and if so, return it as a Class + * instance. + */ + private static Class<?> asTestClass(ClassInfo classInfo) { + try { + final Class<?> clazz = classInfo.load(); + + // Does it extend junit.framework.TestCase? + if (junit.framework.TestCase.class.isAssignableFrom(clazz)) { + // Ignore classes in JUnit itself, or the android(x) test lib. + if (classInfo.getName().startsWith("junit.") + || classInfo.getName().startsWith("org.junit.") + || classInfo.getName().startsWith("android.test.") + || classInfo.getName().startsWith("androidx.test.")) { + return null; // Ignore junit classes. + } + return clazz; + } + // Does it have any "@Test" method? + for (var method : clazz.getMethods()) { + for (var an : method.getAnnotations()) { + if (an.annotationType() == org.junit.Test.class) { + return clazz; + } + } + } + return null; + } catch (java.lang.NoClassDefFoundError e) { + // Ignore unloadable classes. + return null; + } + } +} diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java new file mode 100644 index 000000000000..f7719a6e55b2 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.hosthelper; + +import org.objectweb.asm.Type; + +import java.io.PrintStream; +import java.lang.StackWalker.Option; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.HashMap; + +import javax.annotation.concurrent.GuardedBy; + +/** + * Utilities used in the host side test environment. + */ +public class HostTestUtils { + private HostTestUtils() { + } + + public static final String CLASS_INTERNAL_NAME = Type.getInternalName(HostTestUtils.class); + + /** If true, we won't print method call log. */ + private static final boolean SKIP_METHOD_LOG = "1".equals(System.getenv( + "HOSTTEST_SKIP_METHOD_LOG")); + + /** If true, we won't perform non-stub method direct call check. */ + private static final boolean SKIP_NON_STUB_METHOD_CHECK = "1".equals(System.getenv( + "HOSTTEST_SKIP_NON_STUB_METHOD_CHECK")); + + + /** + * Method call log will be printed to it. + */ + public static PrintStream logPrintStream = System.out; + + /** + * Called from methods with FilterPolicy.Throw. + */ + public static void onThrowMethodCalled() { + // TODO: Maybe add call tracking? + throw new RuntimeException("This method is not supported on the host side"); + } + + /** + * Called from methods with FilterPolicy.Log. + */ + public static void logMethodCall( + String methodClass, + String methodName, + String methodDescriptor + ) { + if (SKIP_METHOD_LOG) { + return; + } + logPrintStream.println("# " + methodClass + "." + methodName + methodDescriptor); + } + + private static final StackWalker sStackWalker = + StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE); + + /** + * Return a {@link StackWalker} that supports {@link StackWalker#getCallerClass()}. + */ + public static StackWalker getStackWalker() { + return sStackWalker; + } + + /** + * Cache used by {@link #isClassAllowedToCallNonStubMethods}. + */ + @GuardedBy("sAllowedClasses") + private static final HashMap<Class, Boolean> sAllowedClasses = new HashMap(); + + /** + * Return true if a given class is allowed to access non-stub methods -- that is, if the class + * is in the hoststubgen generated JARs. (not in the test jar.) + */ + private static boolean isClassAllowedToCallNonStubMethods(Class<?> clazz) { + synchronized (sAllowedClasses) { + var cached = sAllowedClasses.get(clazz); + if (cached != null) { + return cached; + } + } + // All processed classes have this annotation. + var allowed = clazz.getAnnotation(HostStubGenProcessedKeepClass.class) != null; + + // Java classes should be able to access any methods. (via callbacks, etc.) + if (!allowed) { + if (clazz.getPackageName().startsWith("java.") + || clazz.getPackageName().startsWith("javax.")) { + allowed = true; + } + } + synchronized (sAllowedClasses) { + sAllowedClasses.put(clazz, allowed); + } + return allowed; + } + + /** + * Called when non-stub methods are called. We do a host-unsupported method direct call check + * in here. + */ + public static void onNonStubMethodCalled( + String methodClass, + String methodName, + String methodDescriptor, + Class<?> callerClass) { + if (SKIP_NON_STUB_METHOD_CHECK) { + return; + } + if (isClassAllowedToCallNonStubMethods(callerClass)) { + return; // Generated class is allowed to call framework class. + } + logPrintStream.println("! " + methodClass + "." + methodName + methodDescriptor + + " called by " + callerClass.getCanonicalName()); + } + + /** + * Called when any top level class (not nested classes) in the impl jar is loaded. + * + * When HostStubGen inject a class-load hook, it's always a call to this method, with the + * actual method name as the second argument. + * + * This method discovers the hook method with reflections and call it. + * + * TODO: Add a unit test. + */ + public static void onClassLoaded(Class<?> loadedClass, String callbackMethod) { + logPrintStream.println("! Class loaded: " + loadedClass.getCanonicalName() + + " calling hook " + callbackMethod); + + // Forward the call to callbackMethod. + final int lastPeriod = callbackMethod.lastIndexOf("."); + final String className = callbackMethod.substring(0, lastPeriod); + final String methodName = callbackMethod.substring(lastPeriod + 1); + + if (lastPeriod < 0 || className.isEmpty() || methodName.isEmpty()) { + throw new HostTestException(String.format( + "Unable to find class load hook: malformed method name \"%s\"", + callbackMethod)); + } + + Class<?> clazz = null; + try { + clazz = Class.forName(className); + } catch (Exception e) { + throw new HostTestException(String.format( + "Unable to find class load hook: Class %s not found", className), e); + } + if (!Modifier.isPublic(clazz.getModifiers())) { + throw new HostTestException(String.format( + "Unable to find class load hook: Class %s must be public", className)); + } + + Method method = null; + try { + method = clazz.getMethod(methodName, Class.class); + } catch (Exception e) { + throw new HostTestException(String.format( + "Unable to find class load hook: class %s doesn't have method %s" + + " (method must take exactly one parameter of type Class, and public static)", + className, + methodName), e); + } + if (!(Modifier.isPublic(method.getModifiers()) + && Modifier.isStatic(method.getModifiers()))) { + throw new HostTestException(String.format( + "Unable to find class load hook: Method %s in class %s must be public static", + methodName, className)); + } + try { + method.invoke(null, loadedClass); + } catch (Exception e) { + throw new HostTestException(String.format( + "Unable to invoke class load hook %s.%s", + className, + methodName), e); + } + } +} diff --git a/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt b/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt new file mode 100644 index 000000000000..828d2a3e01c6 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt @@ -0,0 +1,39 @@ +# File containing standard options to HostStubGen + +--debug + +# Uncomment below lines to enable each feature. +--enable-non-stub-method-check +# --no-non-stub-method-check + +# --enable-method-logging + + +# Standard annotations. +# Note, each line is a single argument, so we need newlines after each `--xxx-annotation`. +--stub-annotation + android.hosttest.annotation.HostSideTestStub + +--keep-annotation + android.hosttest.annotation.HostSideTestKeep + +--stub-class-annotation + android.hosttest.annotation.HostSideTestWholeClassStub + +--keep-class-annotation + android.hosttest.annotation.HostSideTestWholeClassKeep + +--throw-annotation + android.hosttest.annotation.HostSideTestThrow + +--remove-annotation + android.hosttest.annotation.HostSideTestRemove + +--substitute-annotation + android.hosttest.annotation.HostSideTestSubstitute + +--native-substitute-annotation + android.hosttest.annotation.HostSideTestNativeSubstitutionClass + +--class-load-hook-annotation + android.hosttest.annotation.HostSideTestClassLoadHook diff --git a/tools/hoststubgen/hoststubgen/jarjar-rules.txt b/tools/hoststubgen/hoststubgen/jarjar-rules.txt new file mode 100644 index 000000000000..4e61ba6f67b3 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/jarjar-rules.txt @@ -0,0 +1,2 @@ +# Rename guava +rule com.google.common.** com.android.hoststubgen.x.@0
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Exceptions.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Exceptions.kt new file mode 100644 index 000000000000..207ba52685f8 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Exceptions.kt @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen + +/** + * We will not print the stack trace for exceptions implementing it. + */ +interface UserErrorException + +/** + * Exceptions about parsing class files. + */ +class ClassParseException(message: String) : Exception(message) + +/** + * Use it for internal exception that really shouldn't happen. + */ +class HostStubGenInternalException(message: String) : Exception(message) + +/** + * Exceptions about the content in a jar file. + */ +class InvalidJarFileException(message: String) : Exception(message), UserErrorException + +/** + * Exceptions missing classes, fields, methods, etc. + */ +class UnknownApiException(message: String) : Exception(message), UserErrorException + +/** + * Exceptions related to invalid annotations -- e.g. more than one visibility annotation + * on a single API. + */ +class InvalidAnnotationException(message: String) : Exception(message), UserErrorException + diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt new file mode 100644 index 000000000000..8db4b6961376 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt @@ -0,0 +1,402 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen + +import com.android.hoststubgen.asm.ClassNodes +import com.android.hoststubgen.filters.AnnotationBasedFilter +import com.android.hoststubgen.filters.ClassWidePolicyPropagatingFilter +import com.android.hoststubgen.filters.ConstantFilter +import com.android.hoststubgen.filters.FilterPolicy +import com.android.hoststubgen.filters.ImplicitOutputFilter +import com.android.hoststubgen.filters.KeepAllClassesFilter +import com.android.hoststubgen.filters.OutputFilter +import com.android.hoststubgen.filters.StubIntersectingFilter +import com.android.hoststubgen.filters.createFilterFromTextPolicyFile +import com.android.hoststubgen.filters.printAsTextPolicy +import com.android.hoststubgen.visitors.BaseAdapter +import org.objectweb.asm.ClassReader +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.tree.ClassNode +import org.objectweb.asm.util.CheckClassAdapter +import java.io.BufferedInputStream +import java.io.FileOutputStream +import java.io.InputStream +import java.io.OutputStream +import java.io.PrintWriter +import java.util.zip.ZipEntry +import java.util.zip.ZipFile +import java.util.zip.ZipOutputStream + +/** + * Actual main class. + */ +class HostStubGen(val options: HostStubGenOptions) { + fun run() { + val errors = HostStubGenErrors() + + // Load all classes. + val allClasses = loadClassStructures(options.inJar) + + // Dump the classes, if specified. + options.inputJarDumpFile?.let { + PrintWriter(it).use { pw -> allClasses.dump(pw) } + log.i("Dump file created at $it") + } + + options.inputJarAsKeepAllFile?.let { + PrintWriter(it).use { + pw -> allClasses.forEach { + classNode -> printAsTextPolicy(pw, classNode) + } + } + log.i("Dump file created at $it") + } + + // Build the filters. + val filter = buildFilter(errors, allClasses, options) + + // Transform the jar. + convert( + options.inJar, + options.outStubJar, + options.outImplJar, + filter, + options.enableClassChecker, + allClasses, + errors, + ) + } + + /** + * Load all the classes, without code. + */ + private fun loadClassStructures(inJar: String): ClassNodes { + log.i("Reading class structure from $inJar ...") + val start = System.currentTimeMillis() + + val allClasses = ClassNodes() + + log.withIndent { + ZipFile(inJar).use { inZip -> + val inEntries = inZip.entries() + + while (inEntries.hasMoreElements()) { + val entry = inEntries.nextElement() + + BufferedInputStream(inZip.getInputStream(entry)).use { bis -> + if (entry.name.endsWith(".class")) { + val cr = ClassReader(bis) + val cn = ClassNode() + cr.accept(cn, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG + or ClassReader.SKIP_FRAMES) + if (!allClasses.addClass(cn)) { + log.w("Duplicate class found: ${cn.name}") + } + } else if (entry.name.endsWith(".dex")) { + // Seems like it's an ART jar file. We can't process it. + // It's a fatal error. + throw InvalidJarFileException( + "$inJar is not a desktop jar file. It contains a *.dex file.") + } else { + // Unknown file type. Skip. + while (bis.available() > 0) { + bis.skip((1024 * 1024).toLong()) + } + } + } + } + } + } + if (allClasses.size == 0) { + log.w("$inJar contains no *.class files.") + } + + val end = System.currentTimeMillis() + log.v("Done reading class structure in %.1f second(s).", (end - start) / 1000.0) + return allClasses + } + + /** + * Build the filter, which decides what classes/methods/fields should be put in stub or impl + * jars, and "how". (e.g. with substitution?) + */ + private fun buildFilter( + errors: HostStubGenErrors, + allClasses: ClassNodes, + options: HostStubGenOptions, + ): OutputFilter { + // We build a "chain" of multiple filters here. + // + // The filters are build in from "inside", meaning the first filter created here is + // the last filter used, so it has the least precedence. + // + // So, for example, the "remove" annotation, which is handled by AnnotationBasedFilter, + // can override a class-wide annotation, which is handled by + // ClassWidePolicyPropagatingFilter, and any annotations can be overridden by the + // text-file based filter, which is handled by parseTextFilterPolicyFile. + + // The first filter is for the default policy from the command line options. + var filter: OutputFilter = ConstantFilter(options.defaultPolicy, "default-by-options") + + // Next, we need a filter that resolves "class-wide" policies. + // This is used when a member (methods, fields, nested classes) don't get any polices + // from upper filters. e.g. when a method has no annotations, then this filter will apply + // the class-wide policy, if any. (if not, we'll fall back to the above filter.) + val classWidePropagator = ClassWidePolicyPropagatingFilter(filter) + + // Next, Java annotation based filter. + filter = AnnotationBasedFilter( + errors, + allClasses, + options.stubAnnotations, + options.keepAnnotations, + options.stubClassAnnotations, + options.keepClassAnnotations, + options.throwAnnotations, + options.removeAnnotations, + options.substituteAnnotations, + options.nativeSubstituteAnnotations, + options.classLoadHookAnnotations, + classWidePropagator + ) + + // Next, "text based" filter, which allows to override polices without touching + // the target code. + options.policyOverrideFile?.let { + filter = createFilterFromTextPolicyFile(it, allClasses, filter) + } + + // If `--intersect-stub-jar` is provided, load from these jar files too. + // We use this to restrict stub APIs to public/system/test APIs, + // by intersecting with a stub jar file created by metalava. + if (options.intersectStubJars.size > 0) { + val intersectingJars = loadIntersectingJars(options.intersectStubJars) + + filter = StubIntersectingFilter(errors, intersectingJars, filter) + } + + // Apply the implicit filter. + filter = ImplicitOutputFilter(errors, allClasses, filter) + + // Optionally keep all classes. + if (options.keepAllClasses) { + filter = KeepAllClassesFilter(filter) + } + + return filter + } + + /** + * Load jar files specified with "--intersect-stub-jar". + */ + private fun loadIntersectingJars(filenames: Set<String>): Map<String, ClassNodes> { + val intersectingJars = mutableMapOf<String, ClassNodes>() + + filenames.forEach { filename -> + intersectingJars[filename] = loadClassStructures(filename) + } + return intersectingJars + } + + /** + * Convert a JAR file into "stub" and "impl" JAR files. + */ + private fun convert( + inJar: String, + outStubJar: String, + outImplJar: String, + filter: OutputFilter, + enableChecker: Boolean, + classes: ClassNodes, + errors: HostStubGenErrors, + ) { + log.i("Converting %s into [stub: %s, impl: %s] ...", inJar, outStubJar, outImplJar) + log.i("Checker is %s", if (enableChecker) "enabled" else "disabled") + + val start = System.currentTimeMillis() + + log.withIndent { + // Open the input jar file and process each entry. + ZipFile(inJar).use { inZip -> + ZipOutputStream(FileOutputStream(outStubJar)).use { stubOutStream -> + ZipOutputStream(FileOutputStream(outImplJar)).use { implOutStream -> + val inEntries = inZip.entries() + while (inEntries.hasMoreElements()) { + val entry = inEntries.nextElement() + convertSingleEntry(inZip, entry, stubOutStream, implOutStream, + filter, enableChecker, classes, errors) + } + log.i("Converted all entries.") + } + } + log.i("Created stub: $outStubJar") + log.i("Created impl: $outImplJar") + } + } + val end = System.currentTimeMillis() + log.v("Done transforming the jar in %.1f second(s).", (end - start) / 1000.0) + } + + /** + * Convert a single ZIP entry, which may or may not be a class file. + */ + private fun convertSingleEntry( + inZip: ZipFile, + entry: ZipEntry, + stubOutStream: ZipOutputStream, + implOutStream: ZipOutputStream, + filter: OutputFilter, + enableChecker: Boolean, + classes: ClassNodes, + errors: HostStubGenErrors, + ) { + log.d("Entry: %s", entry.name) + log.withIndent { + val name = entry.name + + // Just ignore all the directories. (TODO: make sure it's okay) + if (name.endsWith("/")) { + return + } + + // If it's a class, convert it. + if (name.endsWith(".class")) { + processSingleClass(inZip, entry, stubOutStream, implOutStream, filter, + enableChecker, classes, errors) + return + } + + // Handle other file types... + + // - *.uau seems to contain hidden API information. + // - *_compat_config.xml is also about compat-framework. + if (name.endsWith(".uau") || + name.endsWith("_compat_config.xml")) { + log.d("Not needed: %s", entry.name) + return + } + + // Unknown type, we just copy it to both output zip files. + // TODO: We probably shouldn't do it for stub jar? + log.v("Copying: %s", entry.name) + copyZipEntry(inZip, entry, stubOutStream) + copyZipEntry(inZip, entry, implOutStream) + } + } + + /** + * Copy a single ZIP entry to the output. + */ + private fun copyZipEntry( + inZip: ZipFile, + entry: ZipEntry, + out: ZipOutputStream, + ) { + BufferedInputStream(inZip.getInputStream(entry)).use { bis -> + // Copy unknown entries as is to the impl out. (but not to the stub out.) + val outEntry = ZipEntry(entry.name) + out.putNextEntry(outEntry) + while (bis.available() > 0) { + out.write(bis.read()) + } + out.closeEntry() + } + } + + /** + * Convert a single class to "stub" and "impl". + */ + private fun processSingleClass( + inZip: ZipFile, + entry: ZipEntry, + stubOutStream: ZipOutputStream, + implOutStream: ZipOutputStream, + filter: OutputFilter, + enableChecker: Boolean, + classes: ClassNodes, + errors: HostStubGenErrors, + ) { + val className = entry.name.replaceFirst("\\.class$".toRegex(), "") + val classPolicy = filter.getPolicyForClass(className) + if (classPolicy.policy == FilterPolicy.Remove) { + log.d("Removing class: %s %s", className, classPolicy) + return + } + // Generate stub first. + if (classPolicy.policy.needsInStub) { + log.v("Creating stub class: %s Policy: %s", className, classPolicy) + log.withIndent { + BufferedInputStream(inZip.getInputStream(entry)).use { bis -> + val newEntry = ZipEntry(entry.name) + stubOutStream.putNextEntry(newEntry) + convertClass(/*forImpl=*/false, bis, stubOutStream, filter, enableChecker, + classes, errors) + stubOutStream.closeEntry() + } + } + } + log.v("Creating impl class: %s Policy: %s", className, classPolicy) + if (classPolicy.policy.needsInImpl) { + log.withIndent { + BufferedInputStream(inZip.getInputStream(entry)).use { bis -> + val newEntry = ZipEntry(entry.name) + implOutStream.putNextEntry(newEntry) + convertClass(/*forImpl=*/true, bis, implOutStream, filter, enableChecker, + classes, errors) + implOutStream.closeEntry() + } + } + } + } + + /** + * Convert a single class to either "stub" or "impl". + */ + private fun convertClass( + forImpl: Boolean, + input: InputStream, + out: OutputStream, + filter: OutputFilter, + enableChecker: Boolean, + classes: ClassNodes, + errors: HostStubGenErrors, + ) { + val cr = ClassReader(input) + + // COMPUTE_FRAMES wouldn't be happy if code uses + val flags = ClassWriter.COMPUTE_MAXS // or ClassWriter.COMPUTE_FRAMES + val cw = ClassWriter(flags) + + // Connect to the class writer + var outVisitor: ClassVisitor = cw + if (enableChecker) { + outVisitor = CheckClassAdapter(outVisitor) + } + val visitorOptions = BaseAdapter.Options( + enablePreTrace = options.enablePreTrace, + enablePostTrace = options.enablePostTrace, + enableMethodLogging = options.enablePreTrace, + enableNonStubMethodCallDetection = options.enableNonStubMethodCallDetection, + errors = errors, + ) + outVisitor = BaseAdapter.getVisitor(classes, outVisitor, filter, forImpl, visitorOptions) + + cr.accept(outVisitor, ClassReader.EXPAND_FRAMES) + val data = cw.toByteArray() + out.write(data) + } +} diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt new file mode 100644 index 000000000000..9df04892ddbd --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen + +class HostStubGenErrors { + fun onErrorFound(message: String) { + // For now, we just throw as soon as any error is found, but eventually we should keep + // all errors and print them at the end. + throw RuntimeException(message) + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt new file mode 100644 index 000000000000..5e71a3690700 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen + +import java.io.OutputStream +import java.io.PrintStream + +val log: HostStubGenLogger = HostStubGenLogger() + +/** Logging level */ +enum class LogLevel { + None, + Error, + Warn, + Info, + Verbose, + Debug, +} + +/** Simple logging class. */ +class HostStubGenLogger( + private var out: PrintStream = System.out!!, + var level: LogLevel = LogLevel.Info, +) { + companion object { + private val sNullPrintStream: PrintStream = PrintStream(OutputStream.nullOutputStream()) + } + + private var indentLevel: Int = 0 + get() = field + set(value) { + field = value + indent = " ".repeat(value) + } + private var indent: String = "" + + fun indent() { + indentLevel++ + } + + fun unindent() { + if (indentLevel <= 0) { + throw IllegalStateException("Unbalanced unindent() call.") + } + indentLevel-- + } + + inline fun <T> withIndent(block: () -> T): T { + try { + indent() + return block() + } finally { + unindent() + } + } + + fun isEnabled(level: LogLevel): Boolean { + return level.ordinal <= this.level.ordinal + } + + private fun println(message: String) { + out.print(indent) + out.println(message) + } + + /** Log an error. */ + fun e(message: String) { + if (level.ordinal < LogLevel.Error.ordinal) { + return + } + println(message) + } + + /** Log an error. */ + fun e(format: String, vararg args: Any?) { + if (level.ordinal < LogLevel.Error.ordinal) { + return + } + e(String.format(format, *args)) + } + + /** Log a warning. */ + fun w(message: String) { + if (level.ordinal < LogLevel.Warn.ordinal) { + return + } + println(message) + } + + /** Log a warning. */ + fun w(format: String, vararg args: Any?) { + if (level.ordinal < LogLevel.Warn.ordinal) { + return + } + w(String.format(format, *args)) + } + + /** Log an info message. */ + fun i(message: String) { + if (level.ordinal < LogLevel.Info.ordinal) { + return + } + println(message) + } + + /** Log a debug message. */ + fun i(format: String, vararg args: Any?) { + if (level.ordinal < LogLevel.Warn.ordinal) { + return + } + i(String.format(format, *args)) + } + + /** Log a verbose message. */ + fun v(message: String) { + if (level.ordinal < LogLevel.Verbose.ordinal) { + return + } + println(message) + } + + /** Log a verbose message. */ + fun v(format: String, vararg args: Any?) { + if (level.ordinal < LogLevel.Verbose.ordinal) { + return + } + v(String.format(format, *args)) + } + + /** Log a debug message. */ + fun d(message: String) { + if (level.ordinal < LogLevel.Debug.ordinal) { + return + } + println(message) + } + + /** Log a debug message. */ + fun d(format: String, vararg args: Any?) { + if (level.ordinal < LogLevel.Warn.ordinal) { + return + } + d(String.format(format, *args)) + } + + inline fun forVerbose(block: () -> Unit) { + if (isEnabled(LogLevel.Verbose)) { + block() + } + } + + inline fun forDebug(block: () -> Unit) { + if (isEnabled(LogLevel.Debug)) { + block() + } + } + + /** Return a stream for error. */ + fun getErrorPrintStream(): PrintStream { + if (level.ordinal < LogLevel.Error.ordinal) { + return sNullPrintStream + } + + // TODO Apply indent + return PrintStream(out) + } + + /** Return a stream for verbose messages. */ + fun getVerbosePrintStream(): PrintStream { + if (level.ordinal < LogLevel.Verbose.ordinal) { + return sNullPrintStream + } + // TODO Apply indent + return PrintStream(out) + } + + /** Return a stream for debug messages. */ + fun getInfoPrintStream(): PrintStream { + if (level.ordinal < LogLevel.Info.ordinal) { + return sNullPrintStream + } + // TODO Apply indent + return PrintStream(out) + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt new file mode 100644 index 000000000000..9a54ecffc8c2 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen + +import com.android.hoststubgen.filters.FilterPolicy +import java.io.BufferedReader +import java.io.File +import java.io.FileReader + +/** + * Options that can be set from command line arguments. + */ +class HostStubGenOptions( + /** Input jar file*/ + var inJar: String = "", + + /** Output stub jar file */ + var outStubJar: String = "", + + /** Output implementation jar file */ + var outImplJar: String = "", + + var inputJarDumpFile: String? = null, + + var inputJarAsKeepAllFile: String? = null, + + var stubAnnotations: MutableSet<String> = mutableSetOf(), + var keepAnnotations: MutableSet<String> = mutableSetOf(), + var throwAnnotations: MutableSet<String> = mutableSetOf(), + var removeAnnotations: MutableSet<String> = mutableSetOf(), + var stubClassAnnotations: MutableSet<String> = mutableSetOf(), + var keepClassAnnotations: MutableSet<String> = mutableSetOf(), + + var substituteAnnotations: MutableSet<String> = mutableSetOf(), + var nativeSubstituteAnnotations: MutableSet<String> = mutableSetOf(), + var classLoadHookAnnotations: MutableSet<String> = mutableSetOf(), + + var intersectStubJars: MutableSet<String> = mutableSetOf(), + + var policyOverrideFile: String? = null, + + var defaultPolicy: FilterPolicy = FilterPolicy.Remove, + var keepAllClasses: Boolean = false, + + var logLevel: LogLevel = LogLevel.Info, + + var cleanUpOnError: Boolean = false, + + var enableClassChecker: Boolean = false, + var enablePreTrace: Boolean = false, + var enablePostTrace: Boolean = false, + + var enableMethodLogging: Boolean = false, + + var enableNonStubMethodCallDetection: Boolean = true, +) { + companion object { + + private fun String.ensureFileExists(): String { + if (!File(this).exists()) { + throw InputFileNotFoundException(this) + } + return this + } + + fun parseArgs(args: Array<String>): HostStubGenOptions { + val ret = HostStubGenOptions() + + val ai = ArgIterator(expandAtFiles(args)) + + var allAnnotations = mutableSetOf<String>() + + fun ensureUniqueAnnotation(name: String): String { + if (!allAnnotations.add(name)) { + throw DuplicateAnnotationException(ai.current) + } + return name + } + + while (true) { + val arg = ai.nextArgOptional() + if (arg == null) { + break + } + + when (arg) { + // TODO: Write help + "-h", "--h" -> TODO("Help is not implemented yet") + + "-v", "--verbose" -> ret.logLevel = LogLevel.Verbose + "-d", "--debug" -> ret.logLevel = LogLevel.Debug + "-q", "--quiet" -> ret.logLevel = LogLevel.None + + "--in-jar" -> ret.inJar = ai.nextArgRequired(arg).ensureFileExists() + "--out-stub-jar" -> ret.outStubJar = ai.nextArgRequired(arg) + "--out-impl-jar" -> ret.outImplJar = ai.nextArgRequired(arg) + + "--policy-override-file" -> + ret.policyOverrideFile = ai.nextArgRequired(arg).ensureFileExists() + + "--clean-up-on-error" -> ret.cleanUpOnError = true + "--no-clean-up-on-error" -> ret.cleanUpOnError = false + + "--default-remove" -> ret.defaultPolicy = FilterPolicy.Remove + "--default-throw" -> ret.defaultPolicy = FilterPolicy.Throw + "--default-keep" -> ret.defaultPolicy = FilterPolicy.Keep + "--default-stub" -> ret.defaultPolicy = FilterPolicy.Stub + + "--keep-all-classes" -> ret.keepAllClasses = true + "--no-keep-all-classes" -> ret.keepAllClasses = false + + "--stub-annotation" -> + ret.stubAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg)) + + "--keep-annotation" -> + ret.keepAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg)) + + "--stub-class-annotation" -> + ret.stubClassAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg)) + + "--keep-class-annotation" -> + ret.keepClassAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg)) + + "--throw-annotation" -> + ret.throwAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg)) + + "--remove-annotation" -> + ret.removeAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg)) + + "--substitute-annotation" -> + ret.substituteAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg)) + + "--native-substitute-annotation" -> + ret.nativeSubstituteAnnotations += + ensureUniqueAnnotation(ai.nextArgRequired(arg)) + + "--class-load-hook-annotation" -> + ret.classLoadHookAnnotations += + ensureUniqueAnnotation(ai.nextArgRequired(arg)) + + "--intersect-stub-jar" -> + ret.intersectStubJars += ai.nextArgRequired(arg).ensureFileExists() + + "--gen-keep-all-file" -> + ret.inputJarAsKeepAllFile = ai.nextArgRequired(arg) + + // Following options are for debugging. + "--enable-class-checker" -> ret.enableClassChecker = true + "--no-class-checker" -> ret.enableClassChecker = false + + "--enable-pre-trace" -> ret.enablePreTrace = true + "--no-pre-trace" -> ret.enablePreTrace = false + + "--enable-post-trace" -> ret.enablePostTrace = true + "--no-post-trace" -> ret.enablePostTrace = false + + "--enable-method-logging" -> ret.enableMethodLogging = true + "--no-method-logging" -> ret.enableMethodLogging = false + + "--enable-non-stub-method-check" -> ret.enableNonStubMethodCallDetection = true + "--no-non-stub-method-check" -> ret.enableNonStubMethodCallDetection = false + + "--gen-input-dump-file" -> ret.inputJarDumpFile = ai.nextArgRequired(arg) + + else -> throw ArgumentsException("Unknown option: $arg") + } + } + if (ret.inJar.isEmpty()) { + throw ArgumentsException("Required option missing: --in-jar") + } + if (ret.outStubJar.isEmpty()) { + throw ArgumentsException("Required option missing: --out-stub-jar") + } + if (ret.outImplJar.isEmpty()) { + throw ArgumentsException("Required option missing: --out-impl-jar") + } + + return ret + } + + /** + * Scan the arguments, and if any of them starts with an `@`, then load from the file + * and use its content as arguments. + * + * In this file, each line is treated as a single argument. + * + * The file can contain '#' as comments. + */ + private fun expandAtFiles(args: Array<String>): List<String> { + val ret = mutableListOf<String>() + + args.forEach { arg -> + if (!arg.startsWith('@')) { + ret += arg + return@forEach + } + // Read from the file, and add each line to the result. + val filename = arg.substring(1).ensureFileExists() + + log.v("Expanding options file $filename") + + BufferedReader(FileReader(filename)).use { reader -> + while (true) { + var line = reader.readLine() + if (line == null) { + break // EOF + } + + line = normalizeTextLine(line) + if (line.isNotEmpty()) { + ret += line + } + } + } + } + return ret + } + } + + open class ArgumentsException(message: String?) : Exception(message), UserErrorException + + /** Thrown when the same annotation is used with different annotation arguments. */ + class DuplicateAnnotationException(annotationName: String?) : + ArgumentsException("Duplicate annotation specified: '$annotationName'") + + /** Thrown when an input file does not exist. */ + class InputFileNotFoundException(filename: String) : + ArgumentsException("File '$filename' not found") + + private class ArgIterator( + private val args: List<String>, + private var currentIndex: Int = -1 + ) { + val current: String + get() = args.get(currentIndex) + + /** + * Get the next argument, or [null] if there's no more arguments. + */ + fun nextArgOptional(): String? { + if ((currentIndex + 1) >= args.size) { + return null + } + return args.get(++currentIndex) + } + + /** + * Get the next argument, or throw if + */ + fun nextArgRequired(argName: String): String { + nextArgOptional().let { + if (it == null) { + throw ArgumentsException("Missing parameter for option $argName") + } + if (it.isEmpty()) { + throw ArgumentsException("Parameter can't be empty for option $argName") + } + return it + } + } + } + + override fun toString(): String { + return """ + HostStubGenOptions{ + inJar='$inJar', + outStubJar='$outStubJar', + outImplJar='$outImplJar', + inputJarDumpFile=$inputJarDumpFile, + inputJarAsKeepAllFile=$inputJarAsKeepAllFile, + stubAnnotations=$stubAnnotations, + keepAnnotations=$keepAnnotations, + throwAnnotations=$throwAnnotations, + removeAnnotations=$removeAnnotations, + stubClassAnnotations=$stubClassAnnotations, + keepClassAnnotations=$keepClassAnnotations, + substituteAnnotations=$substituteAnnotations, + nativeSubstituteAnnotations=$nativeSubstituteAnnotations, + classLoadHookAnnotations=$classLoadHookAnnotations, + intersectStubJars=$intersectStubJars, + policyOverrideFile=$policyOverrideFile, + defaultPolicy=$defaultPolicy, + keepAllClasses=$keepAllClasses, + logLevel=$logLevel, + cleanUpOnError=$cleanUpOnError, + enableClassChecker=$enableClassChecker, + enablePreTrace=$enablePreTrace, + enablePostTrace=$enablePostTrace, + enableMethodLogging=$enableMethodLogging, + enableNonStubMethodCallDetection=$enableNonStubMethodCallDetection, + } + """.trimIndent() + } +} diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt new file mode 100644 index 000000000000..0321d9db03ed --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2023 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. + */ +@file:JvmName("Main") + +package com.android.hoststubgen + +const val COMMAND_NAME = "HostStubGen" + +/** + * Entry point. + */ +fun main(args: Array<String>) { + var success = false + var clanupOnError = false + try { + // Parse the command line arguments. + val options = HostStubGenOptions.parseArgs(args) + clanupOnError = options.cleanUpOnError + + log.level = options.logLevel + + log.v("HostStubGen started") + log.v("Options: $options") + + // Run. + HostStubGen(options).run() + + success = true + } catch (e: Exception) { + log.e("$COMMAND_NAME: Error: ${e.message}") + if (e !is UserErrorException) { + e.printStackTrace(log.getErrorPrintStream()) + } + if (clanupOnError) { + TODO("clanupOnError is not implemented yet") + } + } + + log.v("HostStubGen finished") + + System.exit(if (success) 0 else 1 ) +} diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt new file mode 100644 index 000000000000..9fbd6d09bfb0 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen + +/** + * A regex that maches whitespate. + */ +val whitespaceRegex = """\s+""".toRegex() + +/** + * Remove the comment ('#' and following) and surrounding whitespace from a line. + */ +fun normalizeTextLine(s: String): String { + // Remove # and after. (comment) + val pos = s.indexOf('#') + val uncommented = if (pos < 0) s else s.substring(0, pos) + + // Remove surrounding whitespace. + return uncommented.trim() +} diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt new file mode 100644 index 000000000000..a51bdcf0c793 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.asm + +import com.android.hoststubgen.ClassParseException +import com.android.hoststubgen.HostStubGenInternalException +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes +import org.objectweb.asm.Type +import org.objectweb.asm.tree.AnnotationNode +import org.objectweb.asm.tree.ClassNode + + +/** Name of the class initializer method. */ +val CLASS_INITIALIZER_NAME = "<clinit>" + +/** Descriptor of the class initializer method. */ +val CLASS_INITIALIZER_DESC = "()V" + +/** + * Find any of [anyAnnotations] from the list of visible / invisible annotations. + */ +fun findAnyAnnotation( + anyAnnotations: Set<String>, + visibleAnnotations: List<AnnotationNode>?, + invisibleAnnotations: List<AnnotationNode>?, + ): AnnotationNode? { + for (an in visibleAnnotations ?: emptyList()) { + if (anyAnnotations.contains(an.desc)) { + return an + } + } + for (an in invisibleAnnotations ?: emptyList()) { + if (anyAnnotations.contains(an.desc)) { + return an + } + } + return null +} + +fun findAnnotationValueAsString(an: AnnotationNode, propertyName: String): String? { + for (i in 0..(an.values?.size ?: 0) - 2 step 2) { + val name = an.values[i] + + if (name != propertyName) { + continue + } + val value = an.values[i + 1] + if (value is String) { + return value + } + throw ClassParseException( + "The type of '$name' in annotation \"${an.desc}\" must be String" + + ", but is ${value?.javaClass?.canonicalName}") + } + return null +} + +private val removeLastElement = """[./][^./]*$""".toRegex() + +fun getPackageNameFromClassName(className: String): String { + return className.replace(removeLastElement, "") +} + +fun resolveClassName(className: String, packageName: String): String { + if (className.contains('.') || className.contains('/')) { + return className + } + return "$packageName.$className" +} + +fun String.toJvmClassName(): String { + return this.replace('.', '/') +} + +fun String.toHumanReadableClassName(): String { + return this.replace('/', '.') +} + +fun String.toHumanReadableMethodName(): String { + return this.replace('/', '.') +} + +private val numericalInnerClassName = """.*\$\d+$""".toRegex() + +fun isAnonymousInnerClass(cn: ClassNode): Boolean { + // TODO: Is there a better way? + return cn.name.matches(numericalInnerClassName) +} + +/** + * Take a class name. If it's a nested class, then return the name of its direct outer class name. + * Otherwise, return null. + */ +fun getDirectOuterClassName(className: String): String? { + val pos = className.indexOf('$') + if (pos < 0) { + return null + } + return className.substring(0, pos) +} + +/** + * Write bytecode to push all the method arguments to the stack. + * The number of arguments and their type are taken from [methodDescriptor]. + */ +fun writeByteCodeToPushArguments(methodDescriptor: String, writer: MethodVisitor) { + var i = -1 + Type.getArgumentTypes(methodDescriptor).forEach { type -> + i++ + + // See https://en.wikipedia.org/wiki/List_of_Java_bytecode_instructions + + // Note, long and double will consume two local variable spaces, so the extra `i++`. + when (type) { + Type.VOID_TYPE -> throw HostStubGenInternalException("VOID_TYPE not expected") + Type.BOOLEAN_TYPE, Type.INT_TYPE, Type.SHORT_TYPE, Type.CHAR_TYPE + -> writer.visitVarInsn(Opcodes.ILOAD, i) + Type.LONG_TYPE -> writer.visitVarInsn(Opcodes.LLOAD, i++) + Type.FLOAT_TYPE -> writer.visitVarInsn(Opcodes.FLOAD, i) + Type.DOUBLE_TYPE -> writer.visitVarInsn(Opcodes.DLOAD, i++) + else -> writer.visitVarInsn(Opcodes.ALOAD, i) + } + } +} + +/** + * Write bytecode to "RETURN" that matches the method's return type, according to + * [methodDescriptor]. + */ +fun writeByteCodeToReturn(methodDescriptor: String, writer: MethodVisitor) { + Type.getReturnType(methodDescriptor).let { type -> + // See https://en.wikipedia.org/wiki/List_of_Java_bytecode_instructions + when (type) { + Type.VOID_TYPE -> writer.visitInsn(Opcodes.RETURN) + Type.BOOLEAN_TYPE, Type.INT_TYPE, Type.SHORT_TYPE, Type.CHAR_TYPE + -> writer.visitInsn(Opcodes.IRETURN) + Type.LONG_TYPE -> writer.visitInsn(Opcodes.LRETURN) + Type.FLOAT_TYPE -> writer.visitInsn(Opcodes.FRETURN) + Type.DOUBLE_TYPE -> writer.visitInsn(Opcodes.DRETURN) + else -> writer.visitInsn(Opcodes.ARETURN) + } + } +} + +/** + * Return the "visibility" modifier from an `access` integer. + * + * (see https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.1-200-E.1) + */ +fun getVisibilityModifier(access: Int): Int { + return access and (Opcodes.ACC_PUBLIC or Opcodes.ACC_PRIVATE or Opcodes.ACC_PROTECTED) +} + +/** + * Return true if an `access` integer is "private" or "package private". + */ +fun isVisibilityPrivateOrPackagePrivate(access: Int): Boolean { + return when (getVisibilityModifier(access)) { + 0 -> true // Package private. + Opcodes.ACC_PRIVATE -> true + else -> false + } +} diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt new file mode 100644 index 000000000000..4df0bfc4a8d1 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt @@ -0,0 +1,149 @@ +package com.android.hoststubgen.asm + +import com.android.hoststubgen.ClassParseException +import org.objectweb.asm.tree.AnnotationNode +import org.objectweb.asm.tree.ClassNode +import org.objectweb.asm.tree.FieldNode +import org.objectweb.asm.tree.MethodNode +import org.objectweb.asm.tree.TypeAnnotationNode +import java.io.PrintWriter +import java.util.Arrays + +/** + * Stores all classes loaded from a jar file, in a form of [ClassNode] + */ +class ClassNodes { + val mAllClasses: MutableMap<String, ClassNode> = HashMap() + + /** + * Total number of classes registered. + */ + val size: Int + get() = mAllClasses.size + + /** Add a [ClassNode] */ + fun addClass(cn: ClassNode): Boolean { + if (mAllClasses.containsKey(cn.name)) { + return false + } + mAllClasses[cn.name.toJvmClassName()] = cn + return true + } + + /** Get a class's [ClassNodes] (which may not exist) */ + fun findClass(name: String): ClassNode? { + return mAllClasses[name.toJvmClassName()] + } + + /** Get a class's [ClassNodes] (which must exists) */ + fun getClass(name: String): ClassNode { + return findClass(name) ?: throw ClassParseException("Class $name not found") + } + + /** Find a field, which may not exist. */ + fun findField( + className: String, + fieldName: String, + ): FieldNode? { + return findClass(className)?.fields?.firstOrNull { it.name == fieldName }?.let { fn -> + return fn + } + } + + /** Find a method, which may not exist. */ + fun findMethod( + className: String, + methodName: String, + descriptor: String, + ): MethodNode? { + return findClass(className)?.methods + ?.firstOrNull { it.name == methodName && it.desc == descriptor }?.let { mn -> + return mn + } + } + + /** @return true if a class has a class initializer. */ + fun hasClassInitializer(className: String): Boolean { + return findMethod(className, CLASS_INITIALIZER_NAME, CLASS_INITIALIZER_DESC) != null + } + + /** Run the lambda on each class in alphabetical order. */ + fun forEach(consumer: (classNode: ClassNode) -> Unit) { + val keys = mAllClasses.keys.toTypedArray() + Arrays.sort(keys) + + for (name in keys) { + consumer(mAllClasses[name]!!) + } + } + + /** + * Dump all classes. + */ + fun dump(pw: PrintWriter) { + forEach { classNode -> dumpClass(pw, classNode) } + } + + private fun dumpClass(pw: PrintWriter, cn: ClassNode) { + pw.printf("Class: %s [access: %x]\n", cn.name, cn.access) + dumpAnnotations(pw, " ", + cn.visibleTypeAnnotations, cn.invisibleTypeAnnotations, + cn.visibleAnnotations, cn.invisibleAnnotations, + ) + + for (f in cn.fields ?: emptyList()) { + pw.printf(" Field: %s [sig: %s] [desc: %s] [access: %x]\n", + f.name, f.signature, f.desc, f.access) + dumpAnnotations(pw, " ", + f.visibleTypeAnnotations, f.invisibleTypeAnnotations, + f.visibleAnnotations, f.invisibleAnnotations, + ) + } + for (m in cn.methods ?: emptyList()) { + pw.printf(" Method: %s [sig: %s] [desc: %s] [access: %x]\n", + m.name, m.signature, m.desc, m.access) + dumpAnnotations(pw, " ", + m.visibleTypeAnnotations, m.invisibleTypeAnnotations, + m.visibleAnnotations, m.invisibleAnnotations, + ) + } + } + + private fun dumpAnnotations( + pw: PrintWriter, + prefix: String, + visibleTypeAnnotations: List<TypeAnnotationNode>?, + invisibleTypeAnnotations: List<TypeAnnotationNode>?, + visibleAnnotations: List<AnnotationNode>?, + invisibleAnnotations: List<AnnotationNode>?, + ) { + for (an in visibleTypeAnnotations ?: emptyList()) { + pw.printf("%sTypeAnnotation(vis): %s\n", prefix, an.desc) + } + for (an in invisibleTypeAnnotations ?: emptyList()) { + pw.printf("%sTypeAnnotation(inv): %s\n", prefix, an.desc) + } + for (an in visibleAnnotations ?: emptyList()) { + pw.printf("%sAnnotation(vis): %s\n", prefix, an.desc) + if (an.values == null) { + continue + } + var i = 0 + while (i < an.values.size - 1) { + pw.printf("%s - %s -> %s \n", prefix, an.values[i], an.values[i + 1]) + i += 2 + } + } + for (an in invisibleAnnotations ?: emptyList()) { + pw.printf("%sAnnotation(inv): %s\n", prefix, an.desc) + if (an.values == null) { + continue + } + var i = 0 + while (i < an.values.size - 1) { + pw.printf("%s - %s -> %s \n", prefix, an.values[i], an.values[i + 1]) + i += 2 + } + } + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt new file mode 100644 index 000000000000..454569d2f1c5 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.filters + +import com.android.hoststubgen.ClassParseException +import com.android.hoststubgen.HostStubGenErrors +import com.android.hoststubgen.HostStubGenInternalException +import com.android.hoststubgen.InvalidAnnotationException +import com.android.hoststubgen.asm.ClassNodes +import com.android.hoststubgen.asm.findAnnotationValueAsString +import com.android.hoststubgen.asm.findAnyAnnotation +import com.android.hoststubgen.asm.toHumanReadableMethodName +import com.android.hoststubgen.asm.toJvmClassName +import com.android.hoststubgen.log +import org.objectweb.asm.tree.AnnotationNode +import org.objectweb.asm.tree.ClassNode + +// TODO: Detect invalid cases, such as... +// - Class's visibility is lower than the members'. +// - HostSideTestSubstituteWith is set, but it doesn't have @Stub or @Keep + +/** + * [OutputFilter] using Java annotations. + */ +class AnnotationBasedFilter( + private val errors: HostStubGenErrors, + private val classes: ClassNodes, + stubAnnotations_: Set<String>, + keepAnnotations_: Set<String>, + stubClassAnnotations_: Set<String>, + keepClassAnnotations_: Set<String>, + throwAnnotations_: Set<String>, + removeAnnotations_: Set<String>, + substituteAnnotations_: Set<String>, + nativeSubstituteAnnotations_: Set<String>, + classLoadHookAnnotations_: Set<String>, + fallback: OutputFilter, +) : DelegatingFilter(fallback) { + private var stubAnnotations = convertToInternalNames(stubAnnotations_) + private var keepAnnotations = convertToInternalNames(keepAnnotations_) + private var stubClassAnnotations = convertToInternalNames(stubClassAnnotations_) + private var keepClassAnnotations = convertToInternalNames(keepClassAnnotations_) + private var throwAnnotations = convertToInternalNames(throwAnnotations_) + private var removeAnnotations = convertToInternalNames(removeAnnotations_) + private var substituteAnnotations = convertToInternalNames(substituteAnnotations_) + private var nativeSubstituteAnnotations = convertToInternalNames(nativeSubstituteAnnotations_) + private var classLoadHookAnnotations = convertToInternalNames(classLoadHookAnnotations_) + + /** Annotations that control API visibility. */ + private var visibilityAnnotations: Set<String> = convertToInternalNames( + stubAnnotations_ + + keepAnnotations_ + + stubClassAnnotations_ + + keepClassAnnotations_ + + throwAnnotations_ + + removeAnnotations_) + + /** + * All the annotations we use. Note, this one is in a [convertToJvmNames] format unlike + * other ones, because of how it's used. + */ + private var allAnnotations: Set<String> = convertToJvmNames( + stubAnnotations_ + + keepAnnotations_ + + stubClassAnnotations_ + + keepClassAnnotations_ + + throwAnnotations_ + + removeAnnotations_ + + substituteAnnotations_ + + nativeSubstituteAnnotations_ + + classLoadHookAnnotations_) + + private val substitutionHelper = SubstitutionHelper() + + private val reasonAnnotation = "annotation" + private val reasonClassAnnotation = "class-annotation" + + /** + * Throw if an item has more than one visibility annotations. + * + * name1 - 4 are only used in exception messages. We take them as separate strings + * to avoid unnecessary string concatenations. + */ + private fun detectInvalidAnnotations( + visibles: List<AnnotationNode>?, + invisibles: List<AnnotationNode>?, + type: String, + name1: String, + name2: String, + name3: String, + ) { + var count = 0 + for (an in visibles ?: emptyList()) { + if (visibilityAnnotations.contains(an.desc)) { + count++ + } + } + for (an in invisibles ?: emptyList()) { + if (visibilityAnnotations.contains(an.desc)) { + count++ + } + } + if (count > 1) { + val description = if (name2 == "" && name3 == "") { + "$type $name1" + } else { + "$type $name1.$name2$name3" + } + throw InvalidAnnotationException( + "Found more than one visibility annotations on $description") + } + } + + /** + * Find a visibility annotation. + * + * name1 - 4 are only used in exception messages. + */ + private fun findAnnotation( + visibles: List<AnnotationNode>?, + invisibles: List<AnnotationNode>?, + type: String, + name1: String, + name2: String = "", + name3: String = "", + ): FilterPolicyWithReason? { + detectInvalidAnnotations(visibles, invisibles, type, name1, name2, name3) + + findAnyAnnotation(stubAnnotations, visibles, invisibles)?.let { + return FilterPolicy.Stub.withReason(reasonAnnotation) + } + findAnyAnnotation(stubClassAnnotations, visibles, invisibles)?.let { + return FilterPolicy.StubClass.withReason(reasonClassAnnotation) + } + findAnyAnnotation(keepAnnotations, visibles, invisibles)?.let { + return FilterPolicy.Keep.withReason(reasonAnnotation) + } + findAnyAnnotation(keepClassAnnotations, visibles, invisibles)?.let { + return FilterPolicy.KeepClass.withReason(reasonClassAnnotation) + } + findAnyAnnotation(throwAnnotations, visibles, invisibles)?.let { + return FilterPolicy.Throw.withReason(reasonAnnotation) + } + findAnyAnnotation(removeAnnotations, visibles, invisibles)?.let { + return FilterPolicy.Remove.withReason(reasonAnnotation) + } + return null + } + + override fun getPolicyForClass(className: String): FilterPolicyWithReason { + val cn = classes.getClass(className) + + findAnnotation( + cn.visibleAnnotations, + cn.invisibleAnnotations, + "class", + className)?.let { + return it + } + + // If it's any of the annotations, then always keep it. + if (allAnnotations.contains(className)) { + return FilterPolicy.KeepClass.withReason("HostStubGen Annotation") + } + + return super.getPolicyForClass(className) + } + + override fun getPolicyForField( + className: String, + fieldName: String + ): FilterPolicyWithReason { + val cn = classes.getClass(className) + + cn.fields?.firstOrNull { it.name == fieldName }?.let {fn -> + findAnnotation( + fn.visibleAnnotations, + fn.invisibleAnnotations, + "field", + className, + fieldName + )?.let { policy -> + // If the item has an annotation, then use it. + return policy + } + } + return super.getPolicyForField(className, fieldName) + } + + override fun getPolicyForMethod( + className: String, + methodName: String, + descriptor: String + ): FilterPolicyWithReason { + val cn = classes.getClass(className) + + cn.methods?.firstOrNull { it.name == methodName && it.desc == descriptor }?.let { mn -> + // @SubstituteWith is going to complicate the policy here, so we ask helper + // what to do. + substitutionHelper.getPolicyFromSubstitution(cn, mn.name, mn.desc)?.let { + return it + } + + // If there's no substitution, then we check the annotation. + findAnnotation( + mn.visibleAnnotations, + mn.invisibleAnnotations, + "method", + className, + methodName, + descriptor + )?.let { policy -> + return policy + } + } + return super.getPolicyForMethod(className, methodName, descriptor) + } + + override fun getRenameTo( + className: String, + methodName: String, + descriptor: String + ): String? { + val cn = classes.getClass(className) + + // If the method has a "substitute with" annotation, then return its "value" parameter. + cn.methods?.firstOrNull { it.name == methodName && it.desc == descriptor }?.let { mn -> + return substitutionHelper.getRenameTo(cn, mn.name, mn.desc) + } + return null + } + + override fun getNativeSubstitutionClass(className: String): String? { + classes.getClass(className).let { cn -> + findAnyAnnotation(nativeSubstituteAnnotations, + cn.visibleAnnotations, cn.invisibleAnnotations)?.let { an -> + return getAnnotationField(an, "value")?.toJvmClassName() + } + } + return null + } + + override fun getClassLoadHook(className: String): String? { + classes.getClass(className).let { cn -> + findAnyAnnotation(classLoadHookAnnotations, + cn.visibleAnnotations, cn.invisibleAnnotations)?.let { an -> + return getAnnotationField(an, "value")?.toHumanReadableMethodName() + } + } + return null + } + + private data class MethodKey(val name: String, val desc: String) + + /** + * In order to handle substitution, we need to build a reverse mapping of substitution + * methods. + * + * This class automatically builds such a map internally that the above methods can + * take advantage of. + */ + private inner class SubstitutionHelper { + private var currentClass: ClassNode? = null + + private var policiesFromSubstitution = mutableMapOf<MethodKey, FilterPolicyWithReason>() + private var substituteToMethods = mutableMapOf<MethodKey, String>() + + fun getPolicyFromSubstitution(cn: ClassNode, methodName: String, descriptor: String): + FilterPolicyWithReason? { + setClass(cn) + return policiesFromSubstitution[MethodKey(methodName, descriptor)] + } + + fun getRenameTo(cn: ClassNode, methodName: String, descriptor: String): String? { + setClass(cn) + return substituteToMethods[MethodKey(methodName, descriptor)] + } + + /** + * Every time we see a different class, we scan all its methods for substitution attributes, + * and compute (implicit) policies caused by them. + * + * For example, for the following methods: + * + * @Stub + * @Substitute(suffix = "_host") + * private void foo() { + * // This isn't supported on the host side. + * } + * private void foo_host() { + * // Host side implementation + * } + * + * We internally handle them as: + * + * foo() -> Remove + * foo_host() -> Stub, and then rename it to foo(). + */ + private fun setClass(cn: ClassNode) { + if (currentClass == cn) { + return + } + // If the class is changing, we'll rebuild the internal structure. + currentClass = cn + + policiesFromSubstitution.clear() + substituteToMethods.clear() + + for (mn in cn.methods ?: emptyList()) { + findAnyAnnotation(substituteAnnotations, + mn.visibleAnnotations, + mn.invisibleAnnotations)?.let { an -> + + // Find the policy for this method. + val policy = outermostFilter.getPolicyForMethod(cn.name, mn.name, mn.desc) + .policy.resolveClassWidePolicy() + // Make sure it's either Stub or Keep. + if (!(policy.needsInStub || policy.needsInImpl)) { + // TODO: Use the real annotation names in the message + errors.onErrorFound("@SubstituteWith must have either @Stub or @Keep") + return@let + } + if (!policy.isUsableWithMethods) { + throw HostStubGenInternalException("Policy $policy shouldn't show up here") + } + + val suffix = getAnnotationField(an, "suffix") ?: return@let + val renameFrom = mn.name + suffix + val renameTo = mn.name + + if (renameFrom == renameTo) { + errors.onErrorFound("@SubstituteWith have a different name") + return@let + } + + // This mn has "SubstituteWith". This means, + // 1. Re move the "rename-to" method, so add it to substitutedMethods. + policiesFromSubstitution[MethodKey(renameTo, mn.desc)] = + FilterPolicy.Remove.withReason("substitute-to") + + // 2. We also keep the from-to in the map. + policiesFromSubstitution[MethodKey(renameFrom, mn.desc)] = + policy.withReason("substitute-from") + substituteToMethods[MethodKey(renameFrom, mn.desc)] = renameTo + + log.v("Substitution found: %s%s -> %s", renameFrom, mn.desc, renameTo) + } + } + } + } + + /** + * Return the (String) value of 'value' parameter from an annotation. + */ + private fun getAnnotationField(an: AnnotationNode, name: String): String? { + try { + val suffix = findAnnotationValueAsString(an, name) + if (suffix == null) { + errors.onErrorFound("Annotation \"${an.desc}\" must have field $name") + } + return suffix + } catch (e: ClassParseException) { + errors.onErrorFound(e.message!!) + return null + } + } + + companion object { + /** + * Convert from human-readable type names (e.g. "com.android.TypeName") to the internal type + * names (e.g. "Lcom/android/TypeName). + */ + private fun convertToInternalNames(input: Set<String>): Set<String> { + val ret = mutableSetOf<String>() + input.forEach { ret.add("L" + it.toJvmClassName() + ";") } + return ret + } + + /** + * Convert from human-readable type names to JVM type names. + */ + private fun convertToJvmNames(input: Set<String>): Set<String> { + val ret = mutableSetOf<String>() + input.forEach { ret.add(it.toJvmClassName()) } + return ret + } + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt new file mode 100644 index 000000000000..6aac3d88b8b1 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.filters + +import com.android.hoststubgen.asm.getDirectOuterClassName + +/** + * This is used as the second last fallback filter. This filter propagates the class-wide policy + * (obtained from [outermostFilter]) to the fields and methods. + */ +class ClassWidePolicyPropagatingFilter( + fallback: OutputFilter, + ) : DelegatingFilter(fallback) { + + private fun getClassWidePolicy(className: String, resolve: Boolean): FilterPolicyWithReason? { + var currentClass = className + + while (true) { + outermostFilter.getPolicyForClass(className).let { policy -> + if (policy.policy.isClassWidePolicy) { + val p = if (resolve) policy.policy.resolveClassWidePolicy() else policy.policy + + return p.withReason(policy.reason).wrapReason("class-wide in $currentClass") + } + // If the class's policy is remove, then remove it. + if (policy.policy == FilterPolicy.Remove) { + return FilterPolicy.Remove.withReason("class-wide in $currentClass") + } + } + + // Next, look at the outer class... + val outer = getDirectOuterClassName(currentClass) + if (outer == null) { + return null + } + currentClass = outer + } + } + + override fun getPolicyForClass(className: String): FilterPolicyWithReason { + // If it's a nested class, use the outer class's policy. + getDirectOuterClassName(className)?.let { outerName -> + getClassWidePolicy(outerName, resolve = false)?.let { policy -> + return policy + } + } + + return super.getPolicyForClass(className) + } + + override fun getPolicyForField( + className: String, + fieldName: String + ): FilterPolicyWithReason { + return getClassWidePolicy(className, resolve = true) + ?: super.getPolicyForField(className, fieldName) + } + + override fun getPolicyForMethod( + className: String, + methodName: String, + descriptor: String + ): FilterPolicyWithReason { + return getClassWidePolicy(className, resolve = true) + ?: super.getPolicyForMethod(className, methodName, descriptor) + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt new file mode 100644 index 000000000000..33010baaf894 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.filters + +import com.android.hoststubgen.HostStubGenInternalException + + +/** + * [OutputFilter] with a given policy. Used to represent the default policy. + * + * This is used as the last fallback filter. + * + * @param policy the policy. Cannot be a "substitute" policy. + */ +class ConstantFilter( + policy: FilterPolicy, + val reason: String +) : OutputFilter() { + val classPolicy: FilterPolicy + val fieldPolicy: FilterPolicy + val methodPolicy: FilterPolicy + + init { + if (policy.isSubstitute) { + throw HostStubGenInternalException( + "ConstantFilter doesn't allow substitution policies.") + } + if (policy.isClassWidePolicy) { + // We prevent it, because there's no point in using class-wide policies because + // all members get othe same policy too anyway. + throw HostStubGenInternalException( + "ConstantFilter doesn't allow class-wide policies.") + } + methodPolicy = policy + + // TODO: Need to think about the realistic default behavior. + classPolicy = if (policy != FilterPolicy.Throw) policy else FilterPolicy.Remove + fieldPolicy = classPolicy + } + + override fun getPolicyForClass(className: String): FilterPolicyWithReason { + return classPolicy.withReason(reason) + } + + override fun getPolicyForField(className: String, fieldName: String): FilterPolicyWithReason { + return fieldPolicy.withReason(reason) + } + + override fun getPolicyForMethod( + className: String, + methodName: String, + descriptor: String, + ): FilterPolicyWithReason { + return methodPolicy.withReason(reason) + } +} diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt new file mode 100644 index 000000000000..f0763c4ba097 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.filters + +/** + * Base class for an [OutputFilter] that uses another filter as a fallback. + */ +abstract class DelegatingFilter( + // fallback shouldn't be used by subclasses, so make it private. + // They should instead be calling into `super` or `outermostFilter`. + private val fallback: OutputFilter +) : OutputFilter() { + init { + fallback.outermostFilter = this + } + + override var outermostFilter: OutputFilter = this + get() = field + set(value) { + field = value + // Propagate the inner filters. + fallback.outermostFilter = value + } + + override fun getPolicyForClass(className: String): FilterPolicyWithReason { + return fallback.getPolicyForClass(className) + } + + override fun getPolicyForField( + className: String, + fieldName: String + ): FilterPolicyWithReason { + return fallback.getPolicyForField(className, fieldName) + } + + override fun getPolicyForMethod( + className: String, + methodName: String, + descriptor: String + ): FilterPolicyWithReason { + return fallback.getPolicyForMethod(className, methodName, descriptor) + } + + override fun getRenameTo( + className: String, + methodName: String, + descriptor: String + ): String? { + return fallback.getRenameTo(className, methodName, descriptor) + } + + override fun getNativeSubstitutionClass(className: String): String? { + return fallback.getNativeSubstitutionClass(className) + } + + override fun getClassLoadHook(className: String): String? { + return fallback.getClassLoadHook(className) + } +} diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt new file mode 100644 index 000000000000..f11ac2f7325d --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.filters + +enum class FilterPolicy { + /** + * Keep the item in the stub jar file, so tests can use it. + */ + Stub, + + /** + * Keep the item in the impl jar file, but not in the stub file. Tests cannot use it directly, + * but indirectly. + */ + Keep, + + /** + * Only used for types. Keep the class in the stub, and also all its members. + * But each member can have another annotations to override it. + */ + StubClass, + + /** + * Only used for types. Keep the class in the impl, not in the stub, and also all its members. + * But each member can have another annotations to override it. + */ + KeepClass, + + /** + * Same as [Stub], but replace it with a "substitution" method. Only usable with methods. + */ + SubstituteAndStub, + + /** + * Same as [Keep], but replace it with a "substitution" method. Only usable with methods. + */ + SubstituteAndKeep, + + /** + * Only usable with methods. The item will be kept in the impl jar file, but when called, + * it'll throw. + */ + Throw, + + /** + * Remove the item completely. + */ + Remove; + + val isSubstitute: Boolean + get() = this == SubstituteAndStub || this == SubstituteAndKeep + + val needsInStub: Boolean + get() = this == Stub || this == StubClass || this == SubstituteAndStub + + val needsInImpl: Boolean + get() = this != Remove + + /** Returns whether a policy can be used with classes */ + val isUsableWithClasses: Boolean + get() { + return when (this) { + Stub, StubClass, Keep, KeepClass, Remove -> true + else -> false + } + } + + /** Returns whether a policy can be used with fields. */ + val isUsableWithFields: Boolean + get() { + return when (this) { + Stub, Keep, Remove -> true + else -> false + } + } + + /** Returns whether a policy can be used with methods */ + val isUsableWithMethods: Boolean + get() { + return when (this) { + StubClass, KeepClass -> false + else -> true + } + } + + /** Returns whether a policy is a class-wide one. */ + val isClassWidePolicy: Boolean + get() { + return when (this) { + StubClass, KeepClass -> true + else -> false + } + } + + fun getSubstitutionBasePolicy(): FilterPolicy { + return when (this) { + SubstituteAndKeep -> Keep + SubstituteAndStub -> Stub + else -> this + } + } + + /** + * Convert {Stub,Keep}Class to the corresponding Stub or Keep. + */ + fun resolveClassWidePolicy(): FilterPolicy { + return when (this) { + StubClass -> Stub + KeepClass -> Keep + else -> this + } + } + + /** + * Create a [FilterPolicyWithReason] with a given reason. + */ + fun withReason(reason: String): FilterPolicyWithReason { + return FilterPolicyWithReason(this, reason) + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt new file mode 100644 index 000000000000..b64a2f5fd8a5 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.filters + +/** + * Captures a [FilterPolicy] with a human-readable reason. + */ +data class FilterPolicyWithReason ( + val policy: FilterPolicy, + val reason: String = "", +) { + /** + * Return a new [FilterPolicy] with an updated reason, while keeping the original reason + * as an "inner-reason". + */ + fun wrapReason(reason: String): FilterPolicyWithReason { + return FilterPolicyWithReason(policy, "$reason [inner-reason: ${this.reason}]") + } + + /** + * If the visibility is lower than "Keep" (meaning if it's "remove"), + * then return a new [FilterPolicy] with "Keep". + * Otherwise, return itself + */ + fun promoteToKeep(promotionReason: String): FilterPolicyWithReason { + if (policy.needsInImpl) { + return this + } + val newPolicy = if (policy.isClassWidePolicy) FilterPolicy.KeepClass else FilterPolicy.Keep + + return FilterPolicyWithReason(newPolicy, + "$promotionReason [original remove reason: ${this.reason}]") + } + + /** + * If the visibility is above "Keep" (meaning if it's "stub"), + * then return a new [FilterPolicy] with "Keep". + * Otherwise, return itself + */ + fun demoteToKeep(promotionReason: String): FilterPolicyWithReason { + if (!policy.needsInStub) { + return this + } + val newPolicy = if (policy.isClassWidePolicy) FilterPolicy.KeepClass else FilterPolicy.Keep + + return FilterPolicyWithReason(newPolicy, + "$promotionReason [original stub reason: ${this.reason}]") + } + + override fun toString(): String { + return "[$policy - reason: $reason]" + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt new file mode 100644 index 000000000000..9c372ff68e37 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.filters + +import com.android.hoststubgen.HostStubGenErrors +import com.android.hoststubgen.HostStubGenInternalException +import com.android.hoststubgen.asm.isAnonymousInnerClass +import com.android.hoststubgen.log +import com.android.hoststubgen.asm.ClassNodes +import com.android.hoststubgen.asm.isVisibilityPrivateOrPackagePrivate + +/** + * Filter implementing "implicit" rules, such as: + * - "keep all anonymous inner classes if the outer class is keep". + * (But anonymous inner classes should never be in "stub") + * - For classes in stub, make sure private parameterless constructors are also in stub, if any. + */ +class ImplicitOutputFilter( + private val errors: HostStubGenErrors, + private val classes: ClassNodes, + fallback: OutputFilter +) : DelegatingFilter(fallback) { + private fun getClassImplicitPolicy(className: String): FilterPolicyWithReason? { + // TODO: This check should be cached. + val cn = classes.getClass(className) + + if (isAnonymousInnerClass(cn)) { + log.forDebug { +// log.d(" anon-inner class: ${className} outer: ${cn.outerClass} ") + } + if (cn.outerClass == null) { + throw HostStubGenInternalException( + "outerClass is null for anonymous inner class") + } + // If the outer class needs to be in impl, it should be in impl too. + val outerPolicy = outermostFilter.getPolicyForClass(cn.outerClass) + if (outerPolicy.policy.needsInImpl) { + return FilterPolicy.KeepClass.withReason("anonymous-inner-class") + } + } + return null + } + + override fun getPolicyForClass(className: String): FilterPolicyWithReason { + // Use the implicit policy, if any. + getClassImplicitPolicy(className)?.let { return it } + + return super.getPolicyForClass(className) + } + + override fun getPolicyForMethod( + className: String, + methodName: String, + descriptor: String + ): FilterPolicyWithReason { + val fallback = super.getPolicyForMethod(className, methodName, descriptor) + + // If the class is in the stub, then we need to put the private constructor in the stub too, + // to prevent the class from getting instantiated. + if (outermostFilter.getPolicyForClass(className).policy.needsInStub && + !fallback.policy.needsInStub && + (methodName == "<init>") && // Constructor? + (descriptor == "()V")) { // Has zero parameters? + classes.findMethod(className, methodName, descriptor)?.let { mn -> + if (isVisibilityPrivateOrPackagePrivate(mn.access)) { + return FilterPolicy.Stub.withReason("private constructor in stub class") + } + } + } + + return fallback + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt new file mode 100644 index 000000000000..f3551d49bd36 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.filters + +import com.android.hoststubgen.UnknownApiException +import com.android.hoststubgen.asm.ClassNodes +import com.android.hoststubgen.asm.toHumanReadableClassName +import com.android.hoststubgen.asm.toHumanReadableMethodName + +// TODO: Validate all input names. + +class InMemoryOutputFilter( + private val classes: ClassNodes, + fallback: OutputFilter, +) : DelegatingFilter(fallback) { + private val mPolicies: MutableMap<String, FilterPolicyWithReason> = mutableMapOf() + private val mRenames: MutableMap<String, String> = mutableMapOf() + private val mNativeSubstitutionClasses: MutableMap<String, String> = mutableMapOf() + private val mClassLoadHooks: MutableMap<String, String> = mutableMapOf() + + private fun getClassKey(className: String): String { + return className.toHumanReadableClassName() + } + + private fun getFieldKey(className: String, fieldName: String): String { + return getClassKey(className) + "." + fieldName + } + + private fun getMethodKey(className: String, methodName: String, signature: String): String { + return getClassKey(className) + "." + methodName + ";" + signature + } + + override fun getPolicyForClass(className: String): FilterPolicyWithReason { + return mPolicies[getClassKey(className)] ?: super.getPolicyForClass(className) + } + + private fun ensureClassExists(className: String) { + if (classes.findClass(className) == null) { + throw UnknownApiException("Unknown class $className") + } + } + + private fun ensureFieldExists(className: String, fieldName: String) { + if (classes.findField(className, fieldName) == null) { + throw UnknownApiException("Unknown field $className.$fieldName") + } + } + + private fun ensureMethodExists( + className: String, + methodName: String, + descriptor: String + ) { + if (classes.findMethod(className, methodName, descriptor) == null) { + throw UnknownApiException("Unknown method $className.$methodName$descriptor") + } + } + + fun setPolicyForClass(className: String, policy: FilterPolicyWithReason) { + ensureClassExists(className) + mPolicies[getClassKey(className)] = policy + } + + override fun getPolicyForField(className: String, fieldName: String): FilterPolicyWithReason { + return mPolicies[getFieldKey(className, fieldName)] + ?: super.getPolicyForField(className, fieldName) + } + + fun setPolicyForField(className: String, fieldName: String, policy: FilterPolicyWithReason) { + ensureFieldExists(className, fieldName) + mPolicies[getFieldKey(className, fieldName)] = policy + } + + override fun getPolicyForMethod( + className: String, + methodName: String, + descriptor: String, + ): FilterPolicyWithReason { + return mPolicies[getMethodKey(className, methodName, descriptor)] + ?: super.getPolicyForMethod(className, methodName, descriptor) + } + + fun setPolicyForMethod( + className: String, + methodName: String, + descriptor: String, + policy: FilterPolicyWithReason, + ) { + ensureMethodExists(className, methodName, descriptor) + mPolicies[getMethodKey(className, methodName, descriptor)] = policy + } + + override fun getRenameTo(className: String, methodName: String, descriptor: String): String? { + return mRenames[getMethodKey(className, methodName, descriptor)] + ?: super.getRenameTo(className, methodName, descriptor) + } + + fun setRenameTo(className: String, methodName: String, descriptor: String, toName: String) { + ensureMethodExists(className, methodName, descriptor) + ensureMethodExists(className, toName, descriptor) + mRenames[getMethodKey(className, methodName, descriptor)] = toName + } + + override fun getNativeSubstitutionClass(className: String): String? { + return mNativeSubstitutionClasses[getClassKey(className)] + ?: super.getNativeSubstitutionClass(className) + } + + fun setNativeSubstitutionClass(from: String, to: String) { + ensureClassExists(from) + + // Native substitute classes may be provided from other jars, so we can't do this check. + // ensureClassExists(to) + mNativeSubstitutionClasses[getClassKey(from)] = to.toHumanReadableClassName() + } + + override fun getClassLoadHook(className: String): String? { + return mClassLoadHooks[getClassKey(className)] + ?: super.getClassLoadHook(className) + } + + fun setClassLoadHook(className: String, methodName: String) { + mClassLoadHooks[getClassKey(className)] = methodName.toHumanReadableMethodName() + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepAllClassesFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepAllClassesFilter.kt new file mode 100644 index 000000000000..45dd38d10ef5 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepAllClassesFilter.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.filters + +/** + * An [OutputFilter] that keeps all classes by default. (but none of its members) + * + * We're not currently using it, but using it *might* make certain things easier. For example, with + * this, all classes would at least be loadable. + */ +class KeepAllClassesFilter(fallback: OutputFilter) : DelegatingFilter(fallback) { + override fun getPolicyForClass(className: String): FilterPolicyWithReason { + // If the default visibility wouldn't keep it, change it to "keep". + val f = super.getPolicyForClass(className) + return f.promoteToKeep("keep-all-classes") + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt new file mode 100644 index 000000000000..392ee4b81613 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.filters + +/** + * Base class for "filters", which decides what APIs should go to the stub / impl jars. + */ +abstract class OutputFilter { + /** + * Filters are stacked over one another. This fields contains the "outermost" filter in a + * filter stack chain. + * + * Subclasses must use this filter to get a policy, when they need to infer a policy + * from the policy of another API. + * + * For example, [ClassWidePolicyPropagatingFilter] needs to check the policy of the enclosing + * class to propagate "class-wide" policies, but when it does so, it can't just use + * `this.getPolicyForClass()` because that wouldn't return policies decided by "outer" + * filters. Instead, it uses [outermostFilter.getPolicyForClass()]. + * + * Note, [outermostFilter] can be itself, so make sure not to cause infinity recursions when + * using it. + */ + open var outermostFilter: OutputFilter = this + get() = field + set(value) { + field = value + } + + abstract fun getPolicyForClass(className: String): FilterPolicyWithReason + + abstract fun getPolicyForField(className: String, fieldName: String): FilterPolicyWithReason + + abstract fun getPolicyForMethod( + className: String, + methodName: String, + descriptor: String, + ): FilterPolicyWithReason + + /** + * If a given method is a substitute-from method, return the substitute-to method name. + * + * The substitute-to and from methods must have the same signature, in the same class. + */ + open fun getRenameTo(className: String, methodName: String, descriptor: String): String? { + return null + } + + /** + * Return a "native substitution class" name for a given class. + * + * The result will be in a "human readable" form. (e.g. uses '.'s instead of '/'s) + * + * (which corresponds to @HostSideTestNativeSubstitutionClass of the standard annotations.) + */ + open fun getNativeSubstitutionClass(className: String): String? { + return null + } + + /** + * Return a "class load hook" method name for a given class. + * + * (which corresponds to @HostSideTestClassLoadHook of the standard annotations.) + */ + open fun getClassLoadHook(className: String): String? { + return null + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/StubIntersectingFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/StubIntersectingFilter.kt new file mode 100644 index 000000000000..f92a0271d7c7 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/StubIntersectingFilter.kt @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.filters + +import com.android.hoststubgen.HostStubGenErrors +import com.android.hoststubgen.asm.ClassNodes + +private const val REASON = "demoted, not in intersect jars" + +/** + * An [OutputFilter] that will restrict what to put in stub to only what shows up in "intersecting + * jar" files. + * + * For example, if the Android public API stub jar is provided, then the HostStubGen's output + * stub will be restricted to public APIs. + */ +class StubIntersectingFilter( + private val errors: HostStubGenErrors, + /** + * If a class / field / method is not in any of these jars, then we will not put it in + * stub. + */ + private val intersectingJars: Map<String, ClassNodes>, + fallback: OutputFilter, +) : DelegatingFilter(fallback) { + private inline fun exists(predicate: (ClassNodes) -> Boolean): Boolean { + intersectingJars.forEach { entry -> + if (predicate(entry.value)) { + return true + } + } + return false + } + + /** + * If [origPolicy] is less than "Stub", then return it as-is. + * + * Otherwise, call [inStubChecker] to see if the API is in any of [intersectingJars]. + * If yes, then return [origPolicy] as-is. Otherwise, demote to "Keep". + */ + private fun intersectWithStub( + origPolicy: FilterPolicyWithReason, + inStubChecker: () -> Boolean, + ): FilterPolicyWithReason { + if (origPolicy.policy.needsInStub) { + // Only check the stub jars, when the class is supposed to be in stub otherwise. + if (!inStubChecker()) { + return origPolicy.demoteToKeep(REASON) + } + } + return origPolicy + } + + override fun getPolicyForClass(className: String): FilterPolicyWithReason { + return intersectWithStub(super.getPolicyForClass(className)) { + exists { classes -> classes.findClass(className) != null } + } + } + + override fun getPolicyForField( + className: String, + fieldName: String + ): FilterPolicyWithReason { + return intersectWithStub(super.getPolicyForField(className, fieldName)) { + exists { classes -> classes.findField(className, fieldName) != null } + } + } + + override fun getPolicyForMethod( + className: String, + methodName: String, + descriptor: String + ): FilterPolicyWithReason { + return intersectWithStub(super.getPolicyForMethod(className, methodName, descriptor)) { + exists { classes -> classes.findMethod(className, methodName, descriptor) != null } + } + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt new file mode 100644 index 000000000000..46546e8b9491 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.filters + +import com.android.hoststubgen.UserErrorException +import com.android.hoststubgen.asm.ClassNodes +import com.android.hoststubgen.log +import com.android.hoststubgen.normalizeTextLine +import com.android.hoststubgen.whitespaceRegex +import org.objectweb.asm.Opcodes +import org.objectweb.asm.tree.ClassNode +import java.io.BufferedReader +import java.io.FileReader +import java.io.PrintWriter +import java.util.Objects + +/** + * Print a class node as a "keep" policy. + */ +fun printAsTextPolicy(pw: PrintWriter, cn: ClassNode) { + pw.printf("class %s\t%s\n", cn.name, "keep") + + for (f in cn.fields ?: emptyList()) { + pw.printf(" field %s\t%s\n", f.name, "keep") + } + for (m in cn.methods ?: emptyList()) { + pw.printf(" method %s\t%s\t%s\n", m.name, m.desc, "keep") + } +} + +/** Return true if [access] is either public or protected. */ +private fun isVisible(access: Int): Boolean { + return (access and (Opcodes.ACC_PUBLIC or Opcodes.ACC_PROTECTED)) != 0 +} + +/** + * Exception for a parse error. + */ +private class ParseException : Exception, UserErrorException { + val hasSourceInfo: Boolean + + constructor(message: String) : super(message) { + hasSourceInfo = false + } + + constructor(message: String, file: String, line: Int) : + super("$message in file $file line $line") { + hasSourceInfo = true + } + + fun withSourceInfo(filename: String, lineNo: Int): ParseException { + if (hasSourceInfo) { + return this // Already has source information. + } else { + return ParseException(this.message ?: "", filename, lineNo) + } + } +} + +private const val FILTER_REASON = "file-override" + +/** + * Read a given "policy" file and return as an [OutputFilter] + */ +fun createFilterFromTextPolicyFile( + filename: String, + classes: ClassNodes, + fallback: OutputFilter, + ): OutputFilter { + log.i("Loading offloaded annotations from $filename ...") + log.withIndent { + val ret = InMemoryOutputFilter(classes, fallback) + + var lineNo = 0 + + try { + BufferedReader(FileReader(filename)).use { reader -> + var className = "" + + while (true) { + var line = reader.readLine() + if (line == null) { + break + } + lineNo++ + + line = normalizeTextLine(line) + + if (line.isEmpty()) { + continue // skip empty lines. + } + + val fields = line.split(whitespaceRegex).toTypedArray() + when (fields[0].lowercase()) { + "c", "class" -> { + if (fields.size < 3) { + throw ParseException("Class ('c') expects 2 fields.") + } + className = fields[1] + if (fields[2].startsWith("!")) { + // It's a native-substitution. + val toClass = fields[2].substring(1) + ret.setNativeSubstitutionClass(className, toClass) + } else if (fields[2].startsWith("~")) { + // It's a class-load hook + val callback = fields[2].substring(1) + ret.setClassLoadHook(className, callback) + } else { + val policy = parsePolicy(fields[2]) + if (!policy.isUsableWithClasses) { + throw ParseException("Class can't have policy '$policy'") + } + Objects.requireNonNull(className) + + // TODO: Duplicate check, etc + ret.setPolicyForClass(className, policy.withReason(FILTER_REASON)) + } + } + + "f", "field" -> { + if (fields.size < 3) { + throw ParseException("Field ('f') expects 2 fields.") + } + val name = fields[1] + val policy = parsePolicy(fields[2]) + if (!policy.isUsableWithFields) { + throw ParseException("Field can't have policy '$policy'") + } + Objects.requireNonNull(className) + + // TODO: Duplicate check, etc + ret.setPolicyForField(className, name, policy.withReason(FILTER_REASON)) + } + + "m", "method" -> { + if (fields.size < 4) { + throw ParseException("Method ('m') expects 3 fields.") + } + val name = fields[1] + val signature = fields[2] + val policy = parsePolicy(fields[3]) + + if (!policy.isUsableWithMethods) { + throw ParseException("Method can't have policy '$policy'") + } + + Objects.requireNonNull(className) + + ret.setPolicyForMethod(className, name, signature, + policy.withReason(FILTER_REASON)) + if (policy.isSubstitute) { + val fromName = fields[3].substring(1) + + if (fromName == name) { + throw ParseException( + "Substitution must have a different name") + } + + // Set the policy for the "from" method. + ret.setPolicyForMethod(className, fromName, signature, + policy.getSubstitutionBasePolicy() + .withReason(FILTER_REASON)) + + // Keep "from" -> "to" mapping. + ret.setRenameTo(className, fromName, signature, name) + } + } + + else -> { + throw ParseException("Unknown directive \"${fields[0]}\"") + } + } + } + } + } catch (e: ParseException) { + throw e.withSourceInfo(filename, lineNo) + } + return ret + } +} + +private fun parsePolicy(s: String): FilterPolicy { + return when (s.lowercase()) { + "s", "stub" -> FilterPolicy.Stub + "k", "keep" -> FilterPolicy.Keep + "t", "throw" -> FilterPolicy.Throw + "r", "remove" -> FilterPolicy.Remove + "sc", "stubclass" -> FilterPolicy.StubClass + "kc", "keepclass" -> FilterPolicy.KeepClass + else -> { + if (s.startsWith("@")) { + FilterPolicy.SubstituteAndStub + } else if (s.startsWith("%")) { + FilterPolicy.SubstituteAndKeep + } else { + throw ParseException("Invalid policy \"$s\"") + } + } + } +} diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt new file mode 100644 index 000000000000..3cf9a1d7fb82 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.visitors + +import com.android.hoststubgen.HostStubGenErrors +import com.android.hoststubgen.asm.ClassNodes +import com.android.hoststubgen.asm.getPackageNameFromClassName +import com.android.hoststubgen.asm.resolveClassName +import com.android.hoststubgen.asm.toJvmClassName +import com.android.hoststubgen.filters.FilterPolicy +import com.android.hoststubgen.filters.FilterPolicyWithReason +import com.android.hoststubgen.filters.OutputFilter +import com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +import com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass +import com.android.hoststubgen.log +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.FieldVisitor +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes +import org.objectweb.asm.util.TraceClassVisitor +import java.io.PrintWriter + +val OPCODE_VERSION = Opcodes.ASM9 + +abstract class BaseAdapter ( + protected val classes: ClassNodes, + nextVisitor: ClassVisitor, + protected val filter: OutputFilter, + protected val options: Options, +) : ClassVisitor(OPCODE_VERSION, nextVisitor) { + + /** + * Options to control the behavior. + */ + data class Options ( + val errors: HostStubGenErrors, + val enablePreTrace: Boolean, + val enablePostTrace: Boolean, + val enableMethodLogging: Boolean, + val enableNonStubMethodCallDetection: Boolean, + ) + + protected lateinit var currentPackageName: String + protected lateinit var currentClassName: String + protected var nativeSubstitutionClass: String? = null + protected lateinit var classPolicy: FilterPolicyWithReason + + /** + * Return whether an item with a given policy should be included in the output. + */ + protected abstract fun shouldEmit(policy: FilterPolicy): Boolean + + override fun visit( + version: Int, + access: Int, + name: String, + signature: String?, + superName: String?, + interfaces: Array<String>, + ) { + super.visit(version, access, name, signature, superName, interfaces) + currentClassName = name + currentPackageName = getPackageNameFromClassName(name) + classPolicy = filter.getPolicyForClass(currentClassName) + + log.d("[%s] visit: %s (package: %s)", this.javaClass.simpleName, name, currentPackageName) + log.indent() + log.v("Emitting class: %s", name) + log.indent() + + filter.getNativeSubstitutionClass(currentClassName)?.let { className -> + val fullClassName = resolveClassName(className, currentPackageName).toJvmClassName() + log.d(" NativeSubstitutionClass: $fullClassName") + if (classes.findClass(fullClassName) == null) { + log.w("Native substitution class $fullClassName not found. Class must be " + + "available at runtime.") + } else { + // If the class exists, it must have a KeepClass policy. + if (filter.getPolicyForClass(fullClassName).policy != FilterPolicy.KeepClass) { + // TODO: Use real annotation name. + options.errors.onErrorFound( + "Native substitution class $fullClassName should have @Keep.") + } + } + + nativeSubstitutionClass = fullClassName + } + // Inject annotations to generated classes. + if (classPolicy.policy.needsInStub) { + visitAnnotation(HostStubGenProcessedStubClass.CLASS_DESCRIPTOR, true) + } + if (classPolicy.policy.needsInImpl) { + visitAnnotation(HostStubGenProcessedKeepClass.CLASS_DESCRIPTOR, true) + } + } + + override fun visitEnd() { + log.unindent() + log.unindent() + super.visitEnd() + } + + var skipMemberModificationNestCount = 0 + + /** + * This method allows writing class members without any modifications. + */ + protected inline fun writeRawMembers(callback: () -> Unit) { + skipMemberModificationNestCount++ + try { + callback() + } finally { + skipMemberModificationNestCount-- + } + } + + override fun visitField( + access: Int, + name: String, + descriptor: String, + signature: String?, + value: Any?, + ): FieldVisitor? { + if (skipMemberModificationNestCount > 0) { + return super.visitField(access, name, descriptor, signature, value) + } + val policy = filter.getPolicyForField(currentClassName, name) + log.d("visitField: %s %s [%x] Policy: %s", name, descriptor, access, policy) + + log.withIndent { + if (!shouldEmit(policy.policy)) { + log.d("Removing %s %s", name, policy) + return null + } + + log.v("Emitting field: %s %s %s", name, descriptor, policy) + return super.visitField(access, name, descriptor, signature, value) + } + } + + override fun visitMethod( + access: Int, + name: String, + descriptor: String, + signature: String?, + exceptions: Array<String>?, + ): MethodVisitor? { + if (skipMemberModificationNestCount > 0) { + return super.visitMethod(access, name, descriptor, signature, exceptions) + } + val p = filter.getPolicyForMethod(currentClassName, name, descriptor) + log.d("visitMethod: %s%s [%x] [%s] Policy: %s", name, descriptor, access, signature, p) + + log.withIndent { + // If it's a substitute-to method, then skip. + val policy = filter.getPolicyForMethod(currentClassName, name, descriptor) + if (policy.policy.isSubstitute) { + log.d("Skipping %s%s %s", name, descriptor, policy) + return null + } + if (!shouldEmit(p.policy)) { + log.d("Removing %s%s %s", name, descriptor, policy) + return null + } + + // Maybe rename the method. + val newName: String + val substituteTo = filter.getRenameTo(currentClassName, name, descriptor) + if (substituteTo != null) { + newName = substituteTo + log.v("Emitting %s.%s%s as %s %s", currentClassName, name, descriptor, + newName, policy) + } else { + log.v("Emitting method: %s%s %s", name, descriptor, policy) + newName = name + } + + // Let subclass update the flag. + // But note, we only use it when calling the super's method, + // but not for visitMethodInner(), beucase when subclass wants to change access, + // it can do so inside visitMethodInner(). + val newAccess = updateAccessFlags(access, name, descriptor) + + return visitMethodInner(access, newName, descriptor, signature, exceptions, policy, + super.visitMethod(newAccess, newName, descriptor, signature, exceptions)) + } + } + + open fun updateAccessFlags( + access: Int, + name: String, + descriptor: String, + ): Int { + return access + } + + abstract fun visitMethodInner( + access: Int, + name: String, + descriptor: String, + signature: String?, + exceptions: Array<String>?, + policy: FilterPolicyWithReason, + superVisitor: MethodVisitor?, + ): MethodVisitor? + + companion object { + fun getVisitor( + classes: ClassNodes, + nextVisitor: ClassVisitor, + filter: OutputFilter, + forImpl: Boolean, + options: Options, + ): ClassVisitor { + var next = nextVisitor + + val verbosePrinter = PrintWriter(log.getVerbosePrintStream()) + + // TODO: This doesn't work yet. + + // Inject TraceClassVisitor for debugging. + if (options.enablePostTrace) { + next = TraceClassVisitor(next, verbosePrinter) + } + var ret: ClassVisitor + if (forImpl) { + ret = ImplGeneratingAdapter(classes, next, filter, options) + } else { + ret = StubGeneratingAdapter(classes, next, filter, options) + } + + // Inject TraceClassVisitor for debugging. + if (options.enablePreTrace) { + ret = TraceClassVisitor(ret, verbosePrinter) + } + return ret + } + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt new file mode 100644 index 000000000000..8250412b3717 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.visitors + +import org.objectweb.asm.AnnotationVisitor +import org.objectweb.asm.Attribute +import org.objectweb.asm.Handle +import org.objectweb.asm.Label +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes +import org.objectweb.asm.TypePath + +/** + * A method visitor that removes everything from method body. + * + * To inject a method body, override [visitCode] and create the opcodes there. + */ +abstract class BodyReplacingMethodVisitor( + access: Int, + name: String, + descriptor: String, + signature: String?, + exceptions: Array<String>?, + next: MethodVisitor?, +) : MethodVisitor(OPCODE_VERSION, next) { + val isVoid: Boolean + val isStatic: Boolean + + init { + isVoid = descriptor.endsWith(")V") + isStatic = access and Opcodes.ACC_STATIC != 0 + } + + // Following methods are for things that we need to keep. + // Since they're all calling the super method, we can just remove them, but we keep them + // just to clarify what we're keeping. + + final override fun visitParameter( + name: String?, + access: Int + ) { + super.visitParameter(name, access) + } + + final override fun visitAnnotationDefault(): AnnotationVisitor? { + return super.visitAnnotationDefault() + } + + final override fun visitAnnotation( + descriptor: String?, + visible: Boolean + ): AnnotationVisitor? { + return super.visitAnnotation(descriptor, visible) + } + + final override fun visitTypeAnnotation( + typeRef: Int, + typePath: TypePath?, + descriptor: String?, + visible: Boolean + ): AnnotationVisitor? { + return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible) + } + + final override fun visitAnnotableParameterCount( + parameterCount: Int, + visible: Boolean + ) { + super.visitAnnotableParameterCount(parameterCount, visible) + } + + final override fun visitParameterAnnotation( + parameter: Int, + descriptor: String?, + visible: Boolean + ): AnnotationVisitor? { + return super.visitParameterAnnotation(parameter, descriptor, visible) + } + + final override fun visitAttribute(attribute: Attribute?) { + super.visitAttribute(attribute) + } + + override fun visitEnd() { + super.visitEnd() + } + + /** + * Control when to emit the code. We use this to ignore all visitXxx method calls caused by + * the original method, so we'll remove all the original code. + * + * Only when visitXxx methods are called from [emitNewCode], we pass-through to the base class, + * so the body will be generated. + * + * (See also https://asm.ow2.io/asm4-guide.pdf section 3.2.1 about the MethovVisitor + * call order.) + */ + var emitCode = false + + final override fun visitCode() { + super.visitCode() + + try { + emitCode = true + + emitNewCode() + } finally { + emitCode = false + } + } + + /** + * Subclass must implement it and emit code, and call [visitMaxs] at the end. + */ + abstract fun emitNewCode() + + final override fun visitMaxs( + maxStack: Int, + maxLocals: Int + ) { + if (emitCode) { + super.visitMaxs(maxStack, maxLocals) + } + } + + // Following methods are called inside a method body, and we don't want to + // emit any of them, so they are all no-op. + + final override fun visitFrame( + type: Int, + numLocal: Int, + local: Array<out Any>?, + numStack: Int, + stack: Array<out Any>? + ) { + if (emitCode) { + super.visitFrame(type, numLocal, local, numStack, stack) + } + } + + final override fun visitInsn(opcode: Int) { + if (emitCode) { + super.visitInsn(opcode) + } + } + + final override fun visitIntInsn( + opcode: Int, + operand: Int + ) { + if (emitCode) { + super.visitIntInsn(opcode, operand) + } + } + + final override fun visitVarInsn( + opcode: Int, + varIndex: Int + ) { + if (emitCode) { + super.visitVarInsn(opcode, varIndex) + } + } + + final override fun visitTypeInsn( + opcode: Int, + type: String? + ) { + if (emitCode) { + super.visitTypeInsn(opcode, type) + } + } + + final override fun visitFieldInsn( + opcode: Int, + owner: String?, + name: String?, + descriptor: String? + ) { + if (emitCode) { + super.visitFieldInsn(opcode, owner, name, descriptor) + } + } + + final override fun visitMethodInsn( + opcode: Int, + owner: String?, + name: String?, + descriptor: String?, + isInterface: Boolean + ) { + if (emitCode) { + super.visitMethodInsn(opcode, owner, name, descriptor, isInterface) + } + } + + final override fun visitInvokeDynamicInsn( + name: String?, + descriptor: String?, + bootstrapMethodHandle: Handle?, + vararg bootstrapMethodArguments: Any? + ) { + if (emitCode) { + super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, + *bootstrapMethodArguments) + } + } + + final override fun visitJumpInsn( + opcode: Int, + label: Label? + ) { + if (emitCode) { + super.visitJumpInsn(opcode, label) + } + } + + final override fun visitLabel(label: Label?) { + if (emitCode) { + super.visitLabel(label) + } + } + + final override fun visitLdcInsn(value: Any?) { + if (emitCode) { + super.visitLdcInsn(value) + } + } + + final override fun visitIincInsn( + varIndex: Int, + increment: Int + ) { + if (emitCode) { + super.visitIincInsn(varIndex, increment) + } + } + + final override fun visitTableSwitchInsn( + min: Int, + max: Int, + dflt: Label?, + vararg labels: Label? + ) { + if (emitCode) { + super.visitTableSwitchInsn(min, max, dflt, *labels) + } + } + + final override fun visitLookupSwitchInsn( + dflt: Label?, + keys: IntArray?, + labels: Array<out Label>? + ) { + if (emitCode) { + super.visitLookupSwitchInsn(dflt, keys, labels) + } + } + + final override fun visitMultiANewArrayInsn( + descriptor: String?, + numDimensions: Int + ) { + if (emitCode) { + super.visitMultiANewArrayInsn(descriptor, numDimensions) + } + } + + final override fun visitInsnAnnotation( + typeRef: Int, + typePath: TypePath?, + descriptor: String?, + visible: Boolean + ): AnnotationVisitor? { + if (emitCode) { + return super.visitInsnAnnotation(typeRef, typePath, descriptor, visible) + } + return null + } + + final override fun visitTryCatchBlock( + start: Label?, + end: Label?, + handler: Label?, + type: String? + ) { + if (emitCode) { + super.visitTryCatchBlock(start, end, handler, type) + } + } + + final override fun visitTryCatchAnnotation( + typeRef: Int, + typePath: TypePath?, + descriptor: String?, + visible: Boolean + ): AnnotationVisitor? { + if (emitCode) { + return super.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible) + } + return null + } + + final override fun visitLocalVariable( + name: String?, + descriptor: String?, + signature: String?, + start: Label?, + end: Label?, + index: Int + ) { + if (emitCode) { + super.visitLocalVariable(name, descriptor, signature, start, end, index) + } + } + + final override fun visitLocalVariableAnnotation( + typeRef: Int, + typePath: TypePath?, + start: Array<out Label>?, + end: Array<out Label>?, + index: IntArray?, + descriptor: String?, + visible: Boolean + ): AnnotationVisitor? { + if (emitCode) { + return super.visitLocalVariableAnnotation( + typeRef, typePath, start, end, index, descriptor, visible) + } + return null + } + + final override fun visitLineNumber( + line: Int, + start: Label? + ) { + if (emitCode) { + super.visitLineNumber(line, start) + } + } +} diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt new file mode 100644 index 000000000000..ac068861ec8d --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.visitors + +import com.android.hoststubgen.asm.CLASS_INITIALIZER_DESC +import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME +import com.android.hoststubgen.asm.ClassNodes +import com.android.hoststubgen.asm.isVisibilityPrivateOrPackagePrivate +import com.android.hoststubgen.asm.writeByteCodeToPushArguments +import com.android.hoststubgen.asm.writeByteCodeToReturn +import com.android.hoststubgen.filters.FilterPolicy +import com.android.hoststubgen.filters.FilterPolicyWithReason +import com.android.hoststubgen.filters.OutputFilter +import com.android.hoststubgen.hosthelper.HostTestUtils +import com.android.hoststubgen.log +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes +import org.objectweb.asm.Type + +/** + * An adapter that generates the "impl" class file from an input class file. + */ +class ImplGeneratingAdapter( + classes: ClassNodes, + nextVisitor: ClassVisitor, + filter: OutputFilter, + options: Options, +) : BaseAdapter(classes, nextVisitor, filter, options) { + + override fun shouldEmit(policy: FilterPolicy): Boolean { + return policy.needsInImpl + } + + private var classLoadHookMethod: String? = null + + override fun visit( + version: Int, + access: Int, + name: String, + signature: String?, + superName: String?, + interfaces: Array<String> + ) { + super.visit(version, access, name, signature, superName, interfaces) + + classLoadHookMethod = filter.getClassLoadHook(currentClassName) + + // classLoadHookMethod is non-null, then we need to inject code to call it + // in the class initializer. + // If the target class already has a class initializer, then we need to inject code to it. + // Otherwise, we need to create one. + + classLoadHookMethod?.let { callback -> + log.d(" ClassLoadHook: $callback") + if (!classes.hasClassInitializer(currentClassName)) { + injectClassLoadHook(callback) + } + } + } + + private fun injectClassLoadHook(callback: String) { + writeRawMembers { + // Create a class initializer to call onClassLoaded(). + // Each class can only have at most one class initializer, but the base class + // StaticInitMerger will merge it with the existing one, if any. + visitMethod( + Opcodes.ACC_PRIVATE or Opcodes.ACC_STATIC, + "<clinit>", + "()V", + null, + null + )!!.let { mv -> + // Method prologue + mv.visitCode() + + writeClassLoadHookCall(mv) + mv.visitInsn(Opcodes.RETURN) + + // Method epilogue + mv.visitMaxs(0, 0) + mv.visitEnd() + } + } + } + + private fun writeClassLoadHookCall(mv: MethodVisitor) { + // First argument: the class type. + mv.visitLdcInsn(Type.getType("L" + currentClassName + ";")) + + // Second argument: method name + mv.visitLdcInsn(classLoadHookMethod) + + // Call HostTestUtils.onClassLoaded(). + mv.visitMethodInsn( + Opcodes.INVOKESTATIC, + HostTestUtils.CLASS_INTERNAL_NAME, + "onClassLoaded", + "(Ljava/lang/Class;Ljava/lang/String;)V", + false + ) + } + + override fun updateAccessFlags( + access: Int, + name: String, + descriptor: String, + ): Int { + if ((access and Opcodes.ACC_NATIVE) != 0 && nativeSubstitutionClass != null) { + return access and Opcodes.ACC_NATIVE.inv() + } + return access + } + + override fun visitMethodInner( + access: Int, + name: String, + descriptor: String, + signature: String?, + exceptions: Array<String>?, + policy: FilterPolicyWithReason, + superVisitor: MethodVisitor?, + ): MethodVisitor? { + // Inject method log, if needed. + var innerVisitor = superVisitor + + // If method logging is enabled, inject call to the logging method. + if (options.enableMethodLogging) { + innerVisitor = LogInjectingMethodAdapter( + access, + name, + descriptor, + signature, + exceptions, + innerVisitor, + ) + } + + // If this class already has a class initializer and a class load hook is needed, then + // we inject code. + if (classLoadHookMethod != null && + name == CLASS_INITIALIZER_NAME && + descriptor == CLASS_INITIALIZER_DESC) { + innerVisitor = ClassLoadHookInjectingMethodAdapter( + access, + name, + descriptor, + signature, + exceptions, + innerVisitor, + ) + } + + // If non-stub method call detection is enabled, then inject a call to the checker. + if (options.enableNonStubMethodCallDetection && doesMethodNeedNonStubCallCheck( + access, name, descriptor, policy) ) { + innerVisitor = NonStubMethodCallDetectingAdapter( + access, + name, + descriptor, + signature, + exceptions, + innerVisitor, + ) + } + + log.withIndent { + if ((access and Opcodes.ACC_NATIVE) != 0 && nativeSubstitutionClass != null) { + log.v("Rewriting native method...") + return NativeSubstitutingMethodAdapter( + access, name, descriptor, signature, exceptions, innerVisitor) + } + if (policy.policy == FilterPolicy.Throw) { + log.v("Making method throw...") + return ThrowingMethodAdapter( + access, name, descriptor, signature, exceptions, innerVisitor) + } + } + + return innerVisitor + } + + fun doesMethodNeedNonStubCallCheck( + access: Int, + name: String, + descriptor: String, + policy: FilterPolicyWithReason, + ): Boolean { + // If a method is in the stub, then no need to check. + if (policy.policy.needsInStub) { + return false + } + // If a method is private or package-private, no need to check. + // Technically test code can use framework package name, so it's a bit too lenient. + if (isVisibilityPrivateOrPackagePrivate(access)) { + return false + } + // TODO: If the method overrides a method that's accessible by tests, then we shouldn't + // do the check. (e.g. overrides a stub method or java standard method.) + + return true + } + + /** + * A method adapter that replaces the method body with a HostTestUtils.onThrowMethodCalled() + * call. + */ + private inner class ThrowingMethodAdapter( + access: Int, + val name: String, + descriptor: String, + signature: String?, + exceptions: Array<String>?, + next: MethodVisitor? + ) : BodyReplacingMethodVisitor(access, name, descriptor, signature, exceptions, next) { + override fun emitNewCode() { + visitMethodInsn(Opcodes.INVOKESTATIC, + HostTestUtils.CLASS_INTERNAL_NAME, + "onThrowMethodCalled", + "()V", + false) + + // We still need a RETURN opcode for the return type. + // For now, let's just inject a `throw`. + visitTypeInsn(Opcodes.NEW, "java/lang/RuntimeException") + visitInsn(Opcodes.DUP) + visitLdcInsn("Unreachable") + visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/RuntimeException", + "<init>", "(Ljava/lang/String;)V", false) + visitInsn(Opcodes.ATHROW) + + // visitMaxs(3, if (isStatic) 0 else 1) + visitMaxs(0, 0) // We let ASM figure them out. + } + } + + /** + * A method adapter that replaces a native method call with a call to the "native substitution" + * class. + */ + private inner class NativeSubstitutingMethodAdapter( + access: Int, + private val name: String, + private val descriptor: String, + signature: String?, + exceptions: Array<String>?, + next: MethodVisitor? + ) : MethodVisitor(OPCODE_VERSION, next) { + override fun visitCode() { + super.visitCode() + + throw RuntimeException("NativeSubstitutingMethodVisitor should be called on " + + " native method, where visitCode() shouldn't be called.") + } + + override fun visitEnd() { + writeByteCodeToPushArguments(descriptor, this) + + visitMethodInsn(Opcodes.INVOKESTATIC, + nativeSubstitutionClass, + name, + descriptor, + false) + + writeByteCodeToReturn(descriptor, this) + + visitMaxs(99, 0) // We let ASM figure them out. + super.visitEnd() + } + } + + /** + * A method adapter that injects a call to HostTestUtils.logMethodCall() to every method. + * + * Note, when the target method is a constructor, it may contain calls to `super(...)` or + * `this(...)`. The logging code will be injected *before* such calls. + */ + private inner class LogInjectingMethodAdapter( + access: Int, + val name: String, + val descriptor: String, + signature: String?, + exceptions: Array<String>?, + next: MethodVisitor? + ) : MethodVisitor(OPCODE_VERSION, next) { + override fun visitCode() { + super.visitCode() + visitLdcInsn(currentClassName) + visitLdcInsn(name) + visitLdcInsn(descriptor) + visitMethodInsn(Opcodes.INVOKESTATIC, + HostTestUtils.CLASS_INTERNAL_NAME, + "logMethodCall", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", + false) + } + } + + /** + * Inject a class load hook call. + */ + private inner class ClassLoadHookInjectingMethodAdapter( + access: Int, + val name: String, + val descriptor: String, + signature: String?, + exceptions: Array<String>?, + next: MethodVisitor? + ) : MethodVisitor(OPCODE_VERSION, next) { + override fun visitCode() { + super.visitCode() + + writeClassLoadHookCall(this) + } + } + + /** + * A method adapter that detects calls to non-stub methods. + */ + private inner class NonStubMethodCallDetectingAdapter( + access: Int, + val name: String, + val descriptor: String, + signature: String?, + exceptions: Array<String>?, + next: MethodVisitor? + ) : MethodVisitor(OPCODE_VERSION, next) { + override fun visitCode() { + super.visitCode() + + // First three arguments to HostTestUtils.onNonStubMethodCalled(). + visitLdcInsn(currentClassName) + visitLdcInsn(name) + visitLdcInsn(descriptor) + + // Call: HostTestUtils.getStackWalker().getCallerClass(). + // This push the caller Class in the stack. + visitMethodInsn(Opcodes.INVOKESTATIC, + HostTestUtils.CLASS_INTERNAL_NAME, + "getStackWalker", + "()Ljava/lang/StackWalker;", + false) + visitMethodInsn(Opcodes.INVOKEVIRTUAL, + "java/lang/StackWalker", + "getCallerClass", + "()Ljava/lang/Class;", + false) + + // Then call onNonStubMethodCalled(). + visitMethodInsn(Opcodes.INVOKESTATIC, + HostTestUtils.CLASS_INTERNAL_NAME, + "onNonStubMethodCalled", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V", + false) + } + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/StubGeneratingAdapter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/StubGeneratingAdapter.kt new file mode 100644 index 000000000000..37e2a888969d --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/StubGeneratingAdapter.kt @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.visitors + +import com.android.hoststubgen.asm.ClassNodes +import com.android.hoststubgen.filters.FilterPolicy +import com.android.hoststubgen.filters.FilterPolicyWithReason +import com.android.hoststubgen.filters.OutputFilter +import com.android.hoststubgen.log +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes + +/** + * An adapter that generates the "impl" class file from an input class file. + */ +class StubGeneratingAdapter( + classes: ClassNodes, + nextVisitor: ClassVisitor, + filter: OutputFilter, + options: Options, +) : BaseAdapter(classes, nextVisitor, filter, options) { + + override fun shouldEmit(policy: FilterPolicy): Boolean { + return policy.needsInStub + } + + override fun visitMethodInner( + access: Int, + name: String, + descriptor: String, + signature: String?, + exceptions: Array<String>?, + policy: FilterPolicyWithReason, + superVisitor: MethodVisitor?, + ): MethodVisitor? { + return StubMethodVisitor(access, name, descriptor, signature, exceptions, superVisitor) + } + + private inner class StubMethodVisitor( + access: Int, + val name: String, + descriptor: String, + signature: String?, + exceptions: Array<String>?, + next: MethodVisitor? + ) : BodyReplacingMethodVisitor(access, name, descriptor, signature, exceptions, next) { + override fun emitNewCode() { + log.d(" Generating stub method for $currentClassName.$name") + + // Inject the following code: + // throw new RuntimeException("Stub!"); + + /* + NEW java/lang/RuntimeException + DUP + LDC "not supported on host side" + INVOKESPECIAL java/lang/RuntimeException.<init> (Ljava/lang/String;)V + ATHROW + MAXSTACK = 3 + MAXLOCALS = 2 <- 1 for this, 1 for return value. + */ + visitTypeInsn(Opcodes.NEW, "java/lang/RuntimeException") + visitInsn(Opcodes.DUP) + visitLdcInsn("Stub!") + visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/RuntimeException", + "<init>", "(Ljava/lang/String;)V", false) + visitInsn(Opcodes.ATHROW) + visitMaxs(0, 0) // We let ASM figure them out. + } + } +} diff --git a/tools/hoststubgen/hoststubgen/test-framework/Android.bp b/tools/hoststubgen/hoststubgen/test-framework/Android.bp new file mode 100644 index 000000000000..2b91cc161b7f --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-framework/Android.bp @@ -0,0 +1,19 @@ +// Copyright (C) 2023 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. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +build = ["AndroidHostTest.bp"] diff --git a/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp b/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp new file mode 100644 index 000000000000..e7fb2debfc4e --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp @@ -0,0 +1,44 @@ +// Copyright (C) 2023 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. + +// Add `build = ["AndroidHostTest.bp"]` to Android.bp to include this file. + +// Compile the test jar, using 2 rules. +// 1. Build the test against the stub. +java_library_host { + name: "HostStubGenTest-framework-test-host-test-lib", + defaults: ["hosttest-with-framework-all-hidden-api-test-lib-defaults"], + srcs: [ + "src/**/*.java", + ], + static_libs: [ + "junit", + "truth-prebuilt", + "mockito", + + // http://cs/h/googleplex-android/platform/superproject/main/+/main:platform_testing/libraries/annotations/src/android/platform/test/annotations/ + "platform-test-annotations", + "hoststubgen-annotations", + ], +} + +// 2. Link the above module with necessary runtime dependencies, so it can be executed stand-alone. +java_test_host { + name: "HostStubGenTest-framework-all-test-host-test", + defaults: ["hosttest-with-framework-all-hidden-api-test-defaults"], + static_libs: [ + "HostStubGenTest-framework-test-host-test-lib", + ], + test_suites: ["general-tests"], +} diff --git a/tools/hoststubgen/hoststubgen/test-framework/AndroidTest-host.xml b/tools/hoststubgen/hoststubgen/test-framework/AndroidTest-host.xml new file mode 100644 index 000000000000..f35dcf69ecc9 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-framework/AndroidTest-host.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 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. +--> + +<!-- [Ravenwood] Copied from $ANDROID_BUILD_TOP/cts/hostsidetests/devicepolicy/AndroidTest.xml --> +<configuration description="CtsContentTestCases host-side test"> + <option name="test-suite-tag" value="ravenwood" /> + <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> + <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> + <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" /> + <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" /> + + <test class="com.android.tradefed.testtype.IsolatedHostTest" > + <option name="jar" value="HostStubGenTest-framework-all-test-host-test.jar" /> + </test> +</configuration> diff --git a/tools/hoststubgen/hoststubgen/test-framework/README.md b/tools/hoststubgen/hoststubgen/test-framework/README.md new file mode 100644 index 000000000000..20e2f873b152 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-framework/README.md @@ -0,0 +1,27 @@ +# HostStubGen: real framework test + +This directory contains tests against the actual framework.jar code. The tests were +copied from somewhere else in the android tree. We use this directory to quickly run existing +tests. + +## How to run + +- With `atest`. This is the proper way to run it, but it may fail due to atest's known problems. + + See the top level README.md on why `--no-bazel-mode` is needed (for now). + +``` +$ atest --no-bazel-mode HostStubGenTest-framework-test-host-test +``` + +- With `run-ravenwood-test` + +``` +$ run-ravenwood-test HostStubGenTest-framework-test-host-test +``` + +- Advanced option: `run-test-without-atest.sh` runs the test without using `atest` or `run-ravenwood-test` + +``` +$ ./run-test-without-atest.sh +```
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/test-framework/run-test-without-atest.sh b/tools/hoststubgen/hoststubgen/test-framework/run-test-without-atest.sh new file mode 100755 index 000000000000..cfc06a12e881 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-framework/run-test-without-atest.sh @@ -0,0 +1,85 @@ +#!/bin/bash +# Copyright (C) 2023 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. + +# Run HostStubGenTest-framework-test-host-test directly with JUnit. +# (without using atest.) + +source "${0%/*}"/../../common.sh + + +# Options: +# -v enable verbose log +# -d enable debugger + +verbose=0 +debug=0 +while getopts "vd" opt; do + case "$opt" in + v) verbose=1 ;; + d) debug=1 ;; + esac +done +shift $(($OPTIND - 1)) + + +if (( $verbose )) ; then + JAVA_OPTS="$JAVA_OPTS -verbose:class" +fi + +if (( $debug )) ; then + JAVA_OPTS="$JAVA_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8700" +fi + +#======================================= +module=HostStubGenTest-framework-all-test-host-test +module_jar=$ANDROID_BUILD_TOP/out/host/linux-x86/testcases/$module/$module.jar +run m $module + +out=out + +rm -fr $out +mkdir -p $out + + +# Copy and extract the relevant jar files so we can look into them. +run cp \ + $module_jar \ + $SOONG_INT/frameworks/base/tools/hoststubgen/hoststubgen/framework-all-hidden-api-host/linux_glibc_common/gen/*.jar \ + $out + +run extract $out/*.jar + +# Result is the number of failed tests. +result=0 + + +# This suite runs all tests in the JAR. +tests=(com.android.hoststubgen.hosthelper.HostTestSuite) + +# Uncomment this to run a specific test. +# tests=(com.android.hoststubgen.frameworktest.LogTest) + + +for class in ${tests[@]} ; do + echo "Running $class ..." + + run cd "${module_jar%/*}" + run $JAVA $JAVA_OPTS \ + -cp $module_jar \ + org.junit.runner.JUnitCore \ + $class || result=$(( $result + 1 )) +done + +exit $result diff --git a/tools/hoststubgen/hoststubgen/test-framework/src/com/android/hoststubgen/frameworktest/ArrayMapTest.java b/tools/hoststubgen/hoststubgen/test-framework/src/com/android/hoststubgen/frameworktest/ArrayMapTest.java new file mode 100644 index 000000000000..62bbf48955da --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-framework/src/com/android/hoststubgen/frameworktest/ArrayMapTest.java @@ -0,0 +1,737 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.frameworktest; + +// [ravewnwood] Copied from cts/, and commented out unsupported stuff. + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.ArrayMap; +import android.util.Log; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.AbstractMap; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.function.BiFunction; + +/** + * Some basic tests for {@link android.util.ArrayMap}. + */ +@RunWith(AndroidJUnit4.class) +public class ArrayMapTest { + static final boolean DEBUG = false; + + static final int OP_ADD = 1; + static final int OP_REM = 2; + + static int[] OPS = new int[]{ + OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, + OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, + OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, + OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, + + OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, + OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, + + OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, + OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, + + OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, + OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, + + OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, + OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, + OP_ADD, OP_ADD, OP_ADD, + OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, + OP_REM, OP_REM, OP_REM, + OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, + }; + + static int[] KEYS = new int[]{ + // General adding and removing. + -1, 1900, 600, 200, 1200, 1500, 1800, 100, 1900, + 2100, 300, 800, 600, 1100, 1300, 2000, 1000, 1400, + 600, -1, 1900, 600, 300, 2100, 200, 800, 800, + 1800, 1500, 1300, 1100, 2000, 1400, 1000, 1200, 1900, + + // Shrink when removing item from end. + 100, 200, 300, 400, 500, 600, 700, 800, 900, + 900, 800, 700, 600, 500, 400, 300, 200, 100, + + // Shrink when removing item from middle. + 100, 200, 300, 400, 500, 600, 700, 800, 900, + 900, 800, 700, 600, 500, 400, 200, 300, 100, + + // Shrink when removing item from front. + 100, 200, 300, 400, 500, 600, 700, 800, 900, + 900, 800, 700, 600, 500, 400, 100, 200, 300, + + // Test hash collisions. + 105, 106, 108, 104, 102, 102, 107, 5, 205, + 4, 202, 203, 3, 5, 101, 109, 200, 201, + 0, -1, 100, + 106, 108, 104, 102, 103, 105, 107, 101, 109, + -1, 100, 0, + 4, 5, 3, 5, 200, 203, 202, 201, 205, + }; + + public static class ControlledHash implements Parcelable { + final int mValue; + + ControlledHash(int value) { + mValue = value; + } + + @Override + public final boolean equals(Object o) { + if (o == null) { + return false; + } + return mValue == ((ControlledHash)o).mValue; + } + + @Override + public final int hashCode() { + return mValue/100; + } + + @Override + public final String toString() { + return Integer.toString(mValue); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mValue); + } + + public static final Parcelable.Creator<ControlledHash> CREATOR + = new Parcelable.Creator<ControlledHash>() { + public ControlledHash createFromParcel(Parcel in) { + return new ControlledHash(in.readInt()); + } + + public ControlledHash[] newArray(int size) { + return new ControlledHash[size]; + } + }; + } + + private static boolean compare(Object v1, Object v2) { + if (v1 == null) { + return v2 == null; + } + if (v2 == null) { + return false; + } + return v1.equals(v2); + } + + private static void compareMaps(HashMap map, ArrayMap array) { + if (map.size() != array.size()) { + fail("Bad size: expected " + map.size() + ", got " + array.size()); + } + + Set<Entry> mapSet = map.entrySet(); + for (Map.Entry entry : mapSet) { + Object expValue = entry.getValue(); + Object gotValue = array.get(entry.getKey()); + if (!compare(expValue, gotValue)) { + fail("Bad value: expected " + expValue + ", got " + gotValue + + " at key " + entry.getKey()); + } + } + + for (int i = 0; i < array.size(); i++) { + Object gotValue = array.valueAt(i); + Object key = array.keyAt(i); + Object expValue = map.get(key); + if (!compare(expValue, gotValue)) { + fail("Bad value: expected " + expValue + ", got " + gotValue + + " at key " + key); + } + } + + if (map.entrySet().hashCode() != array.entrySet().hashCode()) { + fail("Entry set hash codes differ: map=0x" + + Integer.toHexString(map.entrySet().hashCode()) + " array=0x" + + Integer.toHexString(array.entrySet().hashCode())); + } + + if (!map.entrySet().equals(array.entrySet())) { + fail("Failed calling equals on map entry set against array set"); + } + + if (!array.entrySet().equals(map.entrySet())) { + fail("Failed calling equals on array entry set against map set"); + } + + if (map.keySet().hashCode() != array.keySet().hashCode()) { + fail("Key set hash codes differ: map=0x" + + Integer.toHexString(map.keySet().hashCode()) + " array=0x" + + Integer.toHexString(array.keySet().hashCode())); + } + + if (!map.keySet().equals(array.keySet())) { + fail("Failed calling equals on map key set against array set"); + } + + if (!array.keySet().equals(map.keySet())) { + fail("Failed calling equals on array key set against map set"); + } + + if (!map.keySet().containsAll(array.keySet())) { + fail("Failed map key set contains all of array key set"); + } + + if (!array.keySet().containsAll(map.keySet())) { + fail("Failed array key set contains all of map key set"); + } + + if (!array.containsAll(map.keySet())) { + fail("Failed array contains all of map key set"); + } + + if (!map.entrySet().containsAll(array.entrySet())) { + fail("Failed map entry set contains all of array entry set"); + } + + if (!array.entrySet().containsAll(map.entrySet())) { + fail("Failed array entry set contains all of map entry set"); + } + } + + private static void validateArrayMap(ArrayMap array) { + Set<Map.Entry> entrySet = array.entrySet(); + int index = 0; + Iterator<Entry> entryIt = entrySet.iterator(); + while (entryIt.hasNext()) { + Map.Entry entry = entryIt.next(); + Object value = entry.getKey(); + Object realValue = array.keyAt(index); + if (!compare(realValue, value)) { + fail("Bad array map entry set: expected key " + realValue + + ", got " + value + " at index " + index); + } + value = entry.getValue(); + realValue = array.valueAt(index); + if (!compare(realValue, value)) { + fail("Bad array map entry set: expected value " + realValue + + ", got " + value + " at index " + index); + } + index++; + } + + index = 0; + Set keySet = array.keySet(); + Iterator keyIt = keySet.iterator(); + while (keyIt.hasNext()) { + Object value = keyIt.next(); + Object realValue = array.keyAt(index); + if (!compare(realValue, value)) { + fail("Bad array map key set: expected key " + realValue + + ", got " + value + " at index " + index); + } + index++; + } + + index = 0; + Collection valueCol = array.values(); + Iterator valueIt = valueCol.iterator(); + while (valueIt.hasNext()) { + Object value = valueIt.next(); + Object realValue = array.valueAt(index); + if (!compare(realValue, value)) { + fail("Bad array map value col: expected value " + realValue + + ", got " + value + " at index " + index); + } + index++; + } + } + + private static void compareBundles(Bundle bundle1, Bundle bundle2) { + Set<String> keySet1 = bundle1.keySet(); + Iterator<String> iterator1 = keySet1.iterator(); + while (iterator1.hasNext()) { + String key = iterator1.next(); + int value1 = bundle1.getInt(key); + if (bundle2.get(key) == null) { + fail("Bad Bundle: bundle2 didn't have expected key " + key); + } + int value2 = bundle2.getInt(key); + if (value1 != value2) { + fail("Bad Bundle: at key key " + key + " expected " + value1 + ", got " + value2); + } + } + Set<String> keySet2 = bundle2.keySet(); + Iterator<String> iterator2 = keySet2.iterator(); + while (iterator2.hasNext()) { + String key = iterator2.next(); + if (bundle1.get(key) == null) { + fail("Bad Bundle: bundle1 didn't have expected key " + key); + } + int value1 = bundle1.getInt(key); + int value2 = bundle2.getInt(key); + if (value1 != value2) { + fail("Bad Bundle: at key key " + key + " expected " + value1 + ", got " + value2); + } + } + } + + private static void dump(Map map, ArrayMap array) { + Log.e("test", "HashMap of " + map.size() + " entries:"); + Set<Map.Entry> mapSet = map.entrySet(); + for (Map.Entry entry : mapSet) { + Log.e("test", " " + entry.getKey() + " -> " + entry.getValue()); + } + Log.e("test", "ArrayMap of " + array.size() + " entries:"); + for (int i = 0; i < array.size(); i++) { + Log.e("test", " " + array.keyAt(i) + " -> " + array.valueAt(i)); + } + } + + private static void dump(ArrayMap map1, ArrayMap map2) { + Log.e("test", "ArrayMap of " + map1.size() + " entries:"); + for (int i = 0; i < map1.size(); i++) { + Log.e("test", " " + map1.keyAt(i) + " -> " + map1.valueAt(i)); + } + Log.e("test", "ArrayMap of " + map2.size() + " entries:"); + for (int i = 0; i < map2.size(); i++) { + Log.e("test", " " + map2.keyAt(i) + " -> " + map2.valueAt(i)); + } + } + + private static void dump(Bundle bundle1, Bundle bundle2) { + Log.e("test", "First Bundle of " + bundle1.size() + " entries:"); + Set<String> keys1 = bundle1.keySet(); + for (String key : keys1) { + Log.e("test", " " + key + " -> " + bundle1.get(key)); + } + Log.e("test", "Second Bundle of " + bundle2.size() + " entries:"); + Set<String> keys2 = bundle2.keySet(); + for (String key : keys2) { + Log.e("test", " " + key + " -> " + bundle2.get(key)); + } + } + + @Test + public void testBasicArrayMap() { + HashMap<ControlledHash, Integer> hashMap = new HashMap<>(); + ArrayMap<ControlledHash, Integer> arrayMap = new ArrayMap<>(); + Bundle bundle = new Bundle(); + + for (int i = 0; i < OPS.length; i++) { + Integer oldHash; + Integer oldArray; + ControlledHash key = KEYS[i] < 0 ? null : new ControlledHash(KEYS[i]); + String strKey = KEYS[i] < 0 ? null : Integer.toString(KEYS[i]); + switch (OPS[i]) { + case OP_ADD: + if (DEBUG) Log.i("test", "Adding key: " + key); + oldHash = hashMap.put(key, i); + oldArray = arrayMap.put(key, i); + bundle.putInt(strKey, i); + break; + case OP_REM: + if (DEBUG) Log.i("test", "Removing key: " + key); + oldHash = hashMap.remove(key); + oldArray = arrayMap.remove(key); + bundle.remove(strKey); + break; + default: + fail("Bad operation " + OPS[i] + " @ " + i); + return; + } + if (!compare(oldHash, oldArray)) { + String msg = "Bad result: expected " + oldHash + ", got " + oldArray; + Log.e("test", msg); + dump(hashMap, arrayMap); + fail(msg); + } + try { + validateArrayMap(arrayMap); + } catch (Throwable e) { + Log.e("test", e.getMessage()); + dump(hashMap, arrayMap); + throw e; + } + try { + compareMaps(hashMap, arrayMap); + } catch (Throwable e) { + Log.e("test", e.getMessage()); + dump(hashMap, arrayMap); + throw e; + } + Parcel parcel = Parcel.obtain(); + bundle.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + Bundle bundle2 = parcel.readBundle(); + try { + compareBundles(bundle, bundle2); + } catch (Throwable e) { + Log.e("test", e.getMessage()); + dump(bundle, bundle2); + throw e; + } + } + + arrayMap.put(new ControlledHash(50000), 100); + ControlledHash lookup = new ControlledHash(50000); + Iterator<ControlledHash> it = arrayMap.keySet().iterator(); + while (it.hasNext()) { + if (it.next().equals(lookup)) { + it.remove(); + } + } + if (arrayMap.containsKey(lookup)) { + String msg = "Bad map iterator: didn't remove test key"; + Log.e("test", msg); + dump(hashMap, arrayMap); + fail(msg); + } + } + + @Test + public void testCopyArrayMap() { + // map copy constructor test + ArrayMap newMap = new ArrayMap<Integer, String>(); + for (int i = 0; i < 10; ++i) { + newMap.put(i, String.valueOf(i)); + } + ArrayMap mapCopy = new ArrayMap(newMap); + if (!compare(mapCopy, newMap)) { + String msg = "ArrayMap copy constructor failure: expected " + + newMap + ", got " + mapCopy; + Log.e("test", msg); + dump(newMap, mapCopy); + fail(msg); + return; + } + } + + @Test + public void testEqualsArrayMap() { + ArrayMap<Integer, String> map1 = new ArrayMap<>(); + ArrayMap<Integer, String> map2 = new ArrayMap<>(); + HashMap<Integer, String> map3 = new HashMap<>(); + if (!compare(map1, map2) || !compare(map1, map3) || !compare(map3, map2)) { + fail("ArrayMap equals failure for empty maps " + map1 + ", " + + map2 + ", " + map3); + } + + for (int i = 0; i < 10; ++i) { + String value = String.valueOf(i); + map1.put(i, value); + map2.put(i, value); + map3.put(i, value); + } + if (!compare(map1, map2) || !compare(map1, map3) || !compare(map3, map2)) { + fail("ArrayMap equals failure for populated maps " + map1 + ", " + + map2 + ", " + map3); + } + + map1.remove(0); + if (compare(map1, map2) || compare(map1, map3) || compare(map3, map1)) { + fail("ArrayMap equals failure for map size " + map1 + ", " + + map2 + ", " + map3); + } + + map1.put(0, "-1"); + if (compare(map1, map2) || compare(map1, map3) || compare(map3, map1)) { + fail("ArrayMap equals failure for map contents " + map1 + ", " + + map2 + ", " + map3); + } + } + +// /** +// * Test creating a malformed array map with duplicated keys and that we will catch this when +// * unparcelling. +// */ +// @Test +// public void testDuplicateKeys() throws NoSuchMethodException, +// InvocationTargetException, IllegalAccessException, NoSuchFieldException { +// ArrayMap<String, Object> map1 = new ArrayMap(2); +// +// Method appendMethod = ArrayMap.class.getMethod("append", Object.class, Object.class); +// appendMethod.invoke(map1, Integer.toString(100000), "foo"); +// appendMethod.invoke(map1, Integer.toString(100000), "bar"); +// +// // Now parcel/unparcel, and verify we get the expected error. +// Parcel parcel = Parcel.obtain(); +// Method writeArrayMapMethod = Parcel.class.getMethod("writeArrayMap", ArrayMap.class); +// writeArrayMapMethod.invoke(parcel, map1); +// parcel.setDataPosition(0); +// ArrayMap<String, Object> map2 = new ArrayMap(2); +// +// try { +// Parcel.class.getMethod("readArrayMap", ArrayMap.class, ClassLoader.class).invoke( +// parcel, map2, null); +// } catch (InvocationTargetException e) { +// Throwable cause = e.getCause(); +// if (cause instanceof IllegalArgumentException) { +// // Good! +// return; +// } +// throw e; +// } +// +// String msg = "Didn't throw expected IllegalArgumentException"; +// Log.e("test", msg); +// dump(map1, map2); +// fail(msg); +// } + + private static void checkEntrySetToArray(ArrayMap<?, ?> testMap) { + try { + testMap.entrySet().toArray(); + fail(); + } catch (UnsupportedOperationException expected) { + } + + try { + Map.Entry<?, ?>[] entries = new Map.Entry[20]; + testMap.entrySet().toArray(entries); + fail(); + } catch (UnsupportedOperationException expected) { + } + } + + // http://b/32294038, Test ArrayMap.entrySet().toArray() + @Test + public void testEntrySetArray() { + // Create + ArrayMap<Integer, String> testMap = new ArrayMap<>(); + + // Test empty + checkEntrySetToArray(testMap); + + // Test non-empty + for (int i = 0; i < 10; ++i) { + testMap.put(i, String.valueOf(i)); + } + checkEntrySetToArray(testMap); + } + + @Test + public void testCanNotIteratePastEnd_entrySetIterator() { + Map<String, String> map = new ArrayMap<>(); + map.put("key 1", "value 1"); + map.put("key 2", "value 2"); + Set<Map.Entry<String, String>> expectedEntriesToIterate = new HashSet<>(Arrays.asList( + entryOf("key 1", "value 1"), + entryOf("key 2", "value 2") + )); + Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator(); + + // Assert iteration over the expected two entries in any order + assertTrue(iterator.hasNext()); + Map.Entry<String, String> firstEntry = copyOf(iterator.next()); + assertTrue(expectedEntriesToIterate.remove(firstEntry)); + + assertTrue(iterator.hasNext()); + Map.Entry<String, String> secondEntry = copyOf(iterator.next()); + assertTrue(expectedEntriesToIterate.remove(secondEntry)); + + assertFalse(iterator.hasNext()); + + try { + iterator.next(); + fail(); + } catch (NoSuchElementException expected) { + } + } + + private static <K, V> Map.Entry<K, V> entryOf(K key, V value) { + return new AbstractMap.SimpleEntry<>(key, value); + } + + private static <K, V> Map.Entry<K, V> copyOf(Map.Entry<K, V> entry) { + return entryOf(entry.getKey(), entry.getValue()); + } + + @Test + public void testCanNotIteratePastEnd_keySetIterator() { + Map<String, String> map = new ArrayMap<>(); + map.put("key 1", "value 1"); + map.put("key 2", "value 2"); + Set<String> expectedKeysToIterate = new HashSet<>(Arrays.asList("key 1", "key 2")); + Iterator<String> iterator = map.keySet().iterator(); + + // Assert iteration over the expected two keys in any order + assertTrue(iterator.hasNext()); + String firstKey = iterator.next(); + assertTrue(expectedKeysToIterate.remove(firstKey)); + + assertTrue(iterator.hasNext()); + String secondKey = iterator.next(); + assertTrue(expectedKeysToIterate.remove(secondKey)); + + assertFalse(iterator.hasNext()); + + try { + iterator.next(); + fail(); + } catch (NoSuchElementException expected) { + } + } + + @Test + public void testCanNotIteratePastEnd_valuesIterator() { + Map<String, String> map = new ArrayMap<>(); + map.put("key 1", "value 1"); + map.put("key 2", "value 2"); + Set<String> expectedValuesToIterate = new HashSet<>(Arrays.asList("value 1", "value 2")); + Iterator<String> iterator = map.values().iterator(); + + // Assert iteration over the expected two values in any order + assertTrue(iterator.hasNext()); + String firstValue = iterator.next(); + assertTrue(expectedValuesToIterate.remove(firstValue)); + + assertTrue(iterator.hasNext()); + String secondValue = iterator.next(); + assertTrue(expectedValuesToIterate.remove(secondValue)); + + assertFalse(iterator.hasNext()); + + try { + iterator.next(); + fail(); + } catch (NoSuchElementException expected) { + } + } + + @Test + public void testForEach() { + ArrayMap<String, Integer> map = new ArrayMap<>(); + + for (int i = 0; i < 50; ++i) { + map.put(Integer.toString(i), i * 10); + } + + // Make sure forEach goes through all of the elements. + HashMap<String, Integer> seen = new HashMap<>(); + map.forEach(seen::put); + compareMaps(seen, map); + } + + /** + * The entrySet Iterator returns itself from each call to {@code next()}. This is unusual + * behavior for {@link Iterator#next()}; this test ensures that any future change to this + * behavior is deliberate. + */ + @Test + public void testUnusualBehavior_eachEntryIsSameAsIterator_entrySetIterator() { + Map<String, String> map = new ArrayMap<>(); + map.put("key 1", "value 1"); + map.put("key 2", "value 2"); + Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator(); + + assertSame(iterator, iterator.next()); + assertSame(iterator, iterator.next()); + } + + @SuppressWarnings("SelfEquals") + @Test + public void testUnusualBehavior_equalsThrowsAfterRemove_entrySetIterator() { + Map<String, String> map = new ArrayMap<>(); + map.put("key 1", "value 1"); + map.put("key 2", "value 2"); + Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator(); + iterator.next(); + iterator.remove(); + try { + iterator.equals(iterator); + fail(); + } catch (IllegalStateException expected) { + } + } + + private static <T> void assertEqualsBothWays(T a, T b) { + assertEquals(a, b); + assertEquals(b, a); + assertEquals(a.hashCode(), b.hashCode()); + } + + @Test + public void testRemoveAll() { + final ArrayMap<Integer, String> map = new ArrayMap<>(); + for (Integer i : Arrays.asList(0, 1, 2, 3, 4, 5)) { + map.put(i, i.toString()); + } + + final ArrayMap<Integer, String> expectedMap = new ArrayMap<>(); + for (Integer i : Arrays.asList(2, 4)) { + expectedMap.put(i, String.valueOf(i)); + } + map.removeAll(Arrays.asList(0, 1, 3, 5, 6)); + if (!compare(map, expectedMap)) { + fail("ArrayMap removeAll failure, expect " + expectedMap + ", but " + map); + } + + map.removeAll(Collections.emptyList()); + if (!compare(map, expectedMap)) { + fail("ArrayMap removeAll failure for empty maps, expect " + expectedMap + ", but " + + map); + } + + map.removeAll(Arrays.asList(2, 4)); + if (!map.isEmpty()) { + fail("ArrayMap removeAll failure, expect empty, but " + map); + } + } + + @Test + public void testReplaceAll() { + final ArrayMap<Integer, Integer> map = new ArrayMap<>(); + final ArrayMap<Integer, Integer> expectedMap = new ArrayMap<>(); + final BiFunction<Integer, Integer, Integer> function = (k, v) -> 2 * v; + for (Integer i : Arrays.asList(0, 1, 2, 3, 4, 5)) { + map.put(i, i); + expectedMap.put(i, 2 * i); + } + + map.replaceAll(function); + if (!compare(map, expectedMap)) { + fail("ArrayMap replaceAll failure, expect " + expectedMap + ", but " + map); + } + } +}
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/test-framework/src/com/android/hoststubgen/frameworktest/LogTest.java b/tools/hoststubgen/hoststubgen/test-framework/src/com/android/hoststubgen/frameworktest/LogTest.java new file mode 100644 index 000000000000..56544b4d5151 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-framework/src/com/android/hoststubgen/frameworktest/LogTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.frameworktest; + +import static com.google.common.truth.Truth.assertThat; + +import android.util.Log; +import android.util.Slog; + +import org.junit.Test; + +/** + * Some basic tests for {@link android.util.Log}. + */ +public class LogTest { + @Test + public void testBasicLogging() { + Log.v("TAG", "Test v log"); + Log.d("TAG", "Test d log"); + Log.i("TAG", "Test i log"); + Log.w("TAG", "Test w log"); + Log.e("TAG", "Test e log"); + + Slog.v("TAG", "Test v slog"); + Slog.d("TAG", "Test d slog"); + Slog.i("TAG", "Test i slog"); + Slog.w("TAG", "Test w slog"); + Slog.e("TAG", "Test e slog"); + } + + @Test + public void testNativeMethods() { + assertThat(Log.isLoggable("mytag", Log.INFO)).isTrue(); + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp new file mode 100644 index 000000000000..8c76a612020f --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp @@ -0,0 +1,141 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +// A library that simulates framework-all.jar +java_library { + name: "hoststubgen-test-tiny-framework", + installable: true, + host_supported: true, + srcs: ["tiny-framework/src/**/*.java"], + static_libs: [ + "hoststubgen-annotations", + ], +} + +// Create stub/impl jars from "hoststubgen-test-tiny-framework", using the following 3 rules. +java_genrule_host { + name: "hoststubgen-test-tiny-framework-host", + defaults: ["hoststubgen-command-defaults"], + cmd: hoststubgen_common_options + + "--in-jar $(location :hoststubgen-test-tiny-framework) " + + "--policy-override-file $(location policy-override-tiny-framework.txt) ", + srcs: [ + ":hoststubgen-test-tiny-framework", + "policy-override-tiny-framework.txt", + ], +} + +java_genrule_host { + name: "hoststubgen-test-tiny-framework-host-stub", + cmd: "cp $(in) $(out)", + srcs: [ + ":hoststubgen-test-tiny-framework-host{host_stub.jar}", + ], + out: [ + "host_stub.jar", + ], +} + +java_genrule_host { + name: "hoststubgen-test-tiny-framework-host-impl", + cmd: "cp $(in) $(out)", + srcs: [ + ":hoststubgen-test-tiny-framework-host{host_impl.jar}", + ], + out: [ + "host_impl.jar", + ], +} + +// Compile the test jar, using 2 rules. +// 1. Build the test against the stub. +java_library_host { + name: "hoststubgen-test-tiny-test-lib", + srcs: ["tiny-test/src/**/*.java"], + + libs: [ + "hoststubgen-test-tiny-framework-host-stub", + ], + static_libs: [ + "junit", + "truth-prebuilt", + + // http://cs/h/googleplex-android/platform/superproject/main/+/main:platform_testing/libraries/annotations/src/android/platform/test/annotations/ + "platform-test-annotations", + ], + visibility: ["//visibility:private"], +} + +// 2. Link "hoststubgen-test-tiny-test-lib" with necessary runtime dependencies, so it can be +// executed stand-alone. +java_test_host { + name: "hoststubgen-test-tiny-test", + test_config: "AndroidTest-host.xml", + static_libs: [ + "hoststubgen-test-tiny-test-lib", + "hoststubgen-helper-runtime", + "hoststubgen-test-tiny-framework-host-impl", + ], + test_suites: ["general-tests"], +} + +// Dump the original, stub and impl jars as text files. +// We use them in test-and-update-golden.sh. +java_genrule_host { + name: "hoststubgen-test-tiny-framework-orig-dump", + defaults: ["hoststubgen-jar-dump-defaults"], + srcs: [ + ":hoststubgen-test-tiny-framework", + ], + out: [ + "01-hoststubgen-test-tiny-framework-orig-dump.txt", + ], + visibility: ["//visibility:private"], +} + +java_genrule_host { + name: "hoststubgen-test-tiny-framework-host-stub-dump", + defaults: ["hoststubgen-jar-dump-defaults"], + srcs: [ + ":hoststubgen-test-tiny-framework-host-stub", + ], + out: [ + "02-hoststubgen-test-tiny-framework-host-stub-dump.txt", + ], + visibility: ["//visibility:private"], +} + +java_genrule_host { + name: "hoststubgen-test-tiny-framework-host-impl-dump", + defaults: ["hoststubgen-jar-dump-defaults"], + srcs: [ + ":hoststubgen-test-tiny-framework-host-impl", + ], + out: [ + "03-hoststubgen-test-tiny-framework-host-impl-dump.txt", + ], + visibility: ["//visibility:private"], +} + +// Run it with `atest`. Compare the dump of the jar files to the golden output. +python_test_host { + name: "tiny-framework-dump-test", + srcs: [ + "tiny-framework-dump-test.py", + ], + data: [ + "golden-output/*.txt", + ], + java_data: [ + "hoststubgen-test-tiny-framework-host-stub-dump", + "hoststubgen-test-tiny-framework-host-impl-dump", + "hoststubgen-test-tiny-framework-orig-dump", + ], + test_suites: ["general-tests"], +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/AndroidTest-host.xml b/tools/hoststubgen/hoststubgen/test-tiny-framework/AndroidTest-host.xml new file mode 100644 index 000000000000..84aad69c33bc --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/AndroidTest-host.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 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. +--> + +<!-- [Ravenwood] Copied from $ANDROID_BUILD_TOP/cts/hostsidetests/devicepolicy/AndroidTest.xml --> +<configuration description="HostStubGen sample test"> + <option name="test-suite-tag" value="ravenwood" /> + <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> + <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> + <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" /> + <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" /> + + <test class="com.android.tradefed.testtype.IsolatedHostTest" > + <option name="jar" value="hoststubgen-test-tiny-test.jar" /> + </test> +</configuration> diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md b/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md new file mode 100644 index 000000000000..f3c0450d42a3 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md @@ -0,0 +1,27 @@ +# HostStubGen: tiny-framework test + +This directory contains a small classes that "simulates" framework.jar, and tests against it. + +This test doesn't use the actual android framework code. + +## How to run + +- With `atest`. This is the proper way to run it, but `atest` has known problems that may + affect the result. If you see weird problems, try the next `run-ravenwood-test` command. + +``` +$ atest hoststubgen-test-tiny-test +``` + +- With `run-ravenwood-test` should work too. This is the proper way to run it. + +``` +$ run-ravenwood-test hoststubgen-test-tiny-test +``` + +- `run-test-manually.sh` also run the test, but it builds the stub/impl jars and the test without + using the build system. This is useful for debugging the tool. + +``` +$ ./run-test-manually.sh +```
\ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh b/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh new file mode 100755 index 000000000000..4d588694b8da --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh @@ -0,0 +1,134 @@ +#!/bin/bash +# Copyright (C) 2023 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. + +help() { + cat <<'EOF' + + diff-and-update-golden.sh [OPTIONS] + + Compare the generated jar files from tiny-framework to the "golden" files. + + OPTIONS: + -u: Update the golden files. + + -3: Run `meld` to compare original, stub and impl jar files in 3-way diff. + This is useful to visualize the exact differences between 3 jar files. + + -2: Run `meld` to compare original <-> impl, and impl <-> stub as two different diffs. +EOF +} + +source "${0%/*}"/../../common.sh + +SCRIPT_NAME="${0##*/}" + +GOLDEN_DIR=golden-output +mkdir -p $GOLDEN_DIR + +DIFF_CMD=${DIFF:-diff -u --ignore-blank-lines --ignore-space-change} + +update=0 +three_way=0 +two_way=0 +while getopts "u32" opt; do +case "$opt" in + u) + update=1 + ;; + 3) + three_way=1 + ;; + 2) + two_way=1 + ;; + '?') + help + exit 1 + ;; +esac +done +shift $(($OPTIND - 1)) + + +# Build the dump files, which are the input of this test. +run m tiny-framework-dump-test + + +# Get the path to the generate text files. (not the golden files.) +# We get them from $OUT/module-info.json + +files=( +$(python3 -c ' +import sys +import os +import json + +with open(sys.argv[1], "r") as f: + data = json.load(f) + + # Equivalent to: jq -r '.["tiny-framework-dump-test"]["installed"][]' + for path in data["tiny-framework-dump-test"]["installed"]: + + if "golden-output" in path: + continue + if path.endswith(".txt"): + print(os.getenv("ANDROID_BUILD_TOP") + "/" + path) +' $OUT/module-info.json) +) + +# Next, compare each file and update them in $GOLDEN_DIR + +any_file_changed=0 + +for file in ${files[*]} ; do + name=$(basename $file) + echo "# Checking $name ..." + + file_changed=0 + if run $DIFF_CMD $GOLDEN_DIR/$name $file; then + : # No diff + else + file_changed=1 + any_file_changed=1 + fi + + if (( $update && $file_changed )) ; then + echo "# Updating $name ..." + run cp $file $GOLDEN_DIR/$name + fi +done + +if (( $three_way )) ; then + echo "# Running 3-way diff with meld..." + run meld ${files[*]} & +fi + +if (( $two_way )) ; then + echo "# Running meld..." + run meld --diff ${files[0]} ${files[1]} --diff ${files[1]} ${files[2]} +fi + +if (( $any_file_changed == 0 )) ; then + echo "$SCRIPT_NAME: Success: no changes detected." + exit 0 +else + if (( $update )) ; then + echo "$SCRIPT_NAME: Warning: golden files have been updated." + exit 2 + else + echo "$SCRIPT_NAME: Failure: changes detected. See above diff for the details." + exit 3 + fi +fi diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt new file mode 100644 index 000000000000..1aa485963f97 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt @@ -0,0 +1,1671 @@ +## Class: android/hosttest/annotation/HostSideTestClassLoadHook.class + Compiled from "HostSideTestClassLoadHook.java" +public interface android.hosttest.annotation.HostSideTestClassLoadHook extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestClassLoadHook + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + public abstract java.lang.String value(); + descriptor: ()Ljava/lang/String; + flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT +} +SourceFile: "HostSideTestClassLoadHook.java" +RuntimeVisibleAnnotations: + 0: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE] + ) + 1: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestKeep.class + Compiled from "HostSideTestKeep.java" +public interface android.hosttest.annotation.HostSideTestKeep extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestKeep + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestKeep.java" +RuntimeVisibleAnnotations: + 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + 1: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestNativeSubstitutionClass.class + Compiled from "HostSideTestNativeSubstitutionClass.java" +public interface android.hosttest.annotation.HostSideTestNativeSubstitutionClass extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestNativeSubstitutionClass + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + public abstract java.lang.String value(); + descriptor: ()Ljava/lang/String; + flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT +} +SourceFile: "HostSideTestNativeSubstitutionClass.java" +RuntimeVisibleAnnotations: + 0: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE] + ) + 1: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestRemove.class + Compiled from "HostSideTestRemove.java" +public interface android.hosttest.annotation.HostSideTestRemove extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestRemove + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestRemove.java" +RuntimeVisibleAnnotations: + 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + 1: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestStub.class + Compiled from "HostSideTestStub.java" +public interface android.hosttest.annotation.HostSideTestStub extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestStub + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestStub.java" +RuntimeVisibleAnnotations: + 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + 1: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestSubstitute.class + Compiled from "HostSideTestSubstitute.java" +public interface android.hosttest.annotation.HostSideTestSubstitute extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestSubstitute + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + public abstract java.lang.String suffix(); + descriptor: ()Ljava/lang/String; + flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT +} +SourceFile: "HostSideTestSubstitute.java" +RuntimeVisibleAnnotations: + 0: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.METHOD] + ) + 1: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestThrow.class + Compiled from "HostSideTestThrow.java" +public interface android.hosttest.annotation.HostSideTestThrow extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestThrow + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestThrow.java" +RuntimeVisibleAnnotations: + 0: #x(#x=[e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + 1: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestWholeClassKeep.class + Compiled from "HostSideTestWholeClassKeep.java" +public interface android.hosttest.annotation.HostSideTestWholeClassKeep extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestWholeClassKeep + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestWholeClassKeep.java" +RuntimeVisibleAnnotations: + 0: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE] + ) + 1: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestWholeClassStub.class + Compiled from "HostSideTestWholeClassStub.java" +public interface android.hosttest.annotation.HostSideTestWholeClassStub extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestWholeClassStub + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestWholeClassStub.java" +RuntimeVisibleAnnotations: + 0: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE] + ) + 1: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/tests/HostSideTestSuppress.class + Compiled from "HostSideTestSuppress.java" +public interface android.hosttest.annotation.tests.HostSideTestSuppress extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/tests/HostSideTestSuppress + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestSuppress.java" +RuntimeVisibleAnnotations: + 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD] + ) +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class + Compiled from "TinyFrameworkCallerCheck.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 3 + private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); + descriptor: ()V + flags: (0x0002) ACC_PRIVATE + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl; + + public static int getOneKeep(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + 0: iconst_1 + 1: ireturn + LineNumberTable: + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestKeep + + public static int getOneStub(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + 0: iconst_1 + 1: ireturn + LineNumberTable: + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub +} +SourceFile: "TinyFrameworkCallerCheck.java" +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class + Compiled from "TinyFrameworkCallerCheck.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 4 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck; + + public static int getOne_withCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I + 3: ireturn + LineNumberTable: + + public static int getOne_noCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I + 3: ireturn + LineNumberTable: +} +SourceFile: "TinyFrameworkCallerCheck.java" +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class + Compiled from "TinyFrameworkClassAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + super_class: #x // java/lang/Object + interfaces: 0, fields: 3, methods: 10, attributes: 2 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + + public int keep; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestKeep + + public int remove; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: iconst_1 + 6: putfield #x // Field stub:I + 9: aload_0 + 10: iconst_2 + 11: putfield #x // Field keep:I + 14: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: iload_1 + 2: invokevirtual #x // Method addOneInner:(I)I + 5: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 6 1 value I + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + + public int addOneInner(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: iload_1 + 1: iconst_1 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 4 1 value I + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestKeep + + public void toBeRemoved(java.lang.String); + descriptor: (Ljava/lang/String;)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V + 7: athrow + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 8 1 foo Ljava/lang/String; + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestRemove + + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String not supported on host side + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 10 1 value I + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + 1: #x(#x=s#x) + android.hosttest.annotation.HostSideTestSubstitute( + suffix="_host" + ) + + public int addTwo_host(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: iload_1 + 1: iconst_2 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 4 1 value I + + public static native int nativeAddThree(int); + descriptor: (I)I + flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + 1: #x(#x=s#x) + android.hosttest.annotation.HostSideTestSubstitute( + suffix="_host" + ) + + public static int nativeAddThree_host(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + 0: iload_0 + 1: iconst_3 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 value I + + public java.lang.String unsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: ldc #x // String This value shouldn\'t be seen on the host side. + 2: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestThrow + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub +} +SourceFile: "TinyFrameworkClassAnnotations.java" +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + 1: #x(#x=s#x) + android.hosttest.annotation.HostSideTestClassLoadHook( + value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" + ) +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class + Compiled from "TinyFrameworkClassClassWideAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations + super_class: #x // java/lang/Object + interfaces: 0, fields: 3, methods: 10, attributes: 2 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public int keep; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public int remove; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: iconst_1 + 6: putfield #x // Field stub:I + 9: aload_0 + 10: iconst_2 + 11: putfield #x // Field keep:I + 14: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: iload_1 + 2: invokevirtual #x // Method addOneInner:(I)I + 5: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + 0 6 1 value I + + public int addOneInner(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: iload_1 + 1: iconst_1 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + 0 4 1 value I + + public void toBeRemoved(java.lang.String); + descriptor: (Ljava/lang/String;)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V + 7: athrow + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + 0 8 1 foo Ljava/lang/String; + + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String not supported on host side + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + 0 10 1 value I + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + 1: #x(#x=s#x) + android.hosttest.annotation.HostSideTestSubstitute( + suffix="_host" + ) + + public int addTwo_host(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: iload_1 + 1: iconst_2 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + 0 4 1 value I + + public static native int nativeAddThree(int); + descriptor: (I)I + flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + 1: #x(#x=s#x) + android.hosttest.annotation.HostSideTestSubstitute( + suffix="_host" + ) + + public static int nativeAddThree_host(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + 0: iload_0 + 1: iconst_3 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 value I + + public java.lang.String unsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: ldc #x // String This value shouldn\'t be seen on the host side. + 2: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; +} +SourceFile: "TinyFrameworkClassClassWideAnnotations.java" +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class + Compiled from "TinyFrameworkClassLoadHook.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 3, attributes: 2 + public static final java.util.Set<java.lang.Class<?>> sLoadedClasses; + descriptor: Ljava/util/Set; + flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL + Signature: #x // Ljava/util/Set<Ljava/lang/Class<*>;>; + + private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook(); + descriptor: ()V + flags: (0x0002) ACC_PRIVATE + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook; + + public static void onClassLoaded(java.lang.Class<?>); + descriptor: (Ljava/lang/Class;)V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + 0: getstatic #x // Field sLoadedClasses:Ljava/util/Set; + 3: aload_0 + 4: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z + 9: pop + 10: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 11 0 clazz Ljava/lang/Class; + LocalVariableTypeTable: + Start Length Slot Name Signature + 0 11 0 clazz Ljava/lang/Class<*>; + Signature: #x // (Ljava/lang/Class<*>;)V + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + 0: new #x // class java/util/HashSet + 3: dup + 4: invokespecial #x // Method java/util/HashSet."<init>":()V + 7: putstatic #x // Field sLoadedClasses:Ljava/util/Set; + 10: return + LineNumberTable: +} +SourceFile: "TinyFrameworkClassLoadHook.java" +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class + Compiled from "TinyFrameworkClassWithInitializer.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 2 + public static boolean sInitialized; + descriptor: Z + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer; + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + 0: iconst_1 + 1: putstatic #x // Field sInitialized:Z + 4: return + LineNumberTable: +} +SourceFile: "TinyFrameworkClassWithInitializer.java" +RuntimeInvisibleAnnotations: + 0: #x(#x=s#x) + android.hosttest.annotation.HostSideTestClassLoadHook( + value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" + ) + 1: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class + Compiled from "TinyFrameworkExceptionTester.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 2, attributes: 2 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester; + + public static int testException(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=1, args_size=0 + 0: new #x // class java/lang/IllegalStateException + 3: dup + 4: ldc #x // String Inner exception + 6: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V + 9: athrow + 10: astore_0 + 11: new #x // class java/lang/RuntimeException + 14: dup + 15: ldc #x // String Outer exception + 17: aload_0 + 18: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V + 21: athrow + Exception table: + from to target type + 0 10 10 Class java/lang/Exception + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 11 0 e Ljava/lang/Exception; + StackMapTable: number_of_entries = 1 + frame_type = 74 /* same_locals_1_stack_item */ + stack = [ class java/lang/Exception ] +} +SourceFile: "TinyFrameworkExceptionTester.java" +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class + Compiled from "TinyFrameworkForTextPolicy.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + super_class: #x // java/lang/Object + interfaces: 0, fields: 3, methods: 10, attributes: 1 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public int keep; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public int remove; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: iconst_1 + 6: putfield #x // Field stub:I + 9: aload_0 + 10: iconst_2 + 11: putfield #x // Field keep:I + 14: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; + + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: iload_1 + 2: invokevirtual #x // Method addOneInner:(I)I + 5: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; + 0 6 1 value I + + public int addOneInner(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: iload_1 + 1: iconst_1 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; + 0 4 1 value I + + public void toBeRemoved(java.lang.String); + descriptor: (Ljava/lang/String;)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V + 7: athrow + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; + 0 8 1 foo Ljava/lang/String; + + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String not supported on host side + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; + 0 10 1 value I + + public int addTwo_host(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: iload_1 + 1: iconst_2 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; + 0 4 1 value I + + public static native int nativeAddThree(int); + descriptor: (I)I + flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE + + public static int addThree_host(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + 0: iload_0 + 1: iconst_3 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 value I + + public java.lang.String unsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: ldc #x // String This value shouldn\'t be seen on the host side. + 2: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; +} +SourceFile: "TinyFrameworkForTextPolicy.java" +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class + Compiled from "TinyFrameworkNative.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 5, attributes: 2 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative; + + public static native int nativeAddTwo(int); + descriptor: (I)I + flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE + + public static int nativeAddTwo_should_be_like_this(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=1, args_size=1 + 0: iload_0 + 1: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I + 4: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 arg I + + public static native long nativeLongPlus(long, long); + descriptor: (JJ)J + flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE + + public static long nativeLongPlus_should_be_like_this(long, long); + descriptor: (JJ)J + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=4, args_size=2 + 0: lload_0 + 1: lload_2 + 2: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J + 5: lreturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 arg1 J + 0 6 2 arg2 J +} +SourceFile: "TinyFrameworkNative.java" +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub + 1: #x(#x=s#x) + android.hosttest.annotation.HostSideTestNativeSubstitutionClass( + value="TinyFrameworkNative_host" + ) +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.class + Compiled from "TinyFrameworkNative_host.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 2 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host; + + public static int nativeAddTwo(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + 0: iload_0 + 1: iconst_2 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 arg I + + public static long nativeLongPlus(long, long); + descriptor: (JJ)J + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=4, args_size=2 + 0: lload_0 + 1: lload_2 + 2: ladd + 3: lreturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 arg1 J + 0 4 2 arg2 J +} +SourceFile: "TinyFrameworkNative_host.java" +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + super_class: #x // java/lang/Object + interfaces: 1, fields: 1, methods: 3, attributes: 5 + final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC + + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + flags: (0x0000) + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: aload_1 + 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + 5: aload_0 + 6: invokespecial #x // Method java/lang/Object."<init>":()V + 9: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1; + 0 10 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: iconst_1 + 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1; + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokevirtual #x // Method get:()Ljava/lang/Integer; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1; +} +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 3, attributes: 5 + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2(); + descriptor: ()V + flags: (0x0000) + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2; + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: iconst_2 + 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2; + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokevirtual #x // Method get:()Ljava/lang/Integer; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2; +} +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + super_class: #x // java/lang/Object + interfaces: 1, fields: 1, methods: 3, attributes: 5 + final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC + + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + flags: (0x0000) + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: aload_1 + 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + 5: aload_0 + 6: invokespecial #x // Method java/lang/Object."<init>":()V + 9: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3; + 0 10 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: iconst_3 + 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3; + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokevirtual #x // Method get:()Ljava/lang/Integer; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3; +} +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 3, attributes: 5 + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4(); + descriptor: ()V + flags: (0x0000) + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4; + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: iconst_4 + 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4; + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokevirtual #x // Method get:()Ljava/lang/Integer; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4; +} +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier_static +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 1, attributes: 3 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: iload_1 + 6: putfield #x // Field value:I + 9: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass; + 0 10 1 x I +} +SourceFile: "TinyFrameworkNestedClasses.java" +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +InnerClasses: + public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 1, attributes: 4 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: aload_1 + 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + 5: aload_0 + 6: invokespecial #x // Method java/lang/Object."<init>":()V + 9: aload_0 + 10: iconst_5 + 11: putfield #x // Field value:I + 14: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass; + 0 15 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; +} +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +InnerClasses: + public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 3, attributes: 5 + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1(); + descriptor: ()V + flags: (0x0000) + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1; + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: bipush 7 + 2: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + 5: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1; + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokevirtual #x // Method get:()Ljava/lang/Integer; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1; +} +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass.getSupplier_static +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +InnerClasses: + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 4 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: bipush 6 + 7: putfield #x // Field value:I + 10: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 11 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass; + + public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + 3: dup + 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V + 7: areturn + LineNumberTable: + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; +} +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +InnerClasses: + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass extends com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass + super_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass + interfaces: 0, fields: 0, methods: 1, attributes: 3 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: iload_1 + 2: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V + 5: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass; + 0 6 1 x I +} +SourceFile: "TinyFrameworkNestedClasses.java" +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +InnerClasses: + public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 4, attributes: 4 + public final java.util.function.Supplier<java.lang.Integer> mSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0011) ACC_PUBLIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + public static final java.util.function.Supplier<java.lang.Integer> sSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + 8: dup + 9: aload_0 + 10: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + 13: putfield #x // Field mSupplier:Ljava/util/function/Supplier; + 16: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 17 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + + public java.util.function.Supplier<java.lang.Integer> getSupplier(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + 3: dup + 4: aload_0 + 5: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + 8: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 9 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + 3: dup + 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V + 7: areturn + LineNumberTable: + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + 3: dup + 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V + 7: putstatic #x // Field sSupplier:Ljava/util/function/Supplier; + 10: return + LineNumberTable: +} +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt new file mode 100644 index 000000000000..6e1528aedc1e --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt @@ -0,0 +1,837 @@ +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class + Compiled from "TinyFrameworkCallerCheck.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 2, attributes: 4 + private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); + descriptor: ()V + flags: (0x0002) ACC_PRIVATE + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public static int getOneStub(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=0, args_size=0 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub +} +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +SourceFile: "TinyFrameworkCallerCheck.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class + Compiled from "TinyFrameworkCallerCheck.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 5 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public static int getOne_withCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=0, args_size=0 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public static int getOne_noCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=0, args_size=0 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow +} +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +SourceFile: "TinyFrameworkCallerCheck.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class + Compiled from "TinyFrameworkClassAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 5, attributes: 3 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public static int nativeAddThree(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub +} +SourceFile: "TinyFrameworkClassAnnotations.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + 1: #x(#x=s#x) + android.hosttest.annotation.HostSideTestClassLoadHook( + value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" + ) +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class + Compiled from "TinyFrameworkClassClassWideAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations + super_class: #x // java/lang/Object + interfaces: 0, fields: 3, methods: 8, attributes: 3 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public int keep; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public int remove; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public int addOneInner(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public void toBeRemoved(java.lang.String); + descriptor: (Ljava/lang/String;)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public static int nativeAddThree(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public java.lang.String unsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow +} +SourceFile: "TinyFrameworkClassClassWideAnnotations.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class + Compiled from "TinyFrameworkClassLoadHook.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 3, attributes: 3 + public static final java.util.Set<java.lang.Class<?>> sLoadedClasses; + descriptor: Ljava/util/Set; + flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL + Signature: #x // Ljava/util/Set<Ljava/lang/Class<*>;>; + + private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook(); + descriptor: ()V + flags: (0x0002) ACC_PRIVATE + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public static void onClassLoaded(java.lang.Class<?>); + descriptor: (Ljava/lang/Class;)V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + Signature: #x // (Ljava/lang/Class<*>;)V + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=3, locals=0, args_size=0 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow +} +SourceFile: "TinyFrameworkClassLoadHook.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class + Compiled from "TinyFrameworkClassWithInitializer.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 3 + public static boolean sInitialized; + descriptor: Z + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=3, locals=0, args_size=0 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow +} +SourceFile: "TinyFrameworkClassWithInitializer.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x(#x=s#x) + android.hosttest.annotation.HostSideTestClassLoadHook( + value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" + ) + 1: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class + Compiled from "TinyFrameworkExceptionTester.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 2, attributes: 3 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public static int testException(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=0, args_size=0 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow +} +SourceFile: "TinyFrameworkExceptionTester.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class + Compiled from "TinyFrameworkForTextPolicy.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 5, attributes: 2 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public static int nativeAddThree(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow +} +SourceFile: "TinyFrameworkForTextPolicy.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class + Compiled from "TinyFrameworkNative.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 5, attributes: 3 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public static native int nativeAddTwo(int); + descriptor: (I)I + flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE + + public static int nativeAddTwo_should_be_like_this(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public static native long nativeLongPlus(long, long); + descriptor: (JJ)J + flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE + + public static long nativeLongPlus_should_be_like_this(long, long); + descriptor: (JJ)J + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=4, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow +} +SourceFile: "TinyFrameworkNative.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub + 1: #x(#x=s#x) + android.hosttest.annotation.HostSideTestNativeSubstitutionClass( + value="TinyFrameworkNative_host" + ) +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 1, attributes: 4 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow +} +InnerClasses: + public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 1, attributes: 5 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow +} +InnerClasses: + public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 5 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=0, args_size=0 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; +} +InnerClasses: + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass extends com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass + super_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass + interfaces: 0, fields: 0, methods: 1, attributes: 4 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow +} +InnerClasses: + public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 4, attributes: 5 + public final java.util.function.Supplier<java.lang.Integer> mSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0011) ACC_PUBLIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + public static final java.util.function.Supplier<java.lang.Integer> sSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + + public java.util.function.Supplier<java.lang.Integer> getSupplier(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=0, args_size=0 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=3, locals=0, args_size=0 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: ldc #x // String Stub! + 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 9: athrow +} +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt new file mode 100644 index 000000000000..5672e9c36706 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt @@ -0,0 +1,1774 @@ +## Class: android/hosttest/annotation/HostSideTestClassLoadHook.class + Compiled from "HostSideTestClassLoadHook.java" +public interface android.hosttest.annotation.HostSideTestClassLoadHook extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestClassLoadHook + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + public abstract java.lang.String value(); + descriptor: ()Ljava/lang/String; + flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT +} +SourceFile: "HostSideTestClassLoadHook.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass + 1: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE] + ) + 2: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestKeep.class + Compiled from "HostSideTestKeep.java" +public interface android.hosttest.annotation.HostSideTestKeep extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestKeep + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestKeep.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass + 1: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + 2: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestNativeSubstitutionClass.class + Compiled from "HostSideTestNativeSubstitutionClass.java" +public interface android.hosttest.annotation.HostSideTestNativeSubstitutionClass extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestNativeSubstitutionClass + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + public abstract java.lang.String value(); + descriptor: ()Ljava/lang/String; + flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT +} +SourceFile: "HostSideTestNativeSubstitutionClass.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass + 1: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE] + ) + 2: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestRemove.class + Compiled from "HostSideTestRemove.java" +public interface android.hosttest.annotation.HostSideTestRemove extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestRemove + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestRemove.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass + 1: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + 2: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestStub.class + Compiled from "HostSideTestStub.java" +public interface android.hosttest.annotation.HostSideTestStub extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestStub + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestStub.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass + 1: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + 2: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestSubstitute.class + Compiled from "HostSideTestSubstitute.java" +public interface android.hosttest.annotation.HostSideTestSubstitute extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestSubstitute + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + public abstract java.lang.String suffix(); + descriptor: ()Ljava/lang/String; + flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT +} +SourceFile: "HostSideTestSubstitute.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass + 1: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.METHOD] + ) + 2: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestThrow.class + Compiled from "HostSideTestThrow.java" +public interface android.hosttest.annotation.HostSideTestThrow extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestThrow + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestThrow.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass + 1: #x(#x=[e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + 2: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestWholeClassKeep.class + Compiled from "HostSideTestWholeClassKeep.java" +public interface android.hosttest.annotation.HostSideTestWholeClassKeep extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestWholeClassKeep + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestWholeClassKeep.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass + 1: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE] + ) + 2: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestWholeClassStub.class + Compiled from "HostSideTestWholeClassStub.java" +public interface android.hosttest.annotation.HostSideTestWholeClassStub extends java.lang.annotation.Annotation + minor version: 0 + major version: 61 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestWholeClassStub + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestWholeClassStub.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass + 1: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE] + ) + 2: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class + Compiled from "TinyFrameworkCallerCheck.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 4 + private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); + descriptor: ()V + flags: (0x0002) ACC_PRIVATE + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl; + + public static int getOneKeep(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl + 2: ldc #x // String getOneKeep + 4: ldc #x // String ()I + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: iconst_1 + 16: ireturn + LineNumberTable: + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestKeep + + public static int getOneStub(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + 0: iconst_1 + 1: ireturn + LineNumberTable: + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub +} +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +SourceFile: "TinyFrameworkCallerCheck.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class + Compiled from "TinyFrameworkCallerCheck.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 5 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck; + + public static int getOne_withCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I + 3: ireturn + LineNumberTable: + + public static int getOne_noCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I + 3: ireturn + LineNumberTable: +} +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +SourceFile: "TinyFrameworkCallerCheck.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class + Compiled from "TinyFrameworkClassAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 8, attributes: 3 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + + public int keep; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestKeep + + private static {}; + descriptor: ()V + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + 0: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + 2: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded + 4: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V + 7: return + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: iconst_1 + 6: putfield #x // Field stub:I + 9: aload_0 + 10: iconst_2 + 11: putfield #x // Field keep:I + 14: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: iload_1 + 2: invokevirtual #x // Method addOneInner:(I)I + 5: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 6 1 value I + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + + public int addOneInner(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=2, args_size=2 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + 2: ldc #x // String addOneInner + 4: ldc #x // String (I)I + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: iload_1 + 16: iconst_1 + 17: iadd + 18: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 15 4 1 value I + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestKeep + + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: iload_1 + 1: iconst_2 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 4 1 value I + + public static int nativeAddThree(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + 0: iload_0 + 1: iconst_3 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 value I + + public java.lang.String unsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + 2: ldc #x // String unsupportedMethod + 4: ldc #x // String ()Ljava/lang/String; + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V + 18: new #x // class java/lang/RuntimeException + 21: dup + 22: ldc #x // String Unreachable + 24: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 27: athrow + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestThrow + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub +} +SourceFile: "TinyFrameworkClassAnnotations.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestStub + 1: #x(#x=s#x) + android.hosttest.annotation.HostSideTestClassLoadHook( + value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" + ) +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class + Compiled from "TinyFrameworkClassClassWideAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations + super_class: #x // java/lang/Object + interfaces: 0, fields: 3, methods: 8, attributes: 3 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public int keep; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public int remove; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: iconst_1 + 6: putfield #x // Field stub:I + 9: aload_0 + 10: iconst_2 + 11: putfield #x // Field keep:I + 14: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: iload_1 + 2: invokevirtual #x // Method addOneInner:(I)I + 5: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + 0 6 1 value I + + public int addOneInner(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: iload_1 + 1: iconst_1 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + 0 4 1 value I + + public void toBeRemoved(java.lang.String); + descriptor: (Ljava/lang/String;)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: new #x // class java/lang/RuntimeException + 3: dup + 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V + 7: athrow + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + 0 8 1 foo Ljava/lang/String; + + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: iload_1 + 1: iconst_2 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + 0 4 1 value I + + public static int nativeAddThree(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + 0: iload_0 + 1: iconst_3 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 value I + + public java.lang.String unsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: ldc #x // String This value shouldn\'t be seen on the host side. + 2: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; +} +SourceFile: "TinyFrameworkClassClassWideAnnotations.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class + Compiled from "TinyFrameworkClassLoadHook.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 3, attributes: 3 + public static final java.util.Set<java.lang.Class<?>> sLoadedClasses; + descriptor: Ljava/util/Set; + flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL + Signature: #x // Ljava/util/Set<Ljava/lang/Class<*>;>; + + private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook(); + descriptor: ()V + flags: (0x0002) ACC_PRIVATE + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook; + + public static void onClassLoaded(java.lang.Class<?>); + descriptor: (Ljava/lang/Class;)V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + 0: getstatic #x // Field sLoadedClasses:Ljava/util/Set; + 3: aload_0 + 4: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z + 9: pop + 10: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 11 0 clazz Ljava/lang/Class; + LocalVariableTypeTable: + Start Length Slot Name Signature + 0 11 0 clazz Ljava/lang/Class<*>; + Signature: #x // (Ljava/lang/Class<*>;)V + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + 0: new #x // class java/util/HashSet + 3: dup + 4: invokespecial #x // Method java/util/HashSet."<init>":()V + 7: putstatic #x // Field sLoadedClasses:Ljava/util/Set; + 10: return + LineNumberTable: +} +SourceFile: "TinyFrameworkClassLoadHook.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class + Compiled from "TinyFrameworkClassWithInitializer.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 3 + public static boolean sInitialized; + descriptor: Z + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer; + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + 0: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer + 2: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded + 4: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V + 7: iconst_1 + 8: putstatic #x // Field sInitialized:Z + 11: return + LineNumberTable: +} +SourceFile: "TinyFrameworkClassWithInitializer.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x(#x=s#x) + android.hosttest.annotation.HostSideTestClassLoadHook( + value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" + ) + 1: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class + Compiled from "TinyFrameworkExceptionTester.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 2, attributes: 3 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester; + + public static int testException(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=1, args_size=0 + 0: new #x // class java/lang/IllegalStateException + 3: dup + 4: ldc #x // String Inner exception + 6: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V + 9: athrow + 10: astore_0 + 11: new #x // class java/lang/RuntimeException + 14: dup + 15: ldc #x // String Outer exception + 17: aload_0 + 18: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V + 21: athrow + Exception table: + from to target type + 0 10 10 Class java/lang/Exception + StackMapTable: number_of_entries = 1 + frame_type = 74 /* same_locals_1_stack_item */ + stack = [ class java/lang/Exception ] + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 11 0 e Ljava/lang/Exception; +} +SourceFile: "TinyFrameworkExceptionTester.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class + Compiled from "TinyFrameworkForTextPolicy.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 8, attributes: 2 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public int keep; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + private static {}; + descriptor: ()V + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + 0: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + 2: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded + 4: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V + 7: return + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: iconst_1 + 6: putfield #x // Field stub:I + 9: aload_0 + 10: iconst_2 + 11: putfield #x // Field keep:I + 14: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; + + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: iload_1 + 2: invokevirtual #x // Method addOneInner:(I)I + 5: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; + 0 6 1 value I + + public int addOneInner(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=2, args_size=2 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + 2: ldc #x // String addOneInner + 4: ldc #x // String (I)I + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: iload_1 + 16: iconst_1 + 17: iadd + 18: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; + 15 4 1 value I + + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: iload_1 + 1: iconst_2 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; + 0 4 1 value I + + public static int nativeAddThree(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + 0: iload_0 + 1: iconst_3 + 2: iadd + 3: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 value I + + public java.lang.String unsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + 2: ldc #x // String unsupportedMethod + 4: ldc #x // String ()Ljava/lang/String; + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V + 18: new #x // class java/lang/RuntimeException + 21: dup + 22: ldc #x // String Unreachable + 24: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + 27: athrow + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; + 4: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy; +} +SourceFile: "TinyFrameworkForTextPolicy.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class + Compiled from "TinyFrameworkNative.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 5, attributes: 3 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative; + + public static int nativeAddTwo(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=1, args_size=1 + 0: iload_0 + 1: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I + 4: ireturn + + public static int nativeAddTwo_should_be_like_this(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=1, args_size=1 + 0: iload_0 + 1: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I + 4: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 arg I + + public static long nativeLongPlus(long, long); + descriptor: (JJ)J + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=4, args_size=2 + 0: lload_0 + 1: lload_2 + 2: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J + 5: lreturn + + public static long nativeLongPlus_should_be_like_this(long, long); + descriptor: (JJ)J + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=4, args_size=2 + 0: lload_0 + 1: lload_2 + 2: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J + 5: lreturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 arg1 J + 0 6 2 arg2 J +} +SourceFile: "TinyFrameworkNative.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub + 1: #x(#x=s#x) + android.hosttest.annotation.HostSideTestNativeSubstitutionClass( + value="TinyFrameworkNative_host" + ) +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.class + Compiled from "TinyFrameworkNative_host.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 3 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + 2: ldc #x // String <init> + 4: ldc #x // String ()V + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: aload_0 + 16: invokespecial #x // Method java/lang/Object."<init>":()V + 19: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host; + + public static int nativeAddTwo(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + 2: ldc #x // String nativeAddTwo + 4: ldc #x // String (I)I + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: iload_0 + 16: iconst_2 + 17: iadd + 18: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 4 0 arg I + + public static long nativeLongPlus(long, long); + descriptor: (JJ)J + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=4, args_size=2 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + 2: ldc #x // String nativeLongPlus + 4: ldc #x // String (JJ)J + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: lload_0 + 16: lload_2 + 17: ladd + 18: lreturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 4 0 arg1 J + 15 4 2 arg2 J +} +SourceFile: "TinyFrameworkNative_host.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + super_class: #x // java/lang/Object + interfaces: 1, fields: 1, methods: 3, attributes: 6 + final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC + + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + flags: (0x0000) + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: aload_1 + 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + 5: aload_0 + 6: invokespecial #x // Method java/lang/Object."<init>":()V + 9: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1; + 0 10 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + 2: ldc #x // String get + 4: ldc #x // String ()Ljava/lang/Integer; + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: iconst_1 + 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + 19: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1; + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + 2: ldc #x // String get + 4: ldc #x // String ()Ljava/lang/Object; + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: aload_0 + 16: invokevirtual #x // Method get:()Ljava/lang/Integer; + 19: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1; +} +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 3, attributes: 6 + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2(); + descriptor: ()V + flags: (0x0000) + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2; + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + 2: ldc #x // String get + 4: ldc #x // String ()Ljava/lang/Integer; + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: iconst_2 + 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + 19: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2; + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + 2: ldc #x // String get + 4: ldc #x // String ()Ljava/lang/Object; + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: aload_0 + 16: invokevirtual #x // Method get:()Ljava/lang/Integer; + 19: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2; +} +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + super_class: #x // java/lang/Object + interfaces: 1, fields: 1, methods: 3, attributes: 6 + final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC + + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + flags: (0x0000) + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: aload_1 + 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + 5: aload_0 + 6: invokespecial #x // Method java/lang/Object."<init>":()V + 9: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3; + 0 10 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + 2: ldc #x // String get + 4: ldc #x // String ()Ljava/lang/Integer; + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: iconst_3 + 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + 19: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3; + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + 2: ldc #x // String get + 4: ldc #x // String ()Ljava/lang/Object; + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: aload_0 + 16: invokevirtual #x // Method get:()Ljava/lang/Integer; + 19: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3; +} +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 3, attributes: 6 + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4(); + descriptor: ()V + flags: (0x0000) + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4; + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + 2: ldc #x // String get + 4: ldc #x // String ()Ljava/lang/Integer; + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: iconst_4 + 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + 19: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4; + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + 2: ldc #x // String get + 4: ldc #x // String ()Ljava/lang/Object; + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: aload_0 + 16: invokevirtual #x // Method get:()Ljava/lang/Integer; + 19: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4; +} +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier_static +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 1, attributes: 4 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: iload_1 + 6: putfield #x // Field value:I + 9: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass; + 0 10 1 x I +} +InnerClasses: + public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 1, attributes: 5 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: aload_1 + 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + 5: aload_0 + 6: invokespecial #x // Method java/lang/Object."<init>":()V + 9: aload_0 + 10: iconst_5 + 11: putfield #x // Field value:I + 14: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass; + 0 15 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; +} +InnerClasses: + public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 61 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 3, attributes: 6 + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1(); + descriptor: ()V + flags: (0x0000) + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1; + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + 2: ldc #x // String get + 4: ldc #x // String ()Ljava/lang/Integer; + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: bipush 7 + 17: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + 20: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1; + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=4, locals=1, args_size=1 + 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + 2: ldc #x // String get + 4: ldc #x // String ()Ljava/lang/Object; + 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + 15: aload_0 + 16: invokevirtual #x // Method get:()Ljava/lang/Integer; + 19: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1; +} +InnerClasses: + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass.getSupplier_static +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 5 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: bipush 6 + 7: putfield #x // Field value:I + 10: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 11 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass; + + public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + 3: dup + 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V + 7: areturn + LineNumberTable: + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; +} +InnerClasses: + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass extends com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass + super_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass + interfaces: 0, fields: 0, methods: 1, attributes: 4 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + 0: aload_0 + 1: iload_1 + 2: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V + 5: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass; + 0 6 1 x I +} +InnerClasses: + public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 4, attributes: 5 + public final java.util.function.Supplier<java.lang.Integer> mSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0011) ACC_PUBLIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + public static final java.util.function.Supplier<java.lang.Integer> sSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #x // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + 8: dup + 9: aload_0 + 10: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + 13: putfield #x // Field mSupplier:Ljava/util/function/Supplier; + 16: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 17 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + + public java.util.function.Supplier<java.lang.Integer> getSupplier(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + 3: dup + 4: aload_0 + 5: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + 8: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 9 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + 3: dup + 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V + 7: areturn + LineNumberTable: + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + 3: dup + 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V + 7: putstatic #x // Field sSupplier:Ljava/util/function/Supplier; + 10: return + LineNumberTable: +} +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + 0: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass + 1: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass +RuntimeInvisibleAnnotations: + 0: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt new file mode 100644 index 000000000000..079d2a84e498 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt @@ -0,0 +1,17 @@ +class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy stub + field stub stub + field keep keep + # field remove remove # Implicitly remove + method <init> ()V stub + method addOne (I)I stub + method addOneInner (I)I keep + method toBeRemoved (Ljava/lang/String;)V remove + method addTwo (I)I @addTwo_host + # method addTwo_host (I)I # used as a substitute + method nativeAddThree (I)I @addThree_host + # method addThree_host (I)I # used as a substitute + method unsupportedMethod ()Ljava/lang/String; throw + method visibleButUsesUnsupportedMethod ()Ljava/lang/String; stub + +# Class load hook +class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy ~com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh b/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh new file mode 100755 index 000000000000..fd486468a1a7 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh @@ -0,0 +1,132 @@ +#!/bin/bash +# Copyright (C) 2023 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. + + +source "${0%/*}"/../../common.sh + +# This scripts run the "tiny-framework" test, but does most stuff from the command line, using +# the native java and javac commands. +# This is useful to + + +debug=0 +while getopts "d" opt; do +case "$opt" in + d) debug=1 ;; +esac +done +shift $(($OPTIND - 1)) + + +out=out + +rm -fr $out +mkdir -p $out + +HOSTSTUBGEN=hoststubgen + +# Rebuild the tool and the dependencies. These are the only things we build with the build system. +run m $HOSTSTUBGEN hoststubgen-annotations hoststubgen-helper-runtime truth-prebuilt junit + + +# Build tiny-framework + +tiny_framework_classes=$out/tiny-framework/classes/ +tiny_framework_jar=$out/tiny-framework.jar +tiny_framework_host_stub_jar=$out/tiny-framework_host_stub.jar +tiny_framework_host_impl_jar=$out/tiny-framework_host_impl.jar + +tiny_test_classes=$out/tiny-test/classes/ +tiny_test_jar=$out/tiny-test.jar + +framework_compile_classpaths=( + $SOONG_INT/frameworks/base/tools/hoststubgen/hoststubgen/hoststubgen-annotations/android_common/javac/hoststubgen-annotations.jar +) + +test_compile_classpaths=( + $SOONG_INT/external/junit/junit/android_common/combined/junit.jar + $SOONG_INT/prebuilts/tools/common/m2/truth-prebuilt/android_common/combined/truth-prebuilt.jar +) + +test_runtime_classpaths=( + $SOONG_INT/frameworks/base/tools/hoststubgen/hoststubgen/hoststubgen-helper-runtime/linux_glibc_common/javac/hoststubgen-helper-runtime.jar +) + +# This suite runs all tests in the JAR. +test_classes=(com.android.hoststubgen.hosthelper.HostTestSuite) + +# Uncomment this to run a specific test. +# tests=(com.android.hoststubgen.test.tinyframework.TinyFrameworkBenchmark) + + +# Build tiny-framework.jar +echo "# Building tiny-framework..." +run $JAVAC \ + -cp $( \ + join : \ + ${framework_compile_classpaths[@]} \ + ) \ + -d $tiny_framework_classes \ + tiny-framework/src/**/*.java + +run $JAR cvf $tiny_framework_jar \ + -C $tiny_framework_classes . + +# Build stub/impl jars +echo "# Generating the stub and impl jars..." +run $HOSTSTUBGEN \ + @../hoststubgen-standard-options.txt \ + --in-jar $tiny_framework_jar \ + --out-stub-jar $tiny_framework_host_stub_jar \ + --out-impl-jar $tiny_framework_host_impl_jar \ + --policy-override-file policy-override-tiny-framework.txt \ + --gen-keep-all-file out/tiny-framework_keep_all.txt \ + --gen-input-dump-file out/tiny-framework_dump.txt \ + $HOSTSTUBGEN_OPTS + +# Extract the jar files, so we can look into them. +extract $tiny_framework_host_stub_jar $tiny_framework_host_impl_jar + +# Build the test +echo "# Building tiny-test..." +run $JAVAC \ + -cp $( \ + join : \ + $tiny_framework_host_stub_jar \ + "${test_compile_classpaths[@]}" \ + ) \ + -d $tiny_test_classes \ + tiny-test/src/**/*.java + +run $JAR cvf $tiny_test_jar \ + -C $tiny_test_classes . + +if (( $debug )) ; then + JAVA_OPTS="$JAVA_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8700" +fi + +# Run the test +echo "# Running tiny-test..." +run $JAVA \ + $JAVA_OPTS \ + -cp $( \ + join : \ + $tiny_test_jar \ + $tiny_framework_host_impl_jar \ + "${test_compile_classpaths[@]}" \ + "${test_runtime_classpaths[@]}" \ + ) \ + org.junit.runner.JUnitCore \ + ${test_classes[@]} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py new file mode 100755 index 000000000000..cee29dcd1d59 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py @@ -0,0 +1,64 @@ +#!/usr/bin/python3 +# Copyright (C) 2023 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. + +# Compare the tiny-framework JAR dumps to the golden files. + +import sys +import os +import unittest +import subprocess + +GOLDEN_DIR = 'golden-output' + +# Run diff. +def run_diff(file1, file2): + command = ['diff', '-u', '--ignore-blank-lines', '--ignore-space-change', file1, file2] + print(' '.join(command)) + result = subprocess.run(command, stderr = sys.stdout) + + success = result.returncode == 0 + + if success: + print(f'No diff found.') + else: + print(f'Fail: {file1} and {file2} are different.') + + return success + + +# Check one golden file. +def check_one_file(filename): + print(f'= Checking file: {filename}') + return run_diff(os.path.join(GOLDEN_DIR, filename), filename) + +class TestWithGoldenOutput(unittest.TestCase): + + # Test to check the generated jar files to the golden output. + def test_compare_to_golden(self): + files = os.listdir(GOLDEN_DIR) + files.sort() + + print(f"Golden files: {files}") + success = True + + for file in files: + if not check_one_file(file): + success = False + + if not success: + self.fail('Some files are different. See stdout log for more details.') + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.java new file mode 100644 index 000000000000..f53020771cc3 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.test.tinyframework; + +import android.hosttest.annotation.HostSideTestKeep; +import android.hosttest.annotation.HostSideTestStub; +import android.hosttest.annotation.HostSideTestWholeClassStub; + +/** + * Used by the benchmark. + */ +@HostSideTestWholeClassStub +public class TinyFrameworkCallerCheck { + + /** + * This method uses an inner method (which has the caller check). + * + * Benchmark result: 768ns + */ + public static int getOne_withCheck() { + return Impl.getOneKeep(); + } + + /** + * This method doesn't have any caller check. + * + * Benchmark result: 2ns + */ + public static int getOne_noCheck() { + return Impl.getOneStub(); + } + + private static class Impl { + @HostSideTestKeep + public static int getOneKeep() { + return 1; + } + + @HostSideTestStub + public static int getOneStub() { + return 1; + } + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.java new file mode 100644 index 000000000000..ab387e0938c3 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.test.tinyframework; + +import android.hosttest.annotation.HostSideTestClassLoadHook; +import android.hosttest.annotation.HostSideTestKeep; +import android.hosttest.annotation.HostSideTestRemove; +import android.hosttest.annotation.HostSideTestStub; +import android.hosttest.annotation.HostSideTestSubstitute; +import android.hosttest.annotation.HostSideTestThrow; + +/** + * Test without class-wide annotations. + */ +@HostSideTestStub +@HostSideTestClassLoadHook( + "com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded") +public class TinyFrameworkClassAnnotations { + @HostSideTestStub + public TinyFrameworkClassAnnotations() { + } + + @HostSideTestStub + public int stub = 1; + + @HostSideTestKeep + public int keep = 2; + + // Members will be deleted by default. + // Deleted fields cannot have an initial value, because otherwise .ctor will fail to set it at + // runtime. + public int remove; + + @HostSideTestStub + public int addOne(int value) { + return addOneInner(value); + } + + @HostSideTestKeep + public int addOneInner(int value) { + return value + 1; + } + + @HostSideTestRemove // Explicitly remove + public void toBeRemoved(String foo) { + throw new RuntimeException(); + } + + @HostSideTestStub + @HostSideTestSubstitute(suffix = "_host") + public int addTwo(int value) { + throw new RuntimeException("not supported on host side"); + } + + public int addTwo_host(int value) { + return value + 2; + } + + @HostSideTestStub + @HostSideTestSubstitute(suffix = "_host") + public static native int nativeAddThree(int value); + + public static int nativeAddThree_host(int value) { + return value + 3; + } + + @HostSideTestThrow + public String unsupportedMethod() { + return "This value shouldn't be seen on the host side."; + } + + @HostSideTestStub + public String visibleButUsesUnsupportedMethod() { + return unsupportedMethod(); + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.java new file mode 100644 index 000000000000..145b65a98d7b --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.test.tinyframework; + +import android.hosttest.annotation.HostSideTestStub; +import android.hosttest.annotation.HostSideTestSubstitute; +import android.hosttest.annotation.HostSideTestWholeClassStub; + +@HostSideTestWholeClassStub +public class TinyFrameworkClassClassWideAnnotations { + public TinyFrameworkClassClassWideAnnotations() { + } + + public int stub = 1; + + public int keep = 2; + + // Cannot have an initial value, because otherwise .ctor will fail to set it at runtime. + public int remove; + + // @Stub + public int addOne(int value) { + return addOneInner(value); + } + + // @Keep + public int addOneInner(int value) { + return value + 1; + } + + // @Remove + public void toBeRemoved(String foo) { + throw new RuntimeException(); + } + + @HostSideTestStub + @HostSideTestSubstitute(suffix = "_host") + public int addTwo(int value) { + throw new RuntimeException("not supported on host side"); + } + + public int addTwo_host(int value) { + return value + 2; + } + + @HostSideTestStub + @HostSideTestSubstitute(suffix = "_host") + public static native int nativeAddThree(int value); + + public static int nativeAddThree_host(int value) { + return value + 3; + } + + public String unsupportedMethod() { + return "This value shouldn't be seen on the host side."; + } + + public String visibleButUsesUnsupportedMethod() { + return unsupportedMethod(); + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java new file mode 100644 index 000000000000..98fc6349cdc9 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.test.tinyframework; + +import android.hosttest.annotation.HostSideTestWholeClassStub; + +import java.util.HashSet; +import java.util.Set; + +@HostSideTestWholeClassStub +public class TinyFrameworkClassLoadHook { + private TinyFrameworkClassLoadHook() { + } + + public static final Set<Class<?>> sLoadedClasses = new HashSet<>(); + + public static void onClassLoaded(Class<?> clazz) { + sLoadedClasses.add(clazz); + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.java new file mode 100644 index 000000000000..53cfdf6cd412 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.test.tinyframework; + +import android.hosttest.annotation.HostSideTestClassLoadHook; +import android.hosttest.annotation.HostSideTestWholeClassStub; + +@HostSideTestClassLoadHook( + "com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded") +@HostSideTestWholeClassStub +public class TinyFrameworkClassWithInitializer { + static { + sInitialized = true; + } + + public static boolean sInitialized; +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java new file mode 100644 index 000000000000..909d3b440f50 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.test.tinyframework; + +import android.hosttest.annotation.HostSideTestWholeClassStub; + +@HostSideTestWholeClassStub +public class TinyFrameworkExceptionTester { + public static int testException() { + try { + throw new IllegalStateException("Inner exception"); + } catch (Exception e) { + throw new RuntimeException("Outer exception", e); + } + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java new file mode 100644 index 000000000000..bde7c35dc294 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.test.tinyframework; + +/** + * Class for testing the "text policy" file. + */ +public class TinyFrameworkForTextPolicy { + public TinyFrameworkForTextPolicy() { + } + + public int stub = 1; + + public int keep = 2; + + // Removed fields cannot have an initial value, because otherwise .ctor will fail to set it at + // runtime. + public int remove; + + public int addOne(int value) { + return addOneInner(value); + } + + public int addOneInner(int value) { + return value + 1; + } + + public void toBeRemoved(String foo) { + throw new RuntimeException(); + } + + public int addTwo(int value) { + throw new RuntimeException("not supported on host side"); + } + + public int addTwo_host(int value) { + return value + 2; + } + + public static native int nativeAddThree(int value); + + public static int addThree_host(int value) { + return value + 3; + } + + public String unsupportedMethod() { + return "This value shouldn't be seen on the host side."; + } + + public String visibleButUsesUnsupportedMethod() { + return unsupportedMethod(); + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java new file mode 100644 index 000000000000..c151dcc9c90e --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.test.tinyframework; + +import android.hosttest.annotation.HostSideTestNativeSubstitutionClass; +import android.hosttest.annotation.HostSideTestWholeClassStub; + +@HostSideTestWholeClassStub +@HostSideTestNativeSubstitutionClass("TinyFrameworkNative_host") +public class TinyFrameworkNative { + public static native int nativeAddTwo(int arg); + + public static int nativeAddTwo_should_be_like_this(int arg) { + return TinyFrameworkNative_host.nativeAddTwo(arg); + } + + public static native long nativeLongPlus(long arg1, long arg2); + + public static long nativeLongPlus_should_be_like_this(long arg1, long arg2) { + return TinyFrameworkNative_host.nativeLongPlus(arg1, arg2); + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java new file mode 100644 index 000000000000..48f7dea8c66c --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.test.tinyframework; + +import android.hosttest.annotation.HostSideTestWholeClassKeep; + +// TODO: This annotation shouldn't be needed. +// We should infer it from HostSideTestNativeSubstitutionClass. +@HostSideTestWholeClassKeep +public class TinyFrameworkNative_host { + public static int nativeAddTwo(int arg) { + return arg + 2; + } + + public static long nativeLongPlus(long arg1, long arg2) { + return arg1 + arg2; + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java new file mode 100644 index 000000000000..e1c48bb85563 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.test.tinyframework; + +import android.hosttest.annotation.HostSideTestWholeClassStub; + +import java.util.function.Supplier; + +@HostSideTestWholeClassStub +public class TinyFrameworkNestedClasses { + public final Supplier<Integer> mSupplier = new Supplier<Integer>() { + @Override + public Integer get() { + return 1; + } + }; + + public static final Supplier<Integer> sSupplier = new Supplier<Integer>() { + @Override + public Integer get() { + return 2; + } + }; + public Supplier<Integer> getSupplier() { + return new Supplier<Integer>() { + @Override + public Integer get() { + return 3; + } + }; + } + + public static Supplier<Integer> getSupplier_static() { + return new Supplier<Integer>() { + @Override + public Integer get() { + return 4; + } + }; + } + + @HostSideTestWholeClassStub + public class InnerClass { + public int value = 5; + } + + @HostSideTestWholeClassStub + public static class StaticNestedClass { + public int value = 6; + + // Double-nest + public static Supplier<Integer> getSupplier_static() { + return new Supplier<Integer>() { + @Override + public Integer get() { + return 7; + } + }; + } + } + + public static class BaseClass { + public int value; + public BaseClass(int x) { + value = x; + } + } + + public static class SubClass extends BaseClass { + public SubClass(int x) { + super(x); + } + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkBenchmark.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkBenchmark.java new file mode 100644 index 000000000000..6b5110ef2cef --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkBenchmark.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.test.tinyframework; + +import org.junit.Test; + +import java.text.DecimalFormat; + +/** + * Contains simple micro-benchmarks. + */ +public class TinyFrameworkBenchmark { + private static final int MINIMAL_ITERATION = 1000; + private static final int MEASURE_SECONDS = 3; + + private static final DecimalFormat sFormatter = new DecimalFormat("#,###"); + + private void doBenchmark(String name, Runnable r) { + // Worm up + for (int i = 0; i < MINIMAL_ITERATION; i++) { + r.run(); + } + + // Start measuring. + final long start = System.nanoTime(); + final long end = start + MEASURE_SECONDS * 1_000_000_000L; + + double iteration = 0; + while (System.nanoTime() <= end) { + for (int i = 0; i < MINIMAL_ITERATION; i++) { + r.run(); + } + iteration += MINIMAL_ITERATION; + } + + final long realEnd = System.nanoTime(); + + System.out.println(String.format("%s\t%s", name, + sFormatter.format((((double) realEnd - start)) / iteration))); + } + + /** + * Micro-benchmark for a method without a non-stub caller check. + */ + @Test + public void benchNoCallerCheck() { + doBenchmark("No caller check", TinyFrameworkCallerCheck::getOne_noCheck); + } + + /** + * Micro-benchmark for a method with a non-stub caller check. + */ + @Test + public void benchWithCallerCheck() { + doBenchmark("With caller check", TinyFrameworkCallerCheck::getOne_withCheck); + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java new file mode 100644 index 000000000000..246d06526f5b --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.test.tinyframework; + +import static com.google.common.truth.Truth.assertThat; + +import com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.SubClass; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class TinyFrameworkClassTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void testSimple() { + TinyFrameworkForTextPolicy tfc = new TinyFrameworkForTextPolicy(); + assertThat(tfc.addOne(1)).isEqualTo(2); + assertThat(tfc.stub).isEqualTo(1); + } + +// @Test +// public void testDoesntCompile() { +// TinyFrameworkClass tfc = new TinyFrameworkClass(); +// +// tfc.addOneInner(1); // Shouldn't compile. +// tfc.toBeRemoved("abc"); // Shouldn't compile. +// tfc.unsupportedMethod(); // Shouldn't compile. +// int a = tfc.keep; // Shouldn't compile +// int b = tfc.remove; // Shouldn't compile +// } + + @Test + public void testSubstitute() { + TinyFrameworkForTextPolicy tfc = new TinyFrameworkForTextPolicy(); + assertThat(tfc.addTwo(1)).isEqualTo(3); + } + + @Test + public void testSubstituteNative() { + TinyFrameworkForTextPolicy tfc = new TinyFrameworkForTextPolicy(); + assertThat(tfc.nativeAddThree(1)).isEqualTo(4); + } + + @Test + public void testVisibleButUsesUnsupportedMethod() { + TinyFrameworkForTextPolicy tfc = new TinyFrameworkForTextPolicy(); + + thrown.expect(RuntimeException.class); + thrown.expectMessage("This method is not supported on the host side"); + tfc.visibleButUsesUnsupportedMethod(); + } + + @Test + public void testNestedClass1() { + assertThat(new TinyFrameworkNestedClasses().mSupplier.get()).isEqualTo(1); + } + + @Test + public void testNestedClass2() { + assertThat(TinyFrameworkNestedClasses.sSupplier.get()).isEqualTo(2); + } + + @Test + public void testNestedClass3() { + assertThat(new TinyFrameworkNestedClasses().getSupplier().get()).isEqualTo(3); + } + + @Test + public void testNestedClass4() { + assertThat(TinyFrameworkNestedClasses.getSupplier_static().get()).isEqualTo(4); + } + + @Test + public void testNestedClass5() { + assertThat((new TinyFrameworkNestedClasses()).new InnerClass().value).isEqualTo(5); + } + + @Test + public void testNestedClass6() { + assertThat(new TinyFrameworkNestedClasses.StaticNestedClass().value).isEqualTo(6); + } + + @Test + public void testNestedClass7() { + assertThat(TinyFrameworkNestedClasses.StaticNestedClass.getSupplier_static().get()) + .isEqualTo(7); + } + + @Test + public void testNativeSubstitutionClass() { + assertThat(TinyFrameworkNative.nativeAddTwo(3)).isEqualTo(5); + } + + @Test + public void testExitLog() { + thrown.expect(RuntimeException.class); + thrown.expectMessage("Outer exception"); + + TinyFrameworkExceptionTester.testException(); + + } + + @Test + public void testMethodCallBeforeSuperCall() { + assertThat(new SubClass(3).value).isEqualTo(3); + } + + @Test + public void testClassLoadHook() { + assertThat(TinyFrameworkClassWithInitializer.sInitialized).isTrue(); + + // Having this line before assertThat() will ensure these class are already loaded. + var classes = new Class[] { + TinyFrameworkClassWithInitializer.class, + TinyFrameworkClassAnnotations.class, + TinyFrameworkForTextPolicy.class, + }; + + // The following classes have a class load hook, so they should be registered. + assertThat(TinyFrameworkClassLoadHook.sLoadedClasses) + .containsAnyIn(classes); + + // This class doesn't have a class load hook, so shouldn't be included. + assertThat(TinyFrameworkClassLoadHook.sLoadedClasses) + .doesNotContain(TinyFrameworkNestedClasses.class); + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithAnnotTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithAnnotTest.java new file mode 100644 index 000000000000..20cc2ec9d50d --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithAnnotTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2023 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. + */ +package com.android.hoststubgen.test.tinyframework; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class TinyFrameworkClassWithAnnotTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void testSimple() { + TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations(); + assertThat(tfc.addOne(1)).isEqualTo(2); + assertThat(tfc.stub).isEqualTo(1); + } + +// @Test +// public void testDoesntCompile() { +// TinyFrameworkClassWithAnnot tfc = new TinyFrameworkClassWithAnnot(); +// +// tfc.addOneInner(1); // Shouldn't compile. +// tfc.toBeRemoved("abc"); // Shouldn't compile. +// tfc.unsupportedMethod(); // Shouldn't compile. +// int a = tfc.keep; // Shouldn't compile +// int b = tfc.remove; // Shouldn't compile +// } + + @Test + public void testSubstitute() { + TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations(); + assertThat(tfc.addTwo(1)).isEqualTo(3); + } + + @Test + public void testSubstituteNative() { + TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations(); + assertThat(tfc.nativeAddThree(1)).isEqualTo(4); + } + + @Test + public void testVisibleButUsesUnsupportedMethod() { + TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations(); + + thrown.expect(RuntimeException.class); + thrown.expectMessage("This method is not supported on the host side"); + tfc.visibleButUsesUnsupportedMethod(); + } +} diff --git a/tools/hoststubgen/scripts/Android.bp b/tools/hoststubgen/scripts/Android.bp new file mode 100644 index 000000000000..5da805e5640e --- /dev/null +++ b/tools/hoststubgen/scripts/Android.bp @@ -0,0 +1,26 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +sh_binary_host { + name: "dump-jar", + src: "dump-jar", + visibility: ["//visibility:public"], +} + +genrule_defaults { + name: "hoststubgen-jar-dump-defaults", + tools: ["dump-jar"], + cmd: "$(location dump-jar) -s -o $(out) $(in)", +} + +sh_binary_host { + name: "run-ravenwood-test", + src: "run-ravenwood-test", + visibility: ["//visibility:public"], +} diff --git a/tools/hoststubgen/scripts/build-framework-hostside-jars-and-extract.sh b/tools/hoststubgen/scripts/build-framework-hostside-jars-and-extract.sh new file mode 100755 index 000000000000..72681234dad8 --- /dev/null +++ b/tools/hoststubgen/scripts/build-framework-hostside-jars-and-extract.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# Copyright (C) 2023 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. + + +# Script to build `framework-host-stub` and `framework-host-impl`, and copy the +# generated jars to $out, and unzip them. Useful for looking into the generated files. + +source "${0%/*}"/../common.sh + +out=framework-all-stub-out + +rm -fr $out +mkdir -p $out + +# Build the jars with `m`. +run m framework-all-hidden-api-host + +# Copy the jar to out/ and extract them. +run cp \ + $SOONG_INT/frameworks/base/tools/hoststubgen/hoststubgen/framework-all-hidden-api-host/linux_glibc_common/gen/* \ + $out + +extract $out/*.jar diff --git a/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh b/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh new file mode 100755 index 000000000000..c3605a9ffaa5 --- /dev/null +++ b/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh @@ -0,0 +1,76 @@ +#!/bin/bash +# Copyright (C) 2023 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. + + +# Script to build hoststubgen and run it directly (without using the build rules) +# on framework-all.jar. + + +echo "THIS SCRIPT IS BROKEN DUE TO CHANGES TO FILE PATHS TO DEPENDENT FILES. FIX IT WHEN YOU NEED TO USE IT." 1>&2 + +exit 99 + + +source "${0%/*}"/../common.sh + +out=out + +mkdir -p $out + +# Build the tool and target jar. +run m hoststubgen framework-all + +base_args=( + @../hoststubgen/hoststubgen-standard-options.txt + + --in-jar $ANDROID_BUILD_TOP/out/soong/.intermediates/frameworks/base/framework-all/android_common/combined/framework-all.jar + --policy-override-file ../hoststubgen/framework-policy-override.txt "${@}" + + # This file will contain all classes as an annotation file, with "keep all" policy. + --gen-keep-all-file $out/framework-all-keep-all-policy.txt + + # This file will contains dump of all classes in the input jar. + --gen-input-dump-file $out/framework-all-dump.txt +) + +do_it() { + local out_file_stem="$1" + shift + local extra_args=("${@}") + + run hoststubgen \ + "${base_args[@]}" \ + "${extra_args[@]}" \ + --out-stub-jar ${out_file_stem}_stub.jar \ + --out-impl-jar ${out_file_stem}_impl.jar \ + $HOSTSTUBGEN_OPTS + + # Extract the jar files, so we can look into them. + run extract ${out_file_stem}_*.jar +} + +#----------------------------------------------------------------------------- +# framework-all, with all hidden APIs. +#----------------------------------------------------------------------------- + +# No extra args. +do_it $out/framework-all_host + +#----------------------------------------------------------------------------- +# framework-test-api, only public/system/test-APIs in the stub. +#----------------------------------------------------------------------------- + +do_it $out/framework-test-api_host \ + --intersect-stub-jar $SOONG_INT/frameworks/base/api/android_test_stubs_current/android_common/combined/*.jar diff --git a/tools/hoststubgen/scripts/dump-jar b/tools/hoststubgen/scripts/dump-jar new file mode 100755 index 000000000000..93729fb22caa --- /dev/null +++ b/tools/hoststubgen/scripts/dump-jar @@ -0,0 +1,163 @@ +#!/bin/bash +# Copyright (C) 2023 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. + +set -e + + +help() { + cat <<'EOF' + + dump-jar: Dump java classes in jar files + + Usage: + dump-jar [-v] CLASS-FILE [...] + + Dump a *.class file + + dump-jar [-v] [-s] [-o OUTPUT-FILENAME] JAR-FILE[: filename regex] [...] + + Dump a jar file. + + If a filename contains a ':', then the following part + will be used to filter files in the jar file. + + For example, "file.jar:/MyClass$" will only dump "MyClass" in file.jar. + + Options: + -v: Enable verbose output. + + -s: Simple output mode, used to check HostStubGen output jars. + + -o: Write the output to a specified file. +EOF +} + +# Parse the options. + +verbose=0 +simple=0 +output="" +while getopts "hvso:" opt; do +case "$opt" in + h) + help + exit 0 + ;; + v) + verbose=1 + ;; + s) + simple=1 + ;; + o) + output="$OPTARG" + ;; + '?') + help + exit 1 + ;; +esac +done +shift $(($OPTIND - 1)) + +JAVAP_OPTS="${JAVAP_OPTS:--v -p -s -sysinfo -constants}" + +if (( $simple )) ; then + JAVAP_OPTS="-p -c -v" +fi + + +# Normalize a java class name. +# Convert '.' to '/' +# Remove the *.class suffix. +normalize() { + local name="$1" + name="${name%.class}" # Remove the .class suffix. + echo "$name" | tr '.' '/' +} + +# Convert the output for `-s` as needed. +filter_output() { + if (( $simple )) ; then + # For "simple output" mode, + # - Normalize the constant numbers (replace with "#x") + # - Remove the constant pool + # - Remove the line number table + # - Some other transient lines + # + # `/PATTERN-1/,/PATTERN-1/{//!d}` is a trick to delete lines between two patterns, without + # the start and the end lines. + sed -e 's/#[0-9][0-9]*/#x/g' \ + -e '/^Constant pool:/,/^[^ ]/{//!d}' \ + -e '/^ *line *[0-9][0-9]*: *[0-9][0-9]*$/d' \ + -e '/SHA-256 checksum/d' \ + -e '/Last modified/d' \ + -e '/^Classfile jar/d' + else + cat # Print as-is. + fi +} + +# Write to the output file (specified with -o) as needed. +write_to_out() { + if [[ -n "$output" ]] ; then + cat >"$output" + echo "Wrote output to $output" 1>&2 + else + cat # print to stdout + fi +} + +for file in "${@}"; do + + # *.class? + if echo "$file" | grep -qE '\.class$' ; then + echo "# Class: $file" 1>&2 + javap $dump_code_opt $JAVAP_OPTS $file + + # *.jar? + elif echo "$file" | grep -qE '\.jar(:.*)?$' ; then + # Take the regex. Remove everything up to : in $file + regex="" + if [[ "$file" =~ : ]] ; then + regex="$(normalize "${file##*:}")" + fi + + # Remove everything after ':', inclusively, in $file. + file="${file%:*}" + + # Print the filename and the regex. + if ! (( $simple )) ; then + echo -n "# Jar: $file" + if [[ "$regex" != "" ]] ;then + echo -n " (regex: $regex)" + fi + echo + fi + + jar tf "$file" | grep '\.class$' | sort | while read -r class ; do + if normalize "$class" | grep -q -- "$regex" ; then + echo "## Class: $class" + javap $dump_code_opt $JAVAP_OPTS -cp $file ${class%.class} + else + (( $verbose )) && echo "## Skipping class: $class" + fi + done + + else + echo "Unknown file type: $file" 1>&2 + exit 1 + fi +done | filter_output | write_to_out diff --git a/tools/hoststubgen/scripts/run-all-tests.sh b/tools/hoststubgen/scripts/run-all-tests.sh new file mode 100755 index 000000000000..6bc0ddb1a8dc --- /dev/null +++ b/tools/hoststubgen/scripts/run-all-tests.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# Copyright (C) 2023 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. + +source "${0%/*}"/../common.sh + +# Move to the top directory of hoststubgen +cd .. + +# These tests are known to pass. +READY_TEST_MODULES=( + HostStubGenTest-framework-all-test-host-test + hoststubgen-test-tiny-test +) + +# First, build all the test modules. This shouldn't fail. +run m run-ravenwood-test ${READY_TEST_MODULES[*]} ${NOT_READY_TEST_MODULES[*]} + +# Next, run the golden check. This should always pass too. +# The following scripts _should_ pass too, but they depend on the internal paths to soong generated +# files, and they may fail when something changes in the build system. +run ./hoststubgen/test-tiny-framework/diff-and-update-golden.sh + +run ./hoststubgen/test-framework/run-test-without-atest.sh +run ./hoststubgen/test-tiny-framework/run-test-manually.sh +run ./scripts/build-framework-hostside-jars-and-extract.sh + +# This script is already broken on goog/master +# run ./scripts/build-framework-hostside-jars-without-genrules.sh + +# These tests should all pass. +run-ravenwood-test ${READY_TEST_MODULES[*]} + +echo ""${0##*/}" finished, with no unexpected failures. Ready to submit!"
\ No newline at end of file diff --git a/tools/hoststubgen/scripts/run-ravenwood-test b/tools/hoststubgen/scripts/run-ravenwood-test new file mode 100755 index 000000000000..9bbb859f5c3d --- /dev/null +++ b/tools/hoststubgen/scripts/run-ravenwood-test @@ -0,0 +1,129 @@ +#!/bin/bash +# Copyright (C) 2023 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. + +set -e + +# Script to run a "Ravenwood" host side test. +# +# A proper way to run these tests is to use `atest`, but `atest` has a known issue of loading +# unrelated jar files as the class path, so for now we use this script to run host side tests. + +# Copy (with some changes) some functions from ../common.sh, so this script can be used without it. + +m() { + if (( $SKIP_BUILD )) ; then + echo "Skipping build: $*" 1>&2 + return 0 + fi + run ${ANDROID_BUILD_TOP}/build/soong/soong_ui.bash --make-mode "$@" +} + +run() { + echo "Running: $*" 1>&2 + "$@" +} + +run_junit_test_jar() { + local jar="$1" + echo "Starting test: $jar ..." + run cd "${jar%/*}" + + run ${JAVA:-java} $JAVA_OPTS \ + -cp $jar \ + org.junit.runner.JUnitCore \ + com.android.hoststubgen.hosthelper.HostTestSuite || return 1 + return 0 +} + +help() { + cat <<'EOF' + + run-ravenwood-test -- Run Ravenwood host tests + + Usage: + run-ravenwood-test [options] MODULE-NAME ... + + Options: + -h: Help + -t: Run test only, without building + -b: Build only, without running + + Example: + run-ravenwood-test HostStubGenTest-framework-test-host-test + +EOF +} + +#------------------------------------------------------------------------- +# Parse options +#------------------------------------------------------------------------- +build=0 +test=0 + +while getopts "htb" opt; do + case "$opt" in + h) help; exit 0 ;; + t) + test=1 + ;; + b) + build=1 + ;; + esac +done +shift $(($OPTIND - 1)) + +# If neither -t nor -b is provided, then build and run./ +if (( ( $build + $test ) == 0 )) ; then + build=1 + test=1 +fi + + +modules=("${@}") + +if (( "${#modules[@]}" == 0 )); then + help + exit 1 +fi + +#------------------------------------------------------------------------- +# Build +#------------------------------------------------------------------------- +if (( $build )) ; then + run m "${modules[@]}" +fi + +#------------------------------------------------------------------------- +# Run +#------------------------------------------------------------------------- + +failures=0 +if (( test )) ; then + for module in "${modules[@]}"; do + if run_junit_test_jar "$ANDROID_BUILD_TOP/out/host/linux-x86/testcases/${module}/${module}.jar"; then + : # passed. + else + failures=$(( failures + 1 )) + fi + done + + if (( $failures > 0 )) ; then + echo "$failures test jar(s) failed." 1>&2 + exit 2 + fi +fi + +exit 0
\ No newline at end of file |